changeset 117 | c0034b35c44e |
parent 84 | d7c5bffdd2d8 |
child 159 | f2e4641681f6 |
114:9fcb0e0991ed | 117:c0034b35c44e |
---|---|
1 /*! |
1 /*! |
2 * jQuery JavaScript Library v2.1.1 |
2 * jQuery JavaScript Library v2.1.3 |
3 * http://jquery.com/ |
3 * http://jquery.com/ |
4 * |
4 * |
5 * Includes Sizzle.js |
5 * Includes Sizzle.js |
6 * http://sizzlejs.com/ |
6 * http://sizzlejs.com/ |
7 * |
7 * |
8 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors |
8 * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors |
9 * Released under the MIT license |
9 * Released under the MIT license |
10 * http://jquery.org/license |
10 * http://jquery.org/license |
11 * |
11 * |
12 * Date: 2014-05-01T17:11Z |
12 * Date: 2014-12-18T15:11Z |
13 */ |
13 */ |
14 |
14 |
15 (function( global, factory ) { |
15 (function( global, factory ) { |
16 |
16 |
17 if ( typeof module === "object" && typeof module.exports === "object" ) { |
17 if ( typeof module === "object" && typeof module.exports === "object" ) { |
18 // For CommonJS and CommonJS-like environments where a proper window is present, |
18 // For CommonJS and CommonJS-like environments where a proper `window` |
19 // execute the factory and get jQuery |
19 // is present, execute the factory and get jQuery. |
20 // For environments that do not inherently posses a window with a document |
20 // For environments that do not have a `window` with a `document` |
21 // (such as Node.js), expose a jQuery-making factory as module.exports |
21 // (such as Node.js), expose a factory as module.exports. |
22 // This accentuates the need for the creation of a real window |
22 // This accentuates the need for the creation of a real `window`. |
23 // e.g. var jQuery = require("jquery")(window); |
23 // e.g. var jQuery = require("jquery")(window); |
24 // See ticket #14549 for more info |
24 // See ticket #14549 for more info. |
25 module.exports = global.document ? |
25 module.exports = global.document ? |
26 factory( global, true ) : |
26 factory( global, true ) : |
27 function( w ) { |
27 function( w ) { |
28 if ( !w.document ) { |
28 if ( !w.document ) { |
29 throw new Error( "jQuery requires a window with a document" ); |
29 throw new Error( "jQuery requires a window with a document" ); |
35 } |
35 } |
36 |
36 |
37 // Pass this if window is not defined yet |
37 // Pass this if window is not defined yet |
38 }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { |
38 }(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { |
39 |
39 |
40 // Can't do this because several apps including ASP.NET trace |
40 // Support: Firefox 18+ |
41 // Can't be in strict mode, several libs including ASP.NET trace |
|
41 // the stack via arguments.caller.callee and Firefox dies if |
42 // the stack via arguments.caller.callee and Firefox dies if |
42 // you try to trace through "use strict" call chains. (#13335) |
43 // you try to trace through "use strict" call chains. (#13335) |
43 // Support: Firefox 18+ |
|
44 // |
44 // |
45 |
45 |
46 var arr = []; |
46 var arr = []; |
47 |
47 |
48 var slice = arr.slice; |
48 var slice = arr.slice; |
65 |
65 |
66 var |
66 var |
67 // Use the correct document accordingly with window argument (sandbox) |
67 // Use the correct document accordingly with window argument (sandbox) |
68 document = window.document, |
68 document = window.document, |
69 |
69 |
70 version = "2.1.1", |
70 version = "2.1.3", |
71 |
71 |
72 // Define a local copy of jQuery |
72 // Define a local copy of jQuery |
73 jQuery = function( selector, context ) { |
73 jQuery = function( selector, context ) { |
74 // The jQuery object is actually just the init constructor 'enhanced' |
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) |
75 // Need init if jQuery is called (just allow error to be thrown if not included) |
183 |
183 |
184 // Handle a deep copy situation |
184 // Handle a deep copy situation |
185 if ( typeof target === "boolean" ) { |
185 if ( typeof target === "boolean" ) { |
186 deep = target; |
186 deep = target; |
187 |
187 |
188 // skip the boolean and the target |
188 // Skip the boolean and the target |
189 target = arguments[ i ] || {}; |
189 target = arguments[ i ] || {}; |
190 i++; |
190 i++; |
191 } |
191 } |
192 |
192 |
193 // Handle case when target is a string or something (possible in deep copy) |
193 // Handle case when target is a string or something (possible in deep copy) |
194 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { |
194 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { |
195 target = {}; |
195 target = {}; |
196 } |
196 } |
197 |
197 |
198 // extend jQuery itself if only one argument is passed |
198 // Extend jQuery itself if only one argument is passed |
199 if ( i === length ) { |
199 if ( i === length ) { |
200 target = this; |
200 target = this; |
201 i--; |
201 i--; |
202 } |
202 } |
203 |
203 |
250 throw new Error( msg ); |
250 throw new Error( msg ); |
251 }, |
251 }, |
252 |
252 |
253 noop: function() {}, |
253 noop: function() {}, |
254 |
254 |
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 ) { |
255 isFunction: function( obj ) { |
259 return jQuery.type(obj) === "function"; |
256 return jQuery.type(obj) === "function"; |
260 }, |
257 }, |
261 |
258 |
262 isArray: Array.isArray, |
259 isArray: Array.isArray, |
267 |
264 |
268 isNumeric: function( obj ) { |
265 isNumeric: function( obj ) { |
269 // parseFloat NaNs numeric-cast false positives (null|true|false|"") |
266 // parseFloat NaNs numeric-cast false positives (null|true|false|"") |
270 // ...but misinterprets leading-number strings, particularly hex literals ("0x...") |
267 // ...but misinterprets leading-number strings, particularly hex literals ("0x...") |
271 // subtraction forces infinities to NaN |
268 // subtraction forces infinities to NaN |
272 return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; |
269 // adding 1 corrects loss of precision from parseFloat (#15100) |
270 return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; |
|
273 }, |
271 }, |
274 |
272 |
275 isPlainObject: function( obj ) { |
273 isPlainObject: function( obj ) { |
276 // Not plain objects: |
274 // Not plain objects: |
277 // - Any object or value whose internal [[Class]] property is not "[object Object]" |
275 // - Any object or value whose internal [[Class]] property is not "[object Object]" |
301 |
299 |
302 type: function( obj ) { |
300 type: function( obj ) { |
303 if ( obj == null ) { |
301 if ( obj == null ) { |
304 return obj + ""; |
302 return obj + ""; |
305 } |
303 } |
306 // Support: Android < 4.0, iOS < 6 (functionish RegExp) |
304 // Support: Android<4.0, iOS<6 (functionish RegExp) |
307 return typeof obj === "object" || typeof obj === "function" ? |
305 return typeof obj === "object" || typeof obj === "function" ? |
308 class2type[ toString.call(obj) ] || "object" : |
306 class2type[ toString.call(obj) ] || "object" : |
309 typeof obj; |
307 typeof obj; |
310 }, |
308 }, |
311 |
309 |
331 } |
329 } |
332 } |
330 } |
333 }, |
331 }, |
334 |
332 |
335 // Convert dashed to camelCase; used by the css and data modules |
333 // Convert dashed to camelCase; used by the css and data modules |
334 // Support: IE9-11+ |
|
336 // Microsoft forgot to hump their vendor prefix (#9572) |
335 // Microsoft forgot to hump their vendor prefix (#9572) |
337 camelCase: function( string ) { |
336 camelCase: function( string ) { |
338 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); |
337 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); |
339 }, |
338 }, |
340 |
339 |
546 return type === "array" || length === 0 || |
545 return type === "array" || length === 0 || |
547 typeof length === "number" && length > 0 && ( length - 1 ) in obj; |
546 typeof length === "number" && length > 0 && ( length - 1 ) in obj; |
548 } |
547 } |
549 var Sizzle = |
548 var Sizzle = |
550 /*! |
549 /*! |
551 * Sizzle CSS Selector Engine v1.10.19 |
550 * Sizzle CSS Selector Engine v2.2.0-pre |
552 * http://sizzlejs.com/ |
551 * http://sizzlejs.com/ |
553 * |
552 * |
554 * Copyright 2013 jQuery Foundation, Inc. and other contributors |
553 * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors |
555 * Released under the MIT license |
554 * Released under the MIT license |
556 * http://jquery.org/license |
555 * http://jquery.org/license |
557 * |
556 * |
558 * Date: 2014-04-18 |
557 * Date: 2014-12-16 |
559 */ |
558 */ |
560 (function( window ) { |
559 (function( window ) { |
561 |
560 |
562 var i, |
561 var i, |
563 support, |
562 support, |
580 rbuggyMatches, |
579 rbuggyMatches, |
581 matches, |
580 matches, |
582 contains, |
581 contains, |
583 |
582 |
584 // Instance-specific data |
583 // Instance-specific data |
585 expando = "sizzle" + -(new Date()), |
584 expando = "sizzle" + 1 * new Date(), |
586 preferredDoc = window.document, |
585 preferredDoc = window.document, |
587 dirruns = 0, |
586 dirruns = 0, |
588 done = 0, |
587 done = 0, |
589 classCache = createCache(), |
588 classCache = createCache(), |
590 tokenCache = createCache(), |
589 tokenCache = createCache(), |
595 } |
594 } |
596 return 0; |
595 return 0; |
597 }, |
596 }, |
598 |
597 |
599 // General-purpose constants |
598 // General-purpose constants |
600 strundefined = typeof undefined, |
|
601 MAX_NEGATIVE = 1 << 31, |
599 MAX_NEGATIVE = 1 << 31, |
602 |
600 |
603 // Instance methods |
601 // Instance methods |
604 hasOwn = ({}).hasOwnProperty, |
602 hasOwn = ({}).hasOwnProperty, |
605 arr = [], |
603 arr = [], |
606 pop = arr.pop, |
604 pop = arr.pop, |
607 push_native = arr.push, |
605 push_native = arr.push, |
608 push = arr.push, |
606 push = arr.push, |
609 slice = arr.slice, |
607 slice = arr.slice, |
610 // Use a stripped-down indexOf if we can't use a native one |
608 // Use a stripped-down indexOf as it's faster than native |
611 indexOf = arr.indexOf || function( elem ) { |
609 // http://jsperf.com/thor-indexof-vs-for/5 |
610 indexOf = function( list, elem ) { |
|
612 var i = 0, |
611 var i = 0, |
613 len = this.length; |
612 len = list.length; |
614 for ( ; i < len; i++ ) { |
613 for ( ; i < len; i++ ) { |
615 if ( this[i] === elem ) { |
614 if ( list[i] === elem ) { |
616 return i; |
615 return i; |
617 } |
616 } |
618 } |
617 } |
619 return -1; |
618 return -1; |
620 }, |
619 }, |
650 // 3. anything else (capture 2) |
649 // 3. anything else (capture 2) |
651 ".*" + |
650 ".*" + |
652 ")\\)|)", |
651 ")\\)|)", |
653 |
652 |
654 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter |
653 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter |
654 rwhitespace = new RegExp( whitespace + "+", "g" ), |
|
655 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), |
655 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), |
656 |
656 |
657 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), |
657 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), |
658 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), |
658 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), |
659 |
659 |
701 high < 0 ? |
701 high < 0 ? |
702 // BMP codepoint |
702 // BMP codepoint |
703 String.fromCharCode( high + 0x10000 ) : |
703 String.fromCharCode( high + 0x10000 ) : |
704 // Supplemental Plane codepoint (surrogate pair) |
704 // Supplemental Plane codepoint (surrogate pair) |
705 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); |
705 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); |
706 }, |
|
707 |
|
708 // Used for iframes |
|
709 // See setDocument() |
|
710 // Removing the function wrapper causes a "Permission Denied" |
|
711 // error in IE |
|
712 unloadHandler = function() { |
|
713 setDocument(); |
|
706 }; |
714 }; |
707 |
715 |
708 // Optimize for push.apply( _, NodeList ) |
716 // Optimize for push.apply( _, NodeList ) |
709 try { |
717 try { |
710 push.apply( |
718 push.apply( |
743 setDocument( context ); |
751 setDocument( context ); |
744 } |
752 } |
745 |
753 |
746 context = context || document; |
754 context = context || document; |
747 results = results || []; |
755 results = results || []; |
748 |
756 nodeType = context.nodeType; |
749 if ( !selector || typeof selector !== "string" ) { |
757 |
758 if ( typeof selector !== "string" || !selector || |
|
759 nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { |
|
760 |
|
750 return results; |
761 return results; |
751 } |
762 } |
752 |
763 |
753 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { |
764 if ( !seed && documentIsHTML ) { |
754 return []; |
765 |
755 } |
766 // Try to shortcut find operations when possible (e.g., not under DocumentFragment) |
756 |
767 if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { |
757 if ( documentIsHTML && !seed ) { |
|
758 |
|
759 // Shortcuts |
|
760 if ( (match = rquickExpr.exec( selector )) ) { |
|
761 // Speed-up: Sizzle("#ID") |
768 // Speed-up: Sizzle("#ID") |
762 if ( (m = match[1]) ) { |
769 if ( (m = match[1]) ) { |
763 if ( nodeType === 9 ) { |
770 if ( nodeType === 9 ) { |
764 elem = context.getElementById( m ); |
771 elem = context.getElementById( m ); |
765 // Check parentNode to catch when Blackberry 4.6 returns |
772 // Check parentNode to catch when Blackberry 4.6 returns |
787 } else if ( match[2] ) { |
794 } else if ( match[2] ) { |
788 push.apply( results, context.getElementsByTagName( selector ) ); |
795 push.apply( results, context.getElementsByTagName( selector ) ); |
789 return results; |
796 return results; |
790 |
797 |
791 // Speed-up: Sizzle(".CLASS") |
798 // Speed-up: Sizzle(".CLASS") |
792 } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { |
799 } else if ( (m = match[3]) && support.getElementsByClassName ) { |
793 push.apply( results, context.getElementsByClassName( m ) ); |
800 push.apply( results, context.getElementsByClassName( m ) ); |
794 return results; |
801 return results; |
795 } |
802 } |
796 } |
803 } |
797 |
804 |
798 // QSA path |
805 // QSA path |
799 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { |
806 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { |
800 nid = old = expando; |
807 nid = old = expando; |
801 newContext = context; |
808 newContext = context; |
802 newSelector = nodeType === 9 && selector; |
809 newSelector = nodeType !== 1 && selector; |
803 |
810 |
804 // qSA works strangely on Element-rooted queries |
811 // qSA works strangely on Element-rooted queries |
805 // We can work around this by specifying an extra ID on the root |
812 // 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) |
813 // and working up from there (Thanks to Andrew Dupont for the technique) |
807 // IE 8 doesn't work on object elements |
814 // IE 8 doesn't work on object elements |
984 * Checks a node for validity as a Sizzle context |
991 * Checks a node for validity as a Sizzle context |
985 * @param {Element|Object=} context |
992 * @param {Element|Object=} context |
986 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value |
993 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value |
987 */ |
994 */ |
988 function testContext( context ) { |
995 function testContext( context ) { |
989 return context && typeof context.getElementsByTagName !== strundefined && context; |
996 return context && typeof context.getElementsByTagName !== "undefined" && context; |
990 } |
997 } |
991 |
998 |
992 // Expose support vars for convenience |
999 // Expose support vars for convenience |
993 support = Sizzle.support = {}; |
1000 support = Sizzle.support = {}; |
994 |
1001 |
1008 * Sets document-related variables once based on the current document |
1015 * 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 |
1016 * @param {Element|Object} [doc] An element or document object to use to set the document |
1010 * @returns {Object} Returns the current document |
1017 * @returns {Object} Returns the current document |
1011 */ |
1018 */ |
1012 setDocument = Sizzle.setDocument = function( node ) { |
1019 setDocument = Sizzle.setDocument = function( node ) { |
1013 var hasCompare, |
1020 var hasCompare, parent, |
1014 doc = node ? node.ownerDocument || node : preferredDoc, |
1021 doc = node ? node.ownerDocument || node : preferredDoc; |
1015 parent = doc.defaultView; |
|
1016 |
1022 |
1017 // If no document and documentElement is available, return |
1023 // If no document and documentElement is available, return |
1018 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { |
1024 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { |
1019 return document; |
1025 return document; |
1020 } |
1026 } |
1021 |
1027 |
1022 // Set our document |
1028 // Set our document |
1023 document = doc; |
1029 document = doc; |
1024 docElem = doc.documentElement; |
1030 docElem = doc.documentElement; |
1025 |
1031 parent = doc.defaultView; |
1026 // Support tests |
|
1027 documentIsHTML = !isXML( doc ); |
|
1028 |
1032 |
1029 // Support: IE>8 |
1033 // Support: IE>8 |
1030 // If iframe document is assigned to "document" variable and if iframe has been reloaded, |
1034 // 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 |
1035 // 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 |
1036 // IE6-8 do not support the defaultView property so parent will be undefined |
1033 if ( parent && parent !== parent.top ) { |
1037 if ( parent && parent !== parent.top ) { |
1034 // IE11 does not have attachEvent, so all must suffer |
1038 // IE11 does not have attachEvent, so all must suffer |
1035 if ( parent.addEventListener ) { |
1039 if ( parent.addEventListener ) { |
1036 parent.addEventListener( "unload", function() { |
1040 parent.addEventListener( "unload", unloadHandler, false ); |
1037 setDocument(); |
|
1038 }, false ); |
|
1039 } else if ( parent.attachEvent ) { |
1041 } else if ( parent.attachEvent ) { |
1040 parent.attachEvent( "onunload", function() { |
1042 parent.attachEvent( "onunload", unloadHandler ); |
1041 setDocument(); |
|
1042 }); |
|
1043 } |
1043 } |
1044 } |
1044 } |
1045 |
1045 |
1046 /* Support tests |
|
1047 ---------------------------------------------------------------------- */ |
|
1048 documentIsHTML = !isXML( doc ); |
|
1049 |
|
1046 /* Attributes |
1050 /* Attributes |
1047 ---------------------------------------------------------------------- */ |
1051 ---------------------------------------------------------------------- */ |
1048 |
1052 |
1049 // Support: IE<8 |
1053 // Support: IE<8 |
1050 // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) |
1054 // Verify that getAttribute really returns attributes and not properties |
1055 // (excepting IE8 booleans) |
|
1051 support.attributes = assert(function( div ) { |
1056 support.attributes = assert(function( div ) { |
1052 div.className = "i"; |
1057 div.className = "i"; |
1053 return !div.getAttribute("className"); |
1058 return !div.getAttribute("className"); |
1054 }); |
1059 }); |
1055 |
1060 |
1060 support.getElementsByTagName = assert(function( div ) { |
1065 support.getElementsByTagName = assert(function( div ) { |
1061 div.appendChild( doc.createComment("") ); |
1066 div.appendChild( doc.createComment("") ); |
1062 return !div.getElementsByTagName("*").length; |
1067 return !div.getElementsByTagName("*").length; |
1063 }); |
1068 }); |
1064 |
1069 |
1065 // Check if getElementsByClassName can be trusted |
1070 // Support: IE<9 |
1066 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { |
1071 support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); |
1067 div.innerHTML = "<div class='a'></div><div class='a i'></div>"; |
|
1068 |
|
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; |
|
1075 }); |
|
1076 |
1072 |
1077 // Support: IE<10 |
1073 // Support: IE<10 |
1078 // Check if getElementById returns elements by name |
1074 // Check if getElementById returns elements by name |
1079 // The broken getElementById methods don't pick up programatically-set names, |
1075 // The broken getElementById methods don't pick up programatically-set names, |
1080 // so use a roundabout getElementsByName test |
1076 // so use a roundabout getElementsByName test |
1084 }); |
1080 }); |
1085 |
1081 |
1086 // ID find and filter |
1082 // ID find and filter |
1087 if ( support.getById ) { |
1083 if ( support.getById ) { |
1088 Expr.find["ID"] = function( id, context ) { |
1084 Expr.find["ID"] = function( id, context ) { |
1089 if ( typeof context.getElementById !== strundefined && documentIsHTML ) { |
1085 if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { |
1090 var m = context.getElementById( id ); |
1086 var m = context.getElementById( id ); |
1091 // Check parentNode to catch when Blackberry 4.6 returns |
1087 // Check parentNode to catch when Blackberry 4.6 returns |
1092 // nodes that are no longer in the document #6963 |
1088 // nodes that are no longer in the document #6963 |
1093 return m && m.parentNode ? [ m ] : []; |
1089 return m && m.parentNode ? [ m ] : []; |
1094 } |
1090 } |
1105 delete Expr.find["ID"]; |
1101 delete Expr.find["ID"]; |
1106 |
1102 |
1107 Expr.filter["ID"] = function( id ) { |
1103 Expr.filter["ID"] = function( id ) { |
1108 var attrId = id.replace( runescape, funescape ); |
1104 var attrId = id.replace( runescape, funescape ); |
1109 return function( elem ) { |
1105 return function( elem ) { |
1110 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); |
1106 var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); |
1111 return node && node.value === attrId; |
1107 return node && node.value === attrId; |
1112 }; |
1108 }; |
1113 }; |
1109 }; |
1114 } |
1110 } |
1115 |
1111 |
1116 // Tag |
1112 // Tag |
1117 Expr.find["TAG"] = support.getElementsByTagName ? |
1113 Expr.find["TAG"] = support.getElementsByTagName ? |
1118 function( tag, context ) { |
1114 function( tag, context ) { |
1119 if ( typeof context.getElementsByTagName !== strundefined ) { |
1115 if ( typeof context.getElementsByTagName !== "undefined" ) { |
1120 return context.getElementsByTagName( tag ); |
1116 return context.getElementsByTagName( tag ); |
1117 |
|
1118 // DocumentFragment nodes don't have gEBTN |
|
1119 } else if ( support.qsa ) { |
|
1120 return context.querySelectorAll( tag ); |
|
1121 } |
1121 } |
1122 } : |
1122 } : |
1123 |
|
1123 function( tag, context ) { |
1124 function( tag, context ) { |
1124 var elem, |
1125 var elem, |
1125 tmp = [], |
1126 tmp = [], |
1126 i = 0, |
1127 i = 0, |
1128 // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too |
|
1127 results = context.getElementsByTagName( tag ); |
1129 results = context.getElementsByTagName( tag ); |
1128 |
1130 |
1129 // Filter out possible comments |
1131 // Filter out possible comments |
1130 if ( tag === "*" ) { |
1132 if ( tag === "*" ) { |
1131 while ( (elem = results[i++]) ) { |
1133 while ( (elem = results[i++]) ) { |
1139 return results; |
1141 return results; |
1140 }; |
1142 }; |
1141 |
1143 |
1142 // Class |
1144 // Class |
1143 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { |
1145 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { |
1144 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { |
1146 if ( documentIsHTML ) { |
1145 return context.getElementsByClassName( className ); |
1147 return context.getElementsByClassName( className ); |
1146 } |
1148 } |
1147 }; |
1149 }; |
1148 |
1150 |
1149 /* QSA/matchesSelector |
1151 /* QSA/matchesSelector |
1168 // Select is set to empty string on purpose |
1170 // Select is set to empty string on purpose |
1169 // This is to test IE's treatment of not explicitly |
1171 // This is to test IE's treatment of not explicitly |
1170 // setting a boolean content attribute, |
1172 // setting a boolean content attribute, |
1171 // since its presence should be enough |
1173 // since its presence should be enough |
1172 // http://bugs.jquery.com/ticket/12359 |
1174 // http://bugs.jquery.com/ticket/12359 |
1173 div.innerHTML = "<select msallowclip=''><option selected=''></option></select>"; |
1175 docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" + |
1176 "<select id='" + expando + "-\f]' msallowcapture=''>" + |
|
1177 "<option selected=''></option></select>"; |
|
1174 |
1178 |
1175 // Support: IE8, Opera 11-12.16 |
1179 // Support: IE8, Opera 11-12.16 |
1176 // Nothing should be selected when empty strings follow ^= or $= or *= |
1180 // Nothing should be selected when empty strings follow ^= or $= or *= |
1177 // The test attribute must be unknown in Opera but "safe" for WinRT |
1181 // 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 |
1182 // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section |
1179 if ( div.querySelectorAll("[msallowclip^='']").length ) { |
1183 if ( div.querySelectorAll("[msallowcapture^='']").length ) { |
1180 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); |
1184 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); |
1181 } |
1185 } |
1182 |
1186 |
1183 // Support: IE8 |
1187 // Support: IE8 |
1184 // Boolean attributes and "value" are not treated correctly |
1188 // Boolean attributes and "value" are not treated correctly |
1185 if ( !div.querySelectorAll("[selected]").length ) { |
1189 if ( !div.querySelectorAll("[selected]").length ) { |
1186 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); |
1190 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); |
1187 } |
1191 } |
1188 |
1192 |
1193 // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ |
|
1194 if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { |
|
1195 rbuggyQSA.push("~="); |
|
1196 } |
|
1197 |
|
1189 // Webkit/Opera - :checked should return selected option elements |
1198 // Webkit/Opera - :checked should return selected option elements |
1190 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked |
1199 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked |
1191 // IE8 throws error here and will not see later tests |
1200 // IE8 throws error here and will not see later tests |
1192 if ( !div.querySelectorAll(":checked").length ) { |
1201 if ( !div.querySelectorAll(":checked").length ) { |
1193 rbuggyQSA.push(":checked"); |
1202 rbuggyQSA.push(":checked"); |
1203 } |
|
1204 |
|
1205 // Support: Safari 8+, iOS 8+ |
|
1206 // https://bugs.webkit.org/show_bug.cgi?id=136851 |
|
1207 // In-page `selector#id sibing-combinator selector` fails |
|
1208 if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { |
|
1209 rbuggyQSA.push(".#.+[+~]"); |
|
1194 } |
1210 } |
1195 }); |
1211 }); |
1196 |
1212 |
1197 assert(function( div ) { |
1213 assert(function( div ) { |
1198 // Support: Windows 8 Native Apps |
1214 // Support: Windows 8 Native Apps |
1306 return 1; |
1322 return 1; |
1307 } |
1323 } |
1308 |
1324 |
1309 // Maintain original order |
1325 // Maintain original order |
1310 return sortInput ? |
1326 return sortInput ? |
1311 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : |
1327 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : |
1312 0; |
1328 0; |
1313 } |
1329 } |
1314 |
1330 |
1315 return compare & 4 ? -1 : 1; |
1331 return compare & 4 ? -1 : 1; |
1316 } : |
1332 } : |
1333 return a === doc ? -1 : |
1349 return a === doc ? -1 : |
1334 b === doc ? 1 : |
1350 b === doc ? 1 : |
1335 aup ? -1 : |
1351 aup ? -1 : |
1336 bup ? 1 : |
1352 bup ? 1 : |
1337 sortInput ? |
1353 sortInput ? |
1338 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : |
1354 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : |
1339 0; |
1355 0; |
1340 |
1356 |
1341 // If the nodes are siblings, we can do a quick check |
1357 // If the nodes are siblings, we can do a quick check |
1342 } else if ( aup === bup ) { |
1358 } else if ( aup === bup ) { |
1343 return siblingCheck( a, b ); |
1359 return siblingCheck( a, b ); |
1396 // As well, disconnected nodes are said to be in a document |
1412 // As well, disconnected nodes are said to be in a document |
1397 // fragment in IE 9 |
1413 // fragment in IE 9 |
1398 elem.document && elem.document.nodeType !== 11 ) { |
1414 elem.document && elem.document.nodeType !== 11 ) { |
1399 return ret; |
1415 return ret; |
1400 } |
1416 } |
1401 } catch(e) {} |
1417 } catch (e) {} |
1402 } |
1418 } |
1403 |
1419 |
1404 return Sizzle( expr, document, null, [ elem ] ).length > 0; |
1420 return Sizzle( expr, document, null, [ elem ] ).length > 0; |
1405 }; |
1421 }; |
1406 |
1422 |
1615 var pattern = classCache[ className + " " ]; |
1631 var pattern = classCache[ className + " " ]; |
1616 |
1632 |
1617 return pattern || |
1633 return pattern || |
1618 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && |
1634 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && |
1619 classCache( className, function( elem ) { |
1635 classCache( className, function( elem ) { |
1620 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); |
1636 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); |
1621 }); |
1637 }); |
1622 }, |
1638 }, |
1623 |
1639 |
1624 "ATTR": function( name, operator, check ) { |
1640 "ATTR": function( name, operator, check ) { |
1625 return function( elem ) { |
1641 return function( elem ) { |
1637 return operator === "=" ? result === check : |
1653 return operator === "=" ? result === check : |
1638 operator === "!=" ? result !== check : |
1654 operator === "!=" ? result !== check : |
1639 operator === "^=" ? check && result.indexOf( check ) === 0 : |
1655 operator === "^=" ? check && result.indexOf( check ) === 0 : |
1640 operator === "*=" ? check && result.indexOf( check ) > -1 : |
1656 operator === "*=" ? check && result.indexOf( check ) > -1 : |
1641 operator === "$=" ? check && result.slice( -check.length ) === check : |
1657 operator === "$=" ? check && result.slice( -check.length ) === check : |
1642 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : |
1658 operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : |
1643 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : |
1659 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : |
1644 false; |
1660 false; |
1645 }; |
1661 }; |
1646 }, |
1662 }, |
1647 |
1663 |
1757 markFunction(function( seed, matches ) { |
1773 markFunction(function( seed, matches ) { |
1758 var idx, |
1774 var idx, |
1759 matched = fn( seed, argument ), |
1775 matched = fn( seed, argument ), |
1760 i = matched.length; |
1776 i = matched.length; |
1761 while ( i-- ) { |
1777 while ( i-- ) { |
1762 idx = indexOf.call( seed, matched[i] ); |
1778 idx = indexOf( seed, matched[i] ); |
1763 seed[ idx ] = !( matches[ idx ] = matched[i] ); |
1779 seed[ idx ] = !( matches[ idx ] = matched[i] ); |
1764 } |
1780 } |
1765 }) : |
1781 }) : |
1766 function( elem ) { |
1782 function( elem ) { |
1767 return fn( elem, 0, args ); |
1783 return fn( elem, 0, args ); |
1796 } |
1812 } |
1797 }) : |
1813 }) : |
1798 function( elem, context, xml ) { |
1814 function( elem, context, xml ) { |
1799 input[0] = elem; |
1815 input[0] = elem; |
1800 matcher( input, null, xml, results ); |
1816 matcher( input, null, xml, results ); |
1817 // Don't keep the element (issue #299) |
|
1818 input[0] = null; |
|
1801 return !results.pop(); |
1819 return !results.pop(); |
1802 }; |
1820 }; |
1803 }), |
1821 }), |
1804 |
1822 |
1805 "has": markFunction(function( selector ) { |
1823 "has": markFunction(function( selector ) { |
1807 return Sizzle( selector, elem ).length > 0; |
1825 return Sizzle( selector, elem ).length > 0; |
1808 }; |
1826 }; |
1809 }), |
1827 }), |
1810 |
1828 |
1811 "contains": markFunction(function( text ) { |
1829 "contains": markFunction(function( text ) { |
1830 text = text.replace( runescape, funescape ); |
|
1812 return function( elem ) { |
1831 return function( elem ) { |
1813 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; |
1832 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; |
1814 }; |
1833 }; |
1815 }), |
1834 }), |
1816 |
1835 |
2228 |
2247 |
2229 // Move matched elements from seed to results to keep them synchronized |
2248 // Move matched elements from seed to results to keep them synchronized |
2230 i = matcherOut.length; |
2249 i = matcherOut.length; |
2231 while ( i-- ) { |
2250 while ( i-- ) { |
2232 if ( (elem = matcherOut[i]) && |
2251 if ( (elem = matcherOut[i]) && |
2233 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { |
2252 (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { |
2234 |
2253 |
2235 seed[temp] = !(results[temp] = elem); |
2254 seed[temp] = !(results[temp] = elem); |
2236 } |
2255 } |
2237 } |
2256 } |
2238 } |
2257 } |
2263 // The foundational matcher ensures that elements are reachable from top-level context(s) |
2282 // The foundational matcher ensures that elements are reachable from top-level context(s) |
2264 matchContext = addCombinator( function( elem ) { |
2283 matchContext = addCombinator( function( elem ) { |
2265 return elem === checkContext; |
2284 return elem === checkContext; |
2266 }, implicitRelative, true ), |
2285 }, implicitRelative, true ), |
2267 matchAnyContext = addCombinator( function( elem ) { |
2286 matchAnyContext = addCombinator( function( elem ) { |
2268 return indexOf.call( checkContext, elem ) > -1; |
2287 return indexOf( checkContext, elem ) > -1; |
2269 }, implicitRelative, true ), |
2288 }, implicitRelative, true ), |
2270 matchers = [ function( elem, context, xml ) { |
2289 matchers = [ function( elem, context, xml ) { |
2271 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( |
2290 var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( |
2272 (checkContext = context).nodeType ? |
2291 (checkContext = context).nodeType ? |
2273 matchContext( elem, context, xml ) : |
2292 matchContext( elem, context, xml ) : |
2274 matchAnyContext( elem, context, xml ) ); |
2293 matchAnyContext( elem, context, xml ) ); |
2294 // Avoid hanging onto element (issue #299) |
|
2295 checkContext = null; |
|
2296 return ret; |
|
2275 } ]; |
2297 } ]; |
2276 |
2298 |
2277 for ( ; i < len; i++ ) { |
2299 for ( ; i < len; i++ ) { |
2278 if ( (matcher = Expr.relative[ tokens[i].type ]) ) { |
2300 if ( (matcher = Expr.relative[ tokens[i].type ]) ) { |
2279 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; |
2301 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; |
2519 // One-time assignments |
2541 // One-time assignments |
2520 |
2542 |
2521 // Sort stability |
2543 // Sort stability |
2522 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; |
2544 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; |
2523 |
2545 |
2524 // Support: Chrome<14 |
2546 // Support: Chrome 14-35+ |
2525 // Always assume duplicates if they aren't passed to the comparison function |
2547 // Always assume duplicates if they aren't passed to the comparison function |
2526 support.detectDuplicates = !!hasDuplicate; |
2548 support.detectDuplicates = !!hasDuplicate; |
2527 |
2549 |
2528 // Initialize against the default document |
2550 // Initialize against the default document |
2529 setDocument(); |
2551 setDocument(); |
2728 |
2750 |
2729 // HANDLE: $(html) -> $(array) |
2751 // HANDLE: $(html) -> $(array) |
2730 if ( match[1] ) { |
2752 if ( match[1] ) { |
2731 context = context instanceof jQuery ? context[0] : context; |
2753 context = context instanceof jQuery ? context[0] : context; |
2732 |
2754 |
2733 // scripts is true for back-compat |
2755 // Option to run scripts is true for back-compat |
2734 // Intentionally let the error be thrown if parseHTML is not present |
2756 // Intentionally let the error be thrown if parseHTML is not present |
2735 jQuery.merge( this, jQuery.parseHTML( |
2757 jQuery.merge( this, jQuery.parseHTML( |
2736 match[1], |
2758 match[1], |
2737 context && context.nodeType ? context.ownerDocument || context : document, |
2759 context && context.nodeType ? context.ownerDocument || context : document, |
2738 true |
2760 true |
2756 |
2778 |
2757 // HANDLE: $(#id) |
2779 // HANDLE: $(#id) |
2758 } else { |
2780 } else { |
2759 elem = document.getElementById( match[2] ); |
2781 elem = document.getElementById( match[2] ); |
2760 |
2782 |
2761 // Check parentNode to catch when Blackberry 4.6 returns |
2783 // Support: Blackberry 4.6 |
2762 // nodes that are no longer in the document #6963 |
2784 // gEBID returns nodes no longer in the document (#6963) |
2763 if ( elem && elem.parentNode ) { |
2785 if ( elem && elem.parentNode ) { |
2764 // Inject the element directly into the jQuery object |
2786 // Inject the element directly into the jQuery object |
2765 this.length = 1; |
2787 this.length = 1; |
2766 this[0] = elem; |
2788 this[0] = elem; |
2767 } |
2789 } |
2810 // Initialize central reference |
2832 // Initialize central reference |
2811 rootjQuery = jQuery( document ); |
2833 rootjQuery = jQuery( document ); |
2812 |
2834 |
2813 |
2835 |
2814 var rparentsprev = /^(?:parents|prev(?:Until|All))/, |
2836 var rparentsprev = /^(?:parents|prev(?:Until|All))/, |
2815 // methods guaranteed to produce a unique set when starting from a unique set |
2837 // Methods guaranteed to produce a unique set when starting from a unique set |
2816 guaranteedUnique = { |
2838 guaranteedUnique = { |
2817 children: true, |
2839 children: true, |
2818 contents: true, |
2840 contents: true, |
2819 next: true, |
2841 next: true, |
2820 prev: true |
2842 prev: true |
2890 } |
2912 } |
2891 |
2913 |
2892 return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); |
2914 return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); |
2893 }, |
2915 }, |
2894 |
2916 |
2895 // Determine the position of an element within |
2917 // Determine the position of an element within the set |
2896 // the matched set of elements |
|
2897 index: function( elem ) { |
2918 index: function( elem ) { |
2898 |
2919 |
2899 // No argument, return index in parent |
2920 // No argument, return index in parent |
2900 if ( !elem ) { |
2921 if ( !elem ) { |
2901 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; |
2922 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; |
2902 } |
2923 } |
2903 |
2924 |
2904 // index in selector |
2925 // Index in selector |
2905 if ( typeof elem === "string" ) { |
2926 if ( typeof elem === "string" ) { |
2906 return indexOf.call( jQuery( elem ), this[ 0 ] ); |
2927 return indexOf.call( jQuery( elem ), this[ 0 ] ); |
2907 } |
2928 } |
2908 |
2929 |
2909 // Locate the position of the desired element |
2930 // Locate the position of the desired element |
3315 }; |
3336 }; |
3316 }, |
3337 }, |
3317 |
3338 |
3318 progressValues, progressContexts, resolveContexts; |
3339 progressValues, progressContexts, resolveContexts; |
3319 |
3340 |
3320 // add listeners to Deferred subordinates; treat others as resolved |
3341 // Add listeners to Deferred subordinates; treat others as resolved |
3321 if ( length > 1 ) { |
3342 if ( length > 1 ) { |
3322 progressValues = new Array( length ); |
3343 progressValues = new Array( length ); |
3323 progressContexts = new Array( length ); |
3344 progressContexts = new Array( length ); |
3324 resolveContexts = new Array( length ); |
3345 resolveContexts = new Array( length ); |
3325 for ( ; i < length; i++ ) { |
3346 for ( ; i < length; i++ ) { |
3332 --remaining; |
3353 --remaining; |
3333 } |
3354 } |
3334 } |
3355 } |
3335 } |
3356 } |
3336 |
3357 |
3337 // if we're not waiting on anything, resolve the master |
3358 // If we're not waiting on anything, resolve the master |
3338 if ( !remaining ) { |
3359 if ( !remaining ) { |
3339 deferred.resolveWith( resolveContexts, resolveValues ); |
3360 deferred.resolveWith( resolveContexts, resolveValues ); |
3340 } |
3361 } |
3341 |
3362 |
3342 return deferred.promise(); |
3363 return deferred.promise(); |
3411 if ( !readyList ) { |
3432 if ( !readyList ) { |
3412 |
3433 |
3413 readyList = jQuery.Deferred(); |
3434 readyList = jQuery.Deferred(); |
3414 |
3435 |
3415 // Catch cases where $(document).ready() is called after the browser event has already occurred. |
3436 // 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 |
3437 // 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 |
3438 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 |
3418 if ( document.readyState === "complete" ) { |
3439 if ( document.readyState === "complete" ) { |
3419 // Handle it asynchronously to allow scripts the opportunity to delay ready |
3440 // Handle it asynchronously to allow scripts the opportunity to delay ready |
3420 setTimeout( jQuery.ready ); |
3441 setTimeout( jQuery.ready ); |
3421 |
3442 |
3505 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); |
3526 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); |
3506 }; |
3527 }; |
3507 |
3528 |
3508 |
3529 |
3509 function Data() { |
3530 function Data() { |
3510 // Support: Android < 4, |
3531 // Support: Android<4, |
3511 // Old WebKit does not have Object.preventExtensions/freeze method, |
3532 // Old WebKit does not have Object.preventExtensions/freeze method, |
3512 // return new empty object instead with no [[set]] accessor |
3533 // return new empty object instead with no [[set]] accessor |
3513 Object.defineProperty( this.cache = {}, 0, { |
3534 Object.defineProperty( this.cache = {}, 0, { |
3514 get: function() { |
3535 get: function() { |
3515 return {}; |
3536 return {}; |
3516 } |
3537 } |
3517 }); |
3538 }); |
3518 |
3539 |
3519 this.expando = jQuery.expando + Math.random(); |
3540 this.expando = jQuery.expando + Data.uid++; |
3520 } |
3541 } |
3521 |
3542 |
3522 Data.uid = 1; |
3543 Data.uid = 1; |
3523 Data.accepts = jQuery.acceptData; |
3544 Data.accepts = jQuery.acceptData; |
3524 |
3545 |
3542 // Secure it in a non-enumerable, non-writable property |
3563 // Secure it in a non-enumerable, non-writable property |
3543 try { |
3564 try { |
3544 descriptor[ this.expando ] = { value: unlock }; |
3565 descriptor[ this.expando ] = { value: unlock }; |
3545 Object.defineProperties( owner, descriptor ); |
3566 Object.defineProperties( owner, descriptor ); |
3546 |
3567 |
3547 // Support: Android < 4 |
3568 // Support: Android<4 |
3548 // Fallback to a less secure definition |
3569 // Fallback to a less secure definition |
3549 } catch ( e ) { |
3570 } catch ( e ) { |
3550 descriptor[ this.expando ] = unlock; |
3571 descriptor[ this.expando ] = unlock; |
3551 jQuery.extend( owner, descriptor ); |
3572 jQuery.extend( owner, descriptor ); |
3552 } |
3573 } |
3682 |
3703 |
3683 var data_user = new Data(); |
3704 var data_user = new Data(); |
3684 |
3705 |
3685 |
3706 |
3686 |
3707 |
3687 /* |
3708 // Implementation Summary |
3688 Implementation Summary |
3709 // |
3689 |
3710 // 1. Enforce API surface and semantic compatibility with 1.9.x branch |
3690 1. Enforce API surface and semantic compatibility with 1.9.x branch |
3711 // 2. Improve the module's maintainability by reducing the storage |
3691 2. Improve the module's maintainability by reducing the storage |
3712 // paths to a single mechanism. |
3692 paths to a single mechanism. |
3713 // 3. Use the same single mechanism to support "private" and "user" data. |
3693 3. Use the same single mechanism to support "private" and "user" data. |
3714 // 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) |
3694 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) |
3715 // 5. Avoid exposing implementation details on user objects (eg. expando properties) |
3695 5. Avoid exposing implementation details on user objects (eg. expando properties) |
3716 // 6. Provide a clear path for implementation upgrade to WeakMap in 2014 |
3696 6. Provide a clear path for implementation upgrade to WeakMap in 2014 |
3717 |
3697 */ |
|
3698 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, |
3718 var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, |
3699 rmultiDash = /([A-Z])/g; |
3719 rmultiDash = /([A-Z])/g; |
3700 |
3720 |
3701 function dataAttr( elem, key, data ) { |
3721 function dataAttr( elem, key, data ) { |
3702 var name; |
3722 var name; |
3897 // automatically dequeued |
3917 // automatically dequeued |
3898 if ( type === "fx" ) { |
3918 if ( type === "fx" ) { |
3899 queue.unshift( "inprogress" ); |
3919 queue.unshift( "inprogress" ); |
3900 } |
3920 } |
3901 |
3921 |
3902 // clear up the last queue stop function |
3922 // Clear up the last queue stop function |
3903 delete hooks.stop; |
3923 delete hooks.stop; |
3904 fn.call( elem, next, hooks ); |
3924 fn.call( elem, next, hooks ); |
3905 } |
3925 } |
3906 |
3926 |
3907 if ( !startLength && hooks ) { |
3927 if ( !startLength && hooks ) { |
3908 hooks.empty.fire(); |
3928 hooks.empty.fire(); |
3909 } |
3929 } |
3910 }, |
3930 }, |
3911 |
3931 |
3912 // not intended for public consumption - generates a queueHooks object, or returns the current one |
3932 // Not public - generate a queueHooks object, or return the current one |
3913 _queueHooks: function( elem, type ) { |
3933 _queueHooks: function( elem, type ) { |
3914 var key = type + "queueHooks"; |
3934 var key = type + "queueHooks"; |
3915 return data_priv.get( elem, key ) || data_priv.access( elem, key, { |
3935 return data_priv.get( elem, key ) || data_priv.access( elem, key, { |
3916 empty: jQuery.Callbacks("once memory").add(function() { |
3936 empty: jQuery.Callbacks("once memory").add(function() { |
3917 data_priv.remove( elem, [ type + "queue", key ] ); |
3937 data_priv.remove( elem, [ type + "queue", key ] ); |
3937 return data === undefined ? |
3957 return data === undefined ? |
3938 this : |
3958 this : |
3939 this.each(function() { |
3959 this.each(function() { |
3940 var queue = jQuery.queue( this, type, data ); |
3960 var queue = jQuery.queue( this, type, data ); |
3941 |
3961 |
3942 // ensure a hooks for this queue |
3962 // Ensure a hooks for this queue |
3943 jQuery._queueHooks( this, type ); |
3963 jQuery._queueHooks( this, type ); |
3944 |
3964 |
3945 if ( type === "fx" && queue[0] !== "inprogress" ) { |
3965 if ( type === "fx" && queue[0] !== "inprogress" ) { |
3946 jQuery.dequeue( this, type ); |
3966 jQuery.dequeue( this, type ); |
3947 } |
3967 } |
4004 (function() { |
4024 (function() { |
4005 var fragment = document.createDocumentFragment(), |
4025 var fragment = document.createDocumentFragment(), |
4006 div = fragment.appendChild( document.createElement( "div" ) ), |
4026 div = fragment.appendChild( document.createElement( "div" ) ), |
4007 input = document.createElement( "input" ); |
4027 input = document.createElement( "input" ); |
4008 |
4028 |
4009 // #11217 - WebKit loses check when the name is after the checked attribute |
4029 // Support: Safari<=5.1 |
4030 // Check state lost if the name is set (#11217) |
|
4010 // Support: Windows Web Apps (WWA) |
4031 // Support: Windows Web Apps (WWA) |
4011 // `name` and `type` need .setAttribute for WWA |
4032 // `name` and `type` must use .setAttribute for WWA (#14901) |
4012 input.setAttribute( "type", "radio" ); |
4033 input.setAttribute( "type", "radio" ); |
4013 input.setAttribute( "checked", "checked" ); |
4034 input.setAttribute( "checked", "checked" ); |
4014 input.setAttribute( "name", "t" ); |
4035 input.setAttribute( "name", "t" ); |
4015 |
4036 |
4016 div.appendChild( input ); |
4037 div.appendChild( input ); |
4017 |
4038 |
4018 // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 |
4039 // Support: Safari<=5.1, Android<4.2 |
4019 // old WebKit doesn't clone checked state correctly in fragments |
4040 // Older WebKit doesn't clone checked state correctly in fragments |
4020 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; |
4041 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; |
4021 |
4042 |
4043 // Support: IE<=11+ |
|
4022 // Make sure textarea (and checkbox) defaultValue is properly cloned |
4044 // Make sure textarea (and checkbox) defaultValue is properly cloned |
4023 // Support: IE9-IE11+ |
|
4024 div.innerHTML = "<textarea>x</textarea>"; |
4045 div.innerHTML = "<textarea>x</textarea>"; |
4025 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; |
4046 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; |
4026 })(); |
4047 })(); |
4027 var strundefined = typeof undefined; |
4048 var strundefined = typeof undefined; |
4028 |
4049 |
4396 event.currentTarget = matched.elem; |
4417 event.currentTarget = matched.elem; |
4397 |
4418 |
4398 j = 0; |
4419 j = 0; |
4399 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { |
4420 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { |
4400 |
4421 |
4401 // Triggered event must either 1) have no namespace, or |
4422 // Triggered event must either 1) have no namespace, or 2) have namespace(s) |
4402 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). |
4423 // 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 ) ) { |
4424 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { |
4404 |
4425 |
4405 event.handleObj = handleObj; |
4426 event.handleObj = handleObj; |
4406 event.data = handleObj.data; |
4427 event.data = handleObj.data; |
4407 |
4428 |
4547 // All events should have a target; Cordova deviceready doesn't |
4568 // All events should have a target; Cordova deviceready doesn't |
4548 if ( !event.target ) { |
4569 if ( !event.target ) { |
4549 event.target = document; |
4570 event.target = document; |
4550 } |
4571 } |
4551 |
4572 |
4552 // Support: Safari 6.0+, Chrome < 28 |
4573 // Support: Safari 6.0+, Chrome<28 |
4553 // Target should not be a text node (#504, #13143) |
4574 // Target should not be a text node (#504, #13143) |
4554 if ( event.target.nodeType === 3 ) { |
4575 if ( event.target.nodeType === 3 ) { |
4555 event.target = event.target.parentNode; |
4576 event.target = event.target.parentNode; |
4556 } |
4577 } |
4557 |
4578 |
4652 |
4673 |
4653 // Events bubbling up the document may have been marked as prevented |
4674 // Events bubbling up the document may have been marked as prevented |
4654 // by a handler lower down the tree; reflect the correct value. |
4675 // by a handler lower down the tree; reflect the correct value. |
4655 this.isDefaultPrevented = src.defaultPrevented || |
4676 this.isDefaultPrevented = src.defaultPrevented || |
4656 src.defaultPrevented === undefined && |
4677 src.defaultPrevented === undefined && |
4657 // Support: Android < 4.0 |
4678 // Support: Android<4.0 |
4658 src.returnValue === false ? |
4679 src.returnValue === false ? |
4659 returnTrue : |
4680 returnTrue : |
4660 returnFalse; |
4681 returnFalse; |
4661 |
4682 |
4662 // Event type |
4683 // Event type |
4742 return ret; |
4763 return ret; |
4743 } |
4764 } |
4744 }; |
4765 }; |
4745 }); |
4766 }); |
4746 |
4767 |
4768 // Support: Firefox, Chrome, Safari |
|
4747 // Create "bubbling" focus and blur events |
4769 // Create "bubbling" focus and blur events |
4748 // Support: Firefox, Chrome, Safari |
|
4749 if ( !support.focusinBubbles ) { |
4770 if ( !support.focusinBubbles ) { |
4750 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { |
4771 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { |
4751 |
4772 |
4752 // Attach a single capturing handler on the document while someone wants focusin/focusout |
4773 // Attach a single capturing handler on the document while someone wants focusin/focusout |
4753 var handler = function( event ) { |
4774 var handler = function( event ) { |
4896 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, |
4917 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, |
4897 |
4918 |
4898 // We have to close these tags to support XHTML (#13200) |
4919 // We have to close these tags to support XHTML (#13200) |
4899 wrapMap = { |
4920 wrapMap = { |
4900 |
4921 |
4901 // Support: IE 9 |
4922 // Support: IE9 |
4902 option: [ 1, "<select multiple='multiple'>", "</select>" ], |
4923 option: [ 1, "<select multiple='multiple'>", "</select>" ], |
4903 |
4924 |
4904 thead: [ 1, "<table>", "</table>" ], |
4925 thead: [ 1, "<table>", "</table>" ], |
4905 col: [ 2, "<table><colgroup>", "</colgroup></table>" ], |
4926 col: [ 2, "<table><colgroup>", "</colgroup></table>" ], |
4906 tr: [ 2, "<table><tbody>", "</tbody></table>" ], |
4927 tr: [ 2, "<table><tbody>", "</tbody></table>" ], |
4907 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], |
4928 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], |
4908 |
4929 |
4909 _default: [ 0, "", "" ] |
4930 _default: [ 0, "", "" ] |
4910 }; |
4931 }; |
4911 |
4932 |
4912 // Support: IE 9 |
4933 // Support: IE9 |
4913 wrapMap.optgroup = wrapMap.option; |
4934 wrapMap.optgroup = wrapMap.option; |
4914 |
4935 |
4915 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; |
4936 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; |
4916 wrapMap.th = wrapMap.td; |
4937 wrapMap.th = wrapMap.td; |
4917 |
4938 |
4997 return tag === undefined || tag && jQuery.nodeName( context, tag ) ? |
5018 return tag === undefined || tag && jQuery.nodeName( context, tag ) ? |
4998 jQuery.merge( [ context ], ret ) : |
5019 jQuery.merge( [ context ], ret ) : |
4999 ret; |
5020 ret; |
5000 } |
5021 } |
5001 |
5022 |
5002 // Support: IE >= 9 |
5023 // Fix IE bugs, see support tests |
5003 function fixInput( src, dest ) { |
5024 function fixInput( src, dest ) { |
5004 var nodeName = dest.nodeName.toLowerCase(); |
5025 var nodeName = dest.nodeName.toLowerCase(); |
5005 |
5026 |
5006 // Fails to persist the checked state of a cloned checkbox or radio button. |
5027 // Fails to persist the checked state of a cloned checkbox or radio button. |
5007 if ( nodeName === "input" && rcheckableType.test( src.type ) ) { |
5028 if ( nodeName === "input" && rcheckableType.test( src.type ) ) { |
5017 clone: function( elem, dataAndEvents, deepDataAndEvents ) { |
5038 clone: function( elem, dataAndEvents, deepDataAndEvents ) { |
5018 var i, l, srcElements, destElements, |
5039 var i, l, srcElements, destElements, |
5019 clone = elem.cloneNode( true ), |
5040 clone = elem.cloneNode( true ), |
5020 inPage = jQuery.contains( elem.ownerDocument, elem ); |
5041 inPage = jQuery.contains( elem.ownerDocument, elem ); |
5021 |
5042 |
5022 // Support: IE >= 9 |
5043 // Fix IE cloning issues |
5023 // Fix Cloning issues |
|
5024 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && |
5044 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && |
5025 !jQuery.isXMLDoc( elem ) ) { |
5045 !jQuery.isXMLDoc( elem ) ) { |
5026 |
5046 |
5027 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 |
5047 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 |
5028 destElements = getAll( clone ); |
5048 destElements = getAll( clone ); |
5069 |
5089 |
5070 if ( elem || elem === 0 ) { |
5090 if ( elem || elem === 0 ) { |
5071 |
5091 |
5072 // Add nodes directly |
5092 // Add nodes directly |
5073 if ( jQuery.type( elem ) === "object" ) { |
5093 if ( jQuery.type( elem ) === "object" ) { |
5074 // Support: QtWebKit |
5094 // Support: QtWebKit, PhantomJS |
5075 // jQuery.merge because push.apply(_, arraylike) throws |
5095 // push.apply(_, arraylike) throws on ancient WebKit |
5076 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); |
5096 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); |
5077 |
5097 |
5078 // Convert non-html into a text node |
5098 // Convert non-html into a text node |
5079 } else if ( !rhtml.test( elem ) ) { |
5099 } else if ( !rhtml.test( elem ) ) { |
5080 nodes.push( context.createTextNode( elem ) ); |
5100 nodes.push( context.createTextNode( elem ) ); |
5092 j = wrap[ 0 ]; |
5112 j = wrap[ 0 ]; |
5093 while ( j-- ) { |
5113 while ( j-- ) { |
5094 tmp = tmp.lastChild; |
5114 tmp = tmp.lastChild; |
5095 } |
5115 } |
5096 |
5116 |
5097 // Support: QtWebKit |
5117 // Support: QtWebKit, PhantomJS |
5098 // jQuery.merge because push.apply(_, arraylike) throws |
5118 // push.apply(_, arraylike) throws on ancient WebKit |
5099 jQuery.merge( nodes, tmp.childNodes ); |
5119 jQuery.merge( nodes, tmp.childNodes ); |
5100 |
5120 |
5101 // Remember the top-level container |
5121 // Remember the top-level container |
5102 tmp = fragment.firstChild; |
5122 tmp = fragment.firstChild; |
5103 |
5123 |
5104 // Fixes #12346 |
5124 // Ensure the created nodes are orphaned (#12392) |
5105 // Support: Webkit, IE |
|
5106 tmp.textContent = ""; |
5125 tmp.textContent = ""; |
5107 } |
5126 } |
5108 } |
5127 } |
5109 } |
5128 } |
5110 |
5129 |
5462 elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), |
5481 elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), |
5463 |
5482 |
5464 // getDefaultComputedStyle might be reliably used only on attached element |
5483 // getDefaultComputedStyle might be reliably used only on attached element |
5465 display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? |
5484 display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? |
5466 |
5485 |
5467 // Use of this method is a temporary fix (more like optmization) until something better comes along, |
5486 // Use of this method is a temporary fix (more like optimization) until something better comes along, |
5468 // since it was removed from specification and supported only in FF |
5487 // since it was removed from specification and supported only in FF |
5469 style.display : jQuery.css( elem[ 0 ], "display" ); |
5488 style.display : jQuery.css( elem[ 0 ], "display" ); |
5470 |
5489 |
5471 // We don't have any data stored on the element, |
5490 // We don't have any data stored on the element, |
5472 // so use "detach" method as fast way to get rid of the element |
5491 // so use "detach" method as fast way to get rid of the element |
5512 var rmargin = (/^margin/); |
5531 var rmargin = (/^margin/); |
5513 |
5532 |
5514 var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); |
5533 var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); |
5515 |
5534 |
5516 var getStyles = function( elem ) { |
5535 var getStyles = function( elem ) { |
5517 return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); |
5536 // Support: IE<=11+, Firefox<=30+ (#15098, #14150) |
5537 // IE throws on elements created in popups |
|
5538 // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" |
|
5539 if ( elem.ownerDocument.defaultView.opener ) { |
|
5540 return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); |
|
5541 } |
|
5542 |
|
5543 return window.getComputedStyle( elem, null ); |
|
5518 }; |
5544 }; |
5519 |
5545 |
5520 |
5546 |
5521 |
5547 |
5522 function curCSS( elem, name, computed ) { |
5548 function curCSS( elem, name, computed ) { |
5524 style = elem.style; |
5550 style = elem.style; |
5525 |
5551 |
5526 computed = computed || getStyles( elem ); |
5552 computed = computed || getStyles( elem ); |
5527 |
5553 |
5528 // Support: IE9 |
5554 // Support: IE9 |
5529 // getPropertyValue is only needed for .css('filter') in IE9, see #12537 |
5555 // getPropertyValue is only needed for .css('filter') (#12537) |
5530 if ( computed ) { |
5556 if ( computed ) { |
5531 ret = computed.getPropertyValue( name ) || computed[ name ]; |
5557 ret = computed.getPropertyValue( name ) || computed[ name ]; |
5532 } |
5558 } |
5533 |
5559 |
5534 if ( computed ) { |
5560 if ( computed ) { |
5570 function addGetHookIf( conditionFn, hookFn ) { |
5596 function addGetHookIf( conditionFn, hookFn ) { |
5571 // Define the hook, we'll check on the first run if it's really needed. |
5597 // Define the hook, we'll check on the first run if it's really needed. |
5572 return { |
5598 return { |
5573 get: function() { |
5599 get: function() { |
5574 if ( conditionFn() ) { |
5600 if ( conditionFn() ) { |
5575 // Hook not needed (or it's not possible to use it due to missing dependency), |
5601 // Hook not needed (or it's not possible to use it due |
5576 // remove it. |
5602 // to missing dependency), remove it. |
5577 // Since there are no other hooks for marginRight, remove the whole object. |
|
5578 delete this.get; |
5603 delete this.get; |
5579 return; |
5604 return; |
5580 } |
5605 } |
5581 |
5606 |
5582 // Hook needed; redefine it so that the support test is not executed again. |
5607 // Hook needed; redefine it so that the support test is not executed again. |
5583 |
|
5584 return (this.get = hookFn).apply( this, arguments ); |
5608 return (this.get = hookFn).apply( this, arguments ); |
5585 } |
5609 } |
5586 }; |
5610 }; |
5587 } |
5611 } |
5588 |
5612 |
5595 |
5619 |
5596 if ( !div.style ) { |
5620 if ( !div.style ) { |
5597 return; |
5621 return; |
5598 } |
5622 } |
5599 |
5623 |
5624 // Support: IE9-11+ |
|
5625 // Style of cloned element affects source element cloned (#8908) |
|
5600 div.style.backgroundClip = "content-box"; |
5626 div.style.backgroundClip = "content-box"; |
5601 div.cloneNode( true ).style.backgroundClip = ""; |
5627 div.cloneNode( true ).style.backgroundClip = ""; |
5602 support.clearCloneStyle = div.style.backgroundClip === "content-box"; |
5628 support.clearCloneStyle = div.style.backgroundClip === "content-box"; |
5603 |
5629 |
5604 container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" + |
5630 container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" + |
5627 // Support: node.js jsdom |
5653 // Support: node.js jsdom |
5628 // Don't assume that getComputedStyle is a property of the global object |
5654 // Don't assume that getComputedStyle is a property of the global object |
5629 if ( window.getComputedStyle ) { |
5655 if ( window.getComputedStyle ) { |
5630 jQuery.extend( support, { |
5656 jQuery.extend( support, { |
5631 pixelPosition: function() { |
5657 pixelPosition: function() { |
5658 |
|
5632 // This test is executed only once but we still do memoizing |
5659 // This test is executed only once but we still do memoizing |
5633 // since we can use the boxSizingReliable pre-computing. |
5660 // since we can use the boxSizingReliable pre-computing. |
5634 // No need to check if the test was already performed, though. |
5661 // No need to check if the test was already performed, though. |
5635 computePixelPositionAndBoxSizingReliable(); |
5662 computePixelPositionAndBoxSizingReliable(); |
5636 return pixelPositionVal; |
5663 return pixelPositionVal; |
5640 computePixelPositionAndBoxSizingReliable(); |
5667 computePixelPositionAndBoxSizingReliable(); |
5641 } |
5668 } |
5642 return boxSizingReliableVal; |
5669 return boxSizingReliableVal; |
5643 }, |
5670 }, |
5644 reliableMarginRight: function() { |
5671 reliableMarginRight: function() { |
5672 |
|
5645 // Support: Android 2.3 |
5673 // Support: Android 2.3 |
5646 // Check if div with explicit width and no margin-right incorrectly |
5674 // Check if div with explicit width and no margin-right incorrectly |
5647 // gets computed margin-right based on width of container. (#3333) |
5675 // gets computed margin-right based on width of container. (#3333) |
5648 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right |
5676 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right |
5649 // This support function is only executed once so no memoizing is needed. |
5677 // This support function is only executed once so no memoizing is needed. |
5661 docElem.appendChild( container ); |
5689 docElem.appendChild( container ); |
5662 |
5690 |
5663 ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); |
5691 ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); |
5664 |
5692 |
5665 docElem.removeChild( container ); |
5693 docElem.removeChild( container ); |
5694 div.removeChild( marginDiv ); |
|
5666 |
5695 |
5667 return ret; |
5696 return ret; |
5668 } |
5697 } |
5669 }); |
5698 }); |
5670 } |
5699 } |
5692 return ret; |
5721 return ret; |
5693 }; |
5722 }; |
5694 |
5723 |
5695 |
5724 |
5696 var |
5725 var |
5697 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" |
5726 // 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 |
5727 // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display |
5699 rdisplayswap = /^(none|table(?!-c[ea]).+)/, |
5728 rdisplayswap = /^(none|table(?!-c[ea]).+)/, |
5700 rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), |
5729 rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ), |
5701 rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), |
5730 rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), |
5702 |
5731 |
5703 cssShow = { position: "absolute", visibility: "hidden", display: "block" }, |
5732 cssShow = { position: "absolute", visibility: "hidden", display: "block" }, |
5706 fontWeight: "400" |
5735 fontWeight: "400" |
5707 }, |
5736 }, |
5708 |
5737 |
5709 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; |
5738 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; |
5710 |
5739 |
5711 // return a css property mapped to a potentially vendor prefixed property |
5740 // Return a css property mapped to a potentially vendor prefixed property |
5712 function vendorPropName( style, name ) { |
5741 function vendorPropName( style, name ) { |
5713 |
5742 |
5714 // shortcut for names that are not vendor prefixed |
5743 // Shortcut for names that are not vendor prefixed |
5715 if ( name in style ) { |
5744 if ( name in style ) { |
5716 return name; |
5745 return name; |
5717 } |
5746 } |
5718 |
5747 |
5719 // check for vendor prefixed names |
5748 // Check for vendor prefixed names |
5720 var capName = name[0].toUpperCase() + name.slice(1), |
5749 var capName = name[0].toUpperCase() + name.slice(1), |
5721 origName = name, |
5750 origName = name, |
5722 i = cssPrefixes.length; |
5751 i = cssPrefixes.length; |
5723 |
5752 |
5724 while ( i-- ) { |
5753 while ( i-- ) { |
5747 name === "width" ? 1 : 0, |
5776 name === "width" ? 1 : 0, |
5748 |
5777 |
5749 val = 0; |
5778 val = 0; |
5750 |
5779 |
5751 for ( ; i < 4; i += 2 ) { |
5780 for ( ; i < 4; i += 2 ) { |
5752 // both box models exclude margin, so add it if we want it |
5781 // Both box models exclude margin, so add it if we want it |
5753 if ( extra === "margin" ) { |
5782 if ( extra === "margin" ) { |
5754 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); |
5783 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); |
5755 } |
5784 } |
5756 |
5785 |
5757 if ( isBorderBox ) { |
5786 if ( isBorderBox ) { |
5758 // border-box includes padding, so remove it if we want content |
5787 // border-box includes padding, so remove it if we want content |
5759 if ( extra === "content" ) { |
5788 if ( extra === "content" ) { |
5760 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); |
5789 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); |
5761 } |
5790 } |
5762 |
5791 |
5763 // at this point, extra isn't border nor margin, so remove border |
5792 // At this point, extra isn't border nor margin, so remove border |
5764 if ( extra !== "margin" ) { |
5793 if ( extra !== "margin" ) { |
5765 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); |
5794 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); |
5766 } |
5795 } |
5767 } else { |
5796 } else { |
5768 // at this point, extra isn't content, so add padding |
5797 // At this point, extra isn't content, so add padding |
5769 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); |
5798 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); |
5770 |
5799 |
5771 // at this point, extra isn't content nor padding, so add border |
5800 // At this point, extra isn't content nor padding, so add border |
5772 if ( extra !== "padding" ) { |
5801 if ( extra !== "padding" ) { |
5773 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); |
5802 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); |
5774 } |
5803 } |
5775 } |
5804 } |
5776 } |
5805 } |
5784 var valueIsBorderBox = true, |
5813 var valueIsBorderBox = true, |
5785 val = name === "width" ? elem.offsetWidth : elem.offsetHeight, |
5814 val = name === "width" ? elem.offsetWidth : elem.offsetHeight, |
5786 styles = getStyles( elem ), |
5815 styles = getStyles( elem ), |
5787 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; |
5816 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; |
5788 |
5817 |
5789 // some non-html elements return undefined for offsetWidth, so check for null/undefined |
5818 // Some non-html elements return undefined for offsetWidth, so check for null/undefined |
5790 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 |
5819 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 |
5791 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 |
5820 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 |
5792 if ( val <= 0 || val == null ) { |
5821 if ( val <= 0 || val == null ) { |
5793 // Fall back to computed then uncomputed css if necessary |
5822 // Fall back to computed then uncomputed css if necessary |
5794 val = curCSS( elem, name, styles ); |
5823 val = curCSS( elem, name, styles ); |
5799 // Computed unit is not pixels. Stop here and return. |
5828 // Computed unit is not pixels. Stop here and return. |
5800 if ( rnumnonpx.test(val) ) { |
5829 if ( rnumnonpx.test(val) ) { |
5801 return val; |
5830 return val; |
5802 } |
5831 } |
5803 |
5832 |
5804 // we need the check for style in case a browser which returns unreliable values |
5833 // Check for style in case a browser which returns unreliable values |
5805 // for getComputedStyle silently falls back to the reliable elem.style |
5834 // for getComputedStyle silently falls back to the reliable elem.style |
5806 valueIsBorderBox = isBorderBox && |
5835 valueIsBorderBox = isBorderBox && |
5807 ( support.boxSizingReliable() || val === elem.style[ name ] ); |
5836 ( support.boxSizingReliable() || val === elem.style[ name ] ); |
5808 |
5837 |
5809 // Normalize "", auto, and prepare for extra |
5838 // Normalize "", auto, and prepare for extra |
5810 val = parseFloat( val ) || 0; |
5839 val = parseFloat( val ) || 0; |
5811 } |
5840 } |
5812 |
5841 |
5813 // use the active box-sizing model to add/subtract irrelevant styles |
5842 // Use the active box-sizing model to add/subtract irrelevant styles |
5814 return ( val + |
5843 return ( val + |
5815 augmentWidthOrHeight( |
5844 augmentWidthOrHeight( |
5816 elem, |
5845 elem, |
5817 name, |
5846 name, |
5818 extra || ( isBorderBox ? "border" : "content" ), |
5847 extra || ( isBorderBox ? "border" : "content" ), |
5872 |
5901 |
5873 return elements; |
5902 return elements; |
5874 } |
5903 } |
5875 |
5904 |
5876 jQuery.extend({ |
5905 jQuery.extend({ |
5906 |
|
5877 // Add in style property hooks for overriding the default |
5907 // Add in style property hooks for overriding the default |
5878 // behavior of getting and setting a style property |
5908 // behavior of getting and setting a style property |
5879 cssHooks: { |
5909 cssHooks: { |
5880 opacity: { |
5910 opacity: { |
5881 get: function( elem, computed ) { |
5911 get: function( elem, computed ) { |
5882 if ( computed ) { |
5912 if ( computed ) { |
5913 |
|
5883 // We should always get a number back from opacity |
5914 // We should always get a number back from opacity |
5884 var ret = curCSS( elem, "opacity" ); |
5915 var ret = curCSS( elem, "opacity" ); |
5885 return ret === "" ? "1" : ret; |
5916 return ret === "" ? "1" : ret; |
5886 } |
5917 } |
5887 } |
5918 } |
5905 }, |
5936 }, |
5906 |
5937 |
5907 // Add in properties whose names you wish to fix before |
5938 // Add in properties whose names you wish to fix before |
5908 // setting or getting the value |
5939 // setting or getting the value |
5909 cssProps: { |
5940 cssProps: { |
5910 // normalize float css property |
|
5911 "float": "cssFloat" |
5941 "float": "cssFloat" |
5912 }, |
5942 }, |
5913 |
5943 |
5914 // Get and set the style property on a DOM Node |
5944 // Get and set the style property on a DOM Node |
5915 style: function( elem, name, value, extra ) { |
5945 style: function( elem, name, value, extra ) { |
5946 |
|
5916 // Don't set styles on text and comment nodes |
5947 // Don't set styles on text and comment nodes |
5917 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { |
5948 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { |
5918 return; |
5949 return; |
5919 } |
5950 } |
5920 |
5951 |
5923 origName = jQuery.camelCase( name ), |
5954 origName = jQuery.camelCase( name ), |
5924 style = elem.style; |
5955 style = elem.style; |
5925 |
5956 |
5926 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); |
5957 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); |
5927 |
5958 |
5928 // gets hook for the prefixed version |
5959 // Gets hook for the prefixed version, then unprefixed version |
5929 // followed by the unprefixed version |
|
5930 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; |
5960 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; |
5931 |
5961 |
5932 // Check if we're setting a value |
5962 // Check if we're setting a value |
5933 if ( value !== undefined ) { |
5963 if ( value !== undefined ) { |
5934 type = typeof value; |
5964 type = typeof value; |
5935 |
5965 |
5936 // convert relative number strings (+= or -=) to relative numbers. #7345 |
5966 // Convert "+=" or "-=" to relative numbers (#7345) |
5937 if ( type === "string" && (ret = rrelNum.exec( value )) ) { |
5967 if ( type === "string" && (ret = rrelNum.exec( value )) ) { |
5938 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); |
5968 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); |
5939 // Fixes bug #9237 |
5969 // Fixes bug #9237 |
5940 type = "number"; |
5970 type = "number"; |
5941 } |
5971 } |
5942 |
5972 |
5943 // Make sure that null and NaN values aren't set. See: #7116 |
5973 // Make sure that null and NaN values aren't set (#7116) |
5944 if ( value == null || value !== value ) { |
5974 if ( value == null || value !== value ) { |
5945 return; |
5975 return; |
5946 } |
5976 } |
5947 |
5977 |
5948 // If a number was passed in, add 'px' to the (except for certain CSS properties) |
5978 // If a number, add 'px' to the (except for certain CSS properties) |
5949 if ( type === "number" && !jQuery.cssNumber[ origName ] ) { |
5979 if ( type === "number" && !jQuery.cssNumber[ origName ] ) { |
5950 value += "px"; |
5980 value += "px"; |
5951 } |
5981 } |
5952 |
5982 |
5953 // Fixes #8908, it can be done more correctly by specifying setters in cssHooks, |
5983 // Support: IE9-11+ |
5954 // but it would mean to define eight (for every problematic property) identical functions |
5984 // background-* props affect original clone's values |
5955 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { |
5985 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { |
5956 style[ name ] = "inherit"; |
5986 style[ name ] = "inherit"; |
5957 } |
5987 } |
5958 |
5988 |
5959 // If a hook was provided, use that value, otherwise just set the specified value |
5989 // If a hook was provided, use that value, otherwise just set the specified value |
5977 origName = jQuery.camelCase( name ); |
6007 origName = jQuery.camelCase( name ); |
5978 |
6008 |
5979 // Make sure that we're working with the right name |
6009 // Make sure that we're working with the right name |
5980 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); |
6010 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); |
5981 |
6011 |
5982 // gets hook for the prefixed version |
6012 // Try prefixed name followed by the unprefixed name |
5983 // followed by the unprefixed version |
|
5984 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; |
6013 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; |
5985 |
6014 |
5986 // If a hook was provided get the computed value from there |
6015 // If a hook was provided get the computed value from there |
5987 if ( hooks && "get" in hooks ) { |
6016 if ( hooks && "get" in hooks ) { |
5988 val = hooks.get( elem, true, extra ); |
6017 val = hooks.get( elem, true, extra ); |
5991 // Otherwise, if a way to get the computed value exists, use that |
6020 // Otherwise, if a way to get the computed value exists, use that |
5992 if ( val === undefined ) { |
6021 if ( val === undefined ) { |
5993 val = curCSS( elem, name, styles ); |
6022 val = curCSS( elem, name, styles ); |
5994 } |
6023 } |
5995 |
6024 |
5996 //convert "normal" to computed value |
6025 // Convert "normal" to computed value |
5997 if ( val === "normal" && name in cssNormalTransform ) { |
6026 if ( val === "normal" && name in cssNormalTransform ) { |
5998 val = cssNormalTransform[ name ]; |
6027 val = cssNormalTransform[ name ]; |
5999 } |
6028 } |
6000 |
6029 |
6001 // Return, converting to number if forced or a qualifier was provided and val looks numeric |
6030 // Make numeric if forced or a qualifier was provided and val looks numeric |
6002 if ( extra === "" || extra ) { |
6031 if ( extra === "" || extra ) { |
6003 num = parseFloat( val ); |
6032 num = parseFloat( val ); |
6004 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; |
6033 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; |
6005 } |
6034 } |
6006 return val; |
6035 return val; |
6009 |
6038 |
6010 jQuery.each([ "height", "width" ], function( i, name ) { |
6039 jQuery.each([ "height", "width" ], function( i, name ) { |
6011 jQuery.cssHooks[ name ] = { |
6040 jQuery.cssHooks[ name ] = { |
6012 get: function( elem, computed, extra ) { |
6041 get: function( elem, computed, extra ) { |
6013 if ( computed ) { |
6042 if ( computed ) { |
6014 // certain elements can have dimension info if we invisibly show them |
6043 |
6015 // however, it must have a current display style that would benefit from this |
6044 // Certain elements can have dimension info if we invisibly show them |
6045 // but it must have a current display style that would benefit |
|
6016 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ? |
6046 return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ? |
6017 jQuery.swap( elem, cssShow, function() { |
6047 jQuery.swap( elem, cssShow, function() { |
6018 return getWidthOrHeight( elem, name, extra ); |
6048 return getWidthOrHeight( elem, name, extra ); |
6019 }) : |
6049 }) : |
6020 getWidthOrHeight( elem, name, extra ); |
6050 getWidthOrHeight( elem, name, extra ); |
6038 |
6068 |
6039 // Support: Android 2.3 |
6069 // Support: Android 2.3 |
6040 jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, |
6070 jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, |
6041 function( elem, computed ) { |
6071 function( elem, computed ) { |
6042 if ( computed ) { |
6072 if ( 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" }, |
6073 return jQuery.swap( elem, { "display": "inline-block" }, |
6046 curCSS, [ elem, "marginRight" ] ); |
6074 curCSS, [ elem, "marginRight" ] ); |
6047 } |
6075 } |
6048 } |
6076 } |
6049 ); |
6077 ); |
6057 jQuery.cssHooks[ prefix + suffix ] = { |
6085 jQuery.cssHooks[ prefix + suffix ] = { |
6058 expand: function( value ) { |
6086 expand: function( value ) { |
6059 var i = 0, |
6087 var i = 0, |
6060 expanded = {}, |
6088 expanded = {}, |
6061 |
6089 |
6062 // assumes a single number if not a string |
6090 // Assumes a single number if not a string |
6063 parts = typeof value === "string" ? value.split(" ") : [ value ]; |
6091 parts = typeof value === "string" ? value.split(" ") : [ value ]; |
6064 |
6092 |
6065 for ( ; i < 4; i++ ) { |
6093 for ( ; i < 4; i++ ) { |
6066 expanded[ prefix + cssExpand[ i ] + suffix ] = |
6094 expanded[ prefix + cssExpand[ i ] + suffix ] = |
6067 parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; |
6095 parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; |
6180 if ( tween.elem[ tween.prop ] != null && |
6208 if ( tween.elem[ tween.prop ] != null && |
6181 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { |
6209 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { |
6182 return tween.elem[ tween.prop ]; |
6210 return tween.elem[ tween.prop ]; |
6183 } |
6211 } |
6184 |
6212 |
6185 // passing an empty string as a 3rd parameter to .css will automatically |
6213 // 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 |
6214 // attempt a parseFloat and fallback to a string if the parse fails. |
6187 // so, simple values such as "10px" are parsed to Float. |
6215 // Simple values such as "10px" are parsed to Float; |
6188 // complex values such as "rotate(1rad)" are returned as is. |
6216 // complex values such as "rotate(1rad)" are returned as-is. |
6189 result = jQuery.css( tween.elem, tween.prop, "" ); |
6217 result = jQuery.css( tween.elem, tween.prop, "" ); |
6190 // Empty strings, null, undefined and "auto" are converted to 0. |
6218 // Empty strings, null, undefined and "auto" are converted to 0. |
6191 return !result || result === "auto" ? 0 : result; |
6219 return !result || result === "auto" ? 0 : result; |
6192 }, |
6220 }, |
6193 set: function( tween ) { |
6221 set: function( tween ) { |
6194 // use step hook for back compat - use cssHook if its there - use .style if its |
6222 // Use step hook for back compat. |
6195 // available and use plain properties where available |
6223 // Use cssHook if its there. |
6224 // Use .style if available and use plain properties where available. |
|
6196 if ( jQuery.fx.step[ tween.prop ] ) { |
6225 if ( jQuery.fx.step[ tween.prop ] ) { |
6197 jQuery.fx.step[ tween.prop ]( tween ); |
6226 jQuery.fx.step[ tween.prop ]( tween ); |
6198 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { |
6227 } 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 ); |
6228 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); |
6200 } else { |
6229 } else { |
6204 } |
6233 } |
6205 }; |
6234 }; |
6206 |
6235 |
6207 // Support: IE9 |
6236 // Support: IE9 |
6208 // Panic based approach to setting things on disconnected nodes |
6237 // Panic based approach to setting things on disconnected nodes |
6209 |
|
6210 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { |
6238 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { |
6211 set: function( tween ) { |
6239 set: function( tween ) { |
6212 if ( tween.elem.nodeType && tween.elem.parentNode ) { |
6240 if ( tween.elem.nodeType && tween.elem.parentNode ) { |
6213 tween.elem[ tween.prop ] = tween.now; |
6241 tween.elem[ tween.prop ] = tween.now; |
6214 } |
6242 } |
6260 |
6288 |
6261 // Iteratively approximate from a nonzero starting point |
6289 // Iteratively approximate from a nonzero starting point |
6262 start = +target || 1; |
6290 start = +target || 1; |
6263 |
6291 |
6264 do { |
6292 do { |
6265 // If previous iteration zeroed out, double until we get *something* |
6293 // 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 |
6294 // Use string for doubling so we don't accidentally see scale as unchanged below |
6267 scale = scale || ".5"; |
6295 scale = scale || ".5"; |
6268 |
6296 |
6269 // Adjust and apply |
6297 // Adjust and apply |
6270 start = start / scale; |
6298 start = start / scale; |
6271 jQuery.style( tween.elem, prop, start + unit ); |
6299 jQuery.style( tween.elem, prop, start + unit ); |
6272 |
6300 |
6273 // Update scale, tolerating zero or NaN from tween.cur() |
6301 // 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 |
6302 // break 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 ); |
6303 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); |
6276 } |
6304 } |
6277 |
6305 |
6278 // Update tween properties |
6306 // Update tween properties |
6279 if ( parts ) { |
6307 if ( parts ) { |
6301 function genFx( type, includeWidth ) { |
6329 function genFx( type, includeWidth ) { |
6302 var which, |
6330 var which, |
6303 i = 0, |
6331 i = 0, |
6304 attrs = { height: type }; |
6332 attrs = { height: type }; |
6305 |
6333 |
6306 // if we include width, step value is 1 to do all cssExpand values, |
6334 // 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 |
6335 // otherwise step value is 2 to skip over Left and Right |
6308 includeWidth = includeWidth ? 1 : 0; |
6336 includeWidth = includeWidth ? 1 : 0; |
6309 for ( ; i < 4 ; i += 2 - includeWidth ) { |
6337 for ( ; i < 4 ; i += 2 - includeWidth ) { |
6310 which = cssExpand[ i ]; |
6338 which = cssExpand[ i ]; |
6311 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; |
6339 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; |
6312 } |
6340 } |
6324 index = 0, |
6352 index = 0, |
6325 length = collection.length; |
6353 length = collection.length; |
6326 for ( ; index < length; index++ ) { |
6354 for ( ; index < length; index++ ) { |
6327 if ( (tween = collection[ index ].call( animation, prop, value )) ) { |
6355 if ( (tween = collection[ index ].call( animation, prop, value )) ) { |
6328 |
6356 |
6329 // we're done with this property |
6357 // We're done with this property |
6330 return tween; |
6358 return tween; |
6331 } |
6359 } |
6332 } |
6360 } |
6333 } |
6361 } |
6334 |
6362 |
6339 orig = {}, |
6367 orig = {}, |
6340 style = elem.style, |
6368 style = elem.style, |
6341 hidden = elem.nodeType && isHidden( elem ), |
6369 hidden = elem.nodeType && isHidden( elem ), |
6342 dataShow = data_priv.get( elem, "fxshow" ); |
6370 dataShow = data_priv.get( elem, "fxshow" ); |
6343 |
6371 |
6344 // handle queue: false promises |
6372 // Handle queue: false promises |
6345 if ( !opts.queue ) { |
6373 if ( !opts.queue ) { |
6346 hooks = jQuery._queueHooks( elem, "fx" ); |
6374 hooks = jQuery._queueHooks( elem, "fx" ); |
6347 if ( hooks.unqueued == null ) { |
6375 if ( hooks.unqueued == null ) { |
6348 hooks.unqueued = 0; |
6376 hooks.unqueued = 0; |
6349 oldfire = hooks.empty.fire; |
6377 oldfire = hooks.empty.fire; |
6354 }; |
6382 }; |
6355 } |
6383 } |
6356 hooks.unqueued++; |
6384 hooks.unqueued++; |
6357 |
6385 |
6358 anim.always(function() { |
6386 anim.always(function() { |
6359 // doing this makes sure that the complete handler will be called |
6387 // Ensure the complete handler is called before this completes |
6360 // before this completes |
|
6361 anim.always(function() { |
6388 anim.always(function() { |
6362 hooks.unqueued--; |
6389 hooks.unqueued--; |
6363 if ( !jQuery.queue( elem, "fx" ).length ) { |
6390 if ( !jQuery.queue( elem, "fx" ).length ) { |
6364 hooks.empty.fire(); |
6391 hooks.empty.fire(); |
6365 } |
6392 } |
6366 }); |
6393 }); |
6367 }); |
6394 }); |
6368 } |
6395 } |
6369 |
6396 |
6370 // height/width overflow pass |
6397 // Height/width overflow pass |
6371 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { |
6398 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { |
6372 // Make sure that nothing sneaks out |
6399 // Make sure that nothing sneaks out |
6373 // Record all 3 overflow attributes because IE9-10 do not |
6400 // Record all 3 overflow attributes because IE9-10 do not |
6374 // change the overflow attribute when overflowX and |
6401 // change the overflow attribute when overflowX and |
6375 // overflowY are set to the same value |
6402 // overflowY are set to the same value |
6427 } |
6454 } |
6428 } else { |
6455 } else { |
6429 dataShow = data_priv.access( elem, "fxshow", {} ); |
6456 dataShow = data_priv.access( elem, "fxshow", {} ); |
6430 } |
6457 } |
6431 |
6458 |
6432 // store state if its toggle - enables .stop().toggle() to "reverse" |
6459 // Store state if its toggle - enables .stop().toggle() to "reverse" |
6433 if ( toggle ) { |
6460 if ( toggle ) { |
6434 dataShow.hidden = !hidden; |
6461 dataShow.hidden = !hidden; |
6435 } |
6462 } |
6436 if ( hidden ) { |
6463 if ( hidden ) { |
6437 jQuery( elem ).show(); |
6464 jQuery( elem ).show(); |
6487 hooks = jQuery.cssHooks[ name ]; |
6514 hooks = jQuery.cssHooks[ name ]; |
6488 if ( hooks && "expand" in hooks ) { |
6515 if ( hooks && "expand" in hooks ) { |
6489 value = hooks.expand( value ); |
6516 value = hooks.expand( value ); |
6490 delete props[ name ]; |
6517 delete props[ name ]; |
6491 |
6518 |
6492 // not quite $.extend, this wont overwrite keys already present. |
6519 // Not quite $.extend, this won't overwrite existing keys. |
6493 // also - reusing 'index' from above because we have the correct "name" |
6520 // Reusing 'index' because we have the correct "name" |
6494 for ( index in value ) { |
6521 for ( index in value ) { |
6495 if ( !( index in props ) ) { |
6522 if ( !( index in props ) ) { |
6496 props[ index ] = value[ index ]; |
6523 props[ index ] = value[ index ]; |
6497 specialEasing[ index ] = easing; |
6524 specialEasing[ index ] = easing; |
6498 } |
6525 } |
6507 var result, |
6534 var result, |
6508 stopped, |
6535 stopped, |
6509 index = 0, |
6536 index = 0, |
6510 length = animationPrefilters.length, |
6537 length = animationPrefilters.length, |
6511 deferred = jQuery.Deferred().always( function() { |
6538 deferred = jQuery.Deferred().always( function() { |
6512 // don't match elem in the :animated selector |
6539 // Don't match elem in the :animated selector |
6513 delete tick.elem; |
6540 delete tick.elem; |
6514 }), |
6541 }), |
6515 tick = function() { |
6542 tick = function() { |
6516 if ( stopped ) { |
6543 if ( stopped ) { |
6517 return false; |
6544 return false; |
6518 } |
6545 } |
6519 var currentTime = fxNow || createFxNow(), |
6546 var currentTime = fxNow || createFxNow(), |
6520 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), |
6547 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), |
6521 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) |
6548 // Support: Android 2.3 |
6549 // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) |
|
6522 temp = remaining / animation.duration || 0, |
6550 temp = remaining / animation.duration || 0, |
6523 percent = 1 - temp, |
6551 percent = 1 - temp, |
6524 index = 0, |
6552 index = 0, |
6525 length = animation.tweens.length; |
6553 length = animation.tweens.length; |
6526 |
6554 |
6552 animation.tweens.push( tween ); |
6580 animation.tweens.push( tween ); |
6553 return tween; |
6581 return tween; |
6554 }, |
6582 }, |
6555 stop: function( gotoEnd ) { |
6583 stop: function( gotoEnd ) { |
6556 var index = 0, |
6584 var index = 0, |
6557 // if we are going to the end, we want to run all the tweens |
6585 // If we are going to the end, we want to run all the tweens |
6558 // otherwise we skip this part |
6586 // otherwise we skip this part |
6559 length = gotoEnd ? animation.tweens.length : 0; |
6587 length = gotoEnd ? animation.tweens.length : 0; |
6560 if ( stopped ) { |
6588 if ( stopped ) { |
6561 return this; |
6589 return this; |
6562 } |
6590 } |
6563 stopped = true; |
6591 stopped = true; |
6564 for ( ; index < length ; index++ ) { |
6592 for ( ; index < length ; index++ ) { |
6565 animation.tweens[ index ].run( 1 ); |
6593 animation.tweens[ index ].run( 1 ); |
6566 } |
6594 } |
6567 |
6595 |
6568 // resolve when we played the last frame |
6596 // Resolve when we played the last frame; otherwise, reject |
6569 // otherwise, reject |
|
6570 if ( gotoEnd ) { |
6597 if ( gotoEnd ) { |
6571 deferred.resolveWith( elem, [ animation, gotoEnd ] ); |
6598 deferred.resolveWith( elem, [ animation, gotoEnd ] ); |
6572 } else { |
6599 } else { |
6573 deferred.rejectWith( elem, [ animation, gotoEnd ] ); |
6600 deferred.rejectWith( elem, [ animation, gotoEnd ] ); |
6574 } |
6601 } |
6646 }; |
6673 }; |
6647 |
6674 |
6648 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : |
6675 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; |
6676 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; |
6650 |
6677 |
6651 // normalize opt.queue - true/undefined/null -> "fx" |
6678 // Normalize opt.queue - true/undefined/null -> "fx" |
6652 if ( opt.queue == null || opt.queue === true ) { |
6679 if ( opt.queue == null || opt.queue === true ) { |
6653 opt.queue = "fx"; |
6680 opt.queue = "fx"; |
6654 } |
6681 } |
6655 |
6682 |
6656 // Queueing |
6683 // Queueing |
6670 }; |
6697 }; |
6671 |
6698 |
6672 jQuery.fn.extend({ |
6699 jQuery.fn.extend({ |
6673 fadeTo: function( speed, to, easing, callback ) { |
6700 fadeTo: function( speed, to, easing, callback ) { |
6674 |
6701 |
6675 // show any hidden elements after setting opacity to 0 |
6702 // Show any hidden elements after setting opacity to 0 |
6676 return this.filter( isHidden ).css( "opacity", 0 ).show() |
6703 return this.filter( isHidden ).css( "opacity", 0 ).show() |
6677 |
6704 |
6678 // animate to the value specified |
6705 // Animate to the value specified |
6679 .end().animate({ opacity: to }, speed, easing, callback ); |
6706 .end().animate({ opacity: to }, speed, easing, callback ); |
6680 }, |
6707 }, |
6681 animate: function( prop, speed, easing, callback ) { |
6708 animate: function( prop, speed, easing, callback ) { |
6682 var empty = jQuery.isEmptyObject( prop ), |
6709 var empty = jQuery.isEmptyObject( prop ), |
6683 optall = jQuery.speed( speed, easing, callback ), |
6710 optall = jQuery.speed( speed, easing, callback ), |
6736 dequeue = false; |
6763 dequeue = false; |
6737 timers.splice( index, 1 ); |
6764 timers.splice( index, 1 ); |
6738 } |
6765 } |
6739 } |
6766 } |
6740 |
6767 |
6741 // start the next in the queue if the last step wasn't forced |
6768 // Start the next in the queue if the last step wasn't forced. |
6742 // timers currently will call their complete callbacks, which will dequeue |
6769 // Timers currently will call their complete callbacks, which |
6743 // but only if they were gotoEnd |
6770 // will dequeue but only if they were gotoEnd. |
6744 if ( dequeue || !gotoEnd ) { |
6771 if ( dequeue || !gotoEnd ) { |
6745 jQuery.dequeue( this, type ); |
6772 jQuery.dequeue( this, type ); |
6746 } |
6773 } |
6747 }); |
6774 }); |
6748 }, |
6775 }, |
6756 queue = data[ type + "queue" ], |
6783 queue = data[ type + "queue" ], |
6757 hooks = data[ type + "queueHooks" ], |
6784 hooks = data[ type + "queueHooks" ], |
6758 timers = jQuery.timers, |
6785 timers = jQuery.timers, |
6759 length = queue ? queue.length : 0; |
6786 length = queue ? queue.length : 0; |
6760 |
6787 |
6761 // enable finishing flag on private data |
6788 // Enable finishing flag on private data |
6762 data.finish = true; |
6789 data.finish = true; |
6763 |
6790 |
6764 // empty the queue first |
6791 // Empty the queue first |
6765 jQuery.queue( this, type, [] ); |
6792 jQuery.queue( this, type, [] ); |
6766 |
6793 |
6767 if ( hooks && hooks.stop ) { |
6794 if ( hooks && hooks.stop ) { |
6768 hooks.stop.call( this, true ); |
6795 hooks.stop.call( this, true ); |
6769 } |
6796 } |
6770 |
6797 |
6771 // look for any active animations, and finish them |
6798 // Look for any active animations, and finish them |
6772 for ( index = timers.length; index--; ) { |
6799 for ( index = timers.length; index--; ) { |
6773 if ( timers[ index ].elem === this && timers[ index ].queue === type ) { |
6800 if ( timers[ index ].elem === this && timers[ index ].queue === type ) { |
6774 timers[ index ].anim.stop( true ); |
6801 timers[ index ].anim.stop( true ); |
6775 timers.splice( index, 1 ); |
6802 timers.splice( index, 1 ); |
6776 } |
6803 } |
6777 } |
6804 } |
6778 |
6805 |
6779 // look for any animations in the old queue and finish them |
6806 // Look for any animations in the old queue and finish them |
6780 for ( index = 0; index < length; index++ ) { |
6807 for ( index = 0; index < length; index++ ) { |
6781 if ( queue[ index ] && queue[ index ].finish ) { |
6808 if ( queue[ index ] && queue[ index ].finish ) { |
6782 queue[ index ].finish.call( this ); |
6809 queue[ index ].finish.call( this ); |
6783 } |
6810 } |
6784 } |
6811 } |
6785 |
6812 |
6786 // turn off finishing flag |
6813 // Turn off finishing flag |
6787 delete data.finish; |
6814 delete data.finish; |
6788 }); |
6815 }); |
6789 } |
6816 } |
6790 }); |
6817 }); |
6791 |
6818 |
6884 select = document.createElement( "select" ), |
6911 select = document.createElement( "select" ), |
6885 opt = select.appendChild( document.createElement( "option" ) ); |
6912 opt = select.appendChild( document.createElement( "option" ) ); |
6886 |
6913 |
6887 input.type = "checkbox"; |
6914 input.type = "checkbox"; |
6888 |
6915 |
6889 // Support: iOS 5.1, Android 4.x, Android 2.3 |
6916 // Support: iOS<=5.1, Android<=4.2+ |
6890 // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) |
6917 // Default value for a checkbox should be "on" |
6891 support.checkOn = input.value !== ""; |
6918 support.checkOn = input.value !== ""; |
6892 |
6919 |
6893 // Must access the parent to make an option select properly |
6920 // Support: IE<=11+ |
6894 // Support: IE9, IE10 |
6921 // Must access selectedIndex to make default options select |
6895 support.optSelected = opt.selected; |
6922 support.optSelected = opt.selected; |
6896 |
6923 |
6897 // Make sure that the options inside disabled selects aren't marked as disabled |
6924 // Support: Android<=2.3 |
6898 // (WebKit marks them as disabled) |
6925 // Options inside disabled selects are incorrectly marked as disabled |
6899 select.disabled = true; |
6926 select.disabled = true; |
6900 support.optDisabled = !opt.disabled; |
6927 support.optDisabled = !opt.disabled; |
6901 |
6928 |
6902 // Check if an input maintains its value after becoming a radio |
6929 // Support: IE<=11+ |
6903 // Support: IE9, IE10 |
6930 // An input loses its value after becoming a radio |
6904 input = document.createElement( "input" ); |
6931 input = document.createElement( "input" ); |
6905 input.value = "t"; |
6932 input.value = "t"; |
6906 input.type = "radio"; |
6933 input.type = "radio"; |
6907 support.radioValue = input.value === "t"; |
6934 support.radioValue = input.value === "t"; |
6908 })(); |
6935 })(); |
6995 attrHooks: { |
7022 attrHooks: { |
6996 type: { |
7023 type: { |
6997 set: function( elem, value ) { |
7024 set: function( elem, value ) { |
6998 if ( !support.radioValue && value === "radio" && |
7025 if ( !support.radioValue && value === "radio" && |
6999 jQuery.nodeName( elem, "input" ) ) { |
7026 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; |
7027 var val = elem.value; |
7003 elem.setAttribute( "type", value ); |
7028 elem.setAttribute( "type", value ); |
7004 if ( val ) { |
7029 if ( val ) { |
7005 elem.value = val; |
7030 elem.value = val; |
7006 } |
7031 } |
7066 |
7091 |
7067 prop: function( elem, name, value ) { |
7092 prop: function( elem, name, value ) { |
7068 var ret, hooks, notxml, |
7093 var ret, hooks, notxml, |
7069 nType = elem.nodeType; |
7094 nType = elem.nodeType; |
7070 |
7095 |
7071 // don't get/set properties on text, comment and attribute nodes |
7096 // Don't get/set properties on text, comment and attribute nodes |
7072 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { |
7097 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { |
7073 return; |
7098 return; |
7074 } |
7099 } |
7075 |
7100 |
7076 notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); |
7101 notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); |
7102 } |
7127 } |
7103 } |
7128 } |
7104 } |
7129 } |
7105 }); |
7130 }); |
7106 |
7131 |
7107 // Support: IE9+ |
|
7108 // Selectedness for an option in an optgroup can be inaccurate |
|
7109 if ( !support.optSelected ) { |
7132 if ( !support.optSelected ) { |
7110 jQuery.propHooks.selected = { |
7133 jQuery.propHooks.selected = { |
7111 get: function( elem ) { |
7134 get: function( elem ) { |
7112 var parent = elem.parentNode; |
7135 var parent = elem.parentNode; |
7113 if ( parent && parent.parentNode ) { |
7136 if ( parent && parent.parentNode ) { |
7211 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { |
7234 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { |
7212 cur = cur.replace( " " + clazz + " ", " " ); |
7235 cur = cur.replace( " " + clazz + " ", " " ); |
7213 } |
7236 } |
7214 } |
7237 } |
7215 |
7238 |
7216 // only assign if different to avoid unneeded rendering. |
7239 // Only assign if different to avoid unneeded rendering. |
7217 finalValue = value ? jQuery.trim( cur ) : ""; |
7240 finalValue = value ? jQuery.trim( cur ) : ""; |
7218 if ( elem.className !== finalValue ) { |
7241 if ( elem.className !== finalValue ) { |
7219 elem.className = finalValue; |
7242 elem.className = finalValue; |
7220 } |
7243 } |
7221 } |
7244 } |
7238 }); |
7261 }); |
7239 } |
7262 } |
7240 |
7263 |
7241 return this.each(function() { |
7264 return this.each(function() { |
7242 if ( type === "string" ) { |
7265 if ( type === "string" ) { |
7243 // toggle individual class names |
7266 // Toggle individual class names |
7244 var className, |
7267 var className, |
7245 i = 0, |
7268 i = 0, |
7246 self = jQuery( this ), |
7269 self = jQuery( this ), |
7247 classNames = value.match( rnotwhite ) || []; |
7270 classNames = value.match( rnotwhite ) || []; |
7248 |
7271 |
7249 while ( (className = classNames[ i++ ]) ) { |
7272 while ( (className = classNames[ i++ ]) ) { |
7250 // check each className given, space separated list |
7273 // Check each className given, space separated list |
7251 if ( self.hasClass( className ) ) { |
7274 if ( self.hasClass( className ) ) { |
7252 self.removeClass( className ); |
7275 self.removeClass( className ); |
7253 } else { |
7276 } else { |
7254 self.addClass( className ); |
7277 self.addClass( className ); |
7255 } |
7278 } |
7260 if ( this.className ) { |
7283 if ( this.className ) { |
7261 // store className if set |
7284 // store className if set |
7262 data_priv.set( this, "__className__", this.className ); |
7285 data_priv.set( this, "__className__", this.className ); |
7263 } |
7286 } |
7264 |
7287 |
7265 // If the element has a class name or if we're passed "false", |
7288 // 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). |
7289 // then remove the whole classname (if there was one, the above saved it). |
7267 // Otherwise bring back whatever was previously saved (if anything), |
7290 // Otherwise bring back whatever was previously saved (if anything), |
7268 // falling back to the empty string if nothing was stored. |
7291 // falling back to the empty string if nothing was stored. |
7269 this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; |
7292 this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; |
7270 } |
7293 } |
7304 } |
7327 } |
7305 |
7328 |
7306 ret = elem.value; |
7329 ret = elem.value; |
7307 |
7330 |
7308 return typeof ret === "string" ? |
7331 return typeof ret === "string" ? |
7309 // handle most common string cases |
7332 // Handle most common string cases |
7310 ret.replace(rreturn, "") : |
7333 ret.replace(rreturn, "") : |
7311 // handle cases where value is null/undef or number |
7334 // Handle cases where value is null/undef or number |
7312 ret == null ? "" : ret; |
7335 ret == null ? "" : ret; |
7313 } |
7336 } |
7314 |
7337 |
7315 return; |
7338 return; |
7316 } |
7339 } |
7414 if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) { |
7437 if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) { |
7415 optionSet = true; |
7438 optionSet = true; |
7416 } |
7439 } |
7417 } |
7440 } |
7418 |
7441 |
7419 // force browsers to behave consistently when non-matching value is set |
7442 // Force browsers to behave consistently when non-matching value is set |
7420 if ( !optionSet ) { |
7443 if ( !optionSet ) { |
7421 elem.selectedIndex = -1; |
7444 elem.selectedIndex = -1; |
7422 } |
7445 } |
7423 return values; |
7446 return values; |
7424 } |
7447 } |
7435 } |
7458 } |
7436 } |
7459 } |
7437 }; |
7460 }; |
7438 if ( !support.checkOn ) { |
7461 if ( !support.checkOn ) { |
7439 jQuery.valHooks[ this ].get = function( elem ) { |
7462 jQuery.valHooks[ this ].get = function( elem ) { |
7440 // Support: Webkit |
|
7441 // "" is returned instead of "on" if a value isn't specified |
|
7442 return elem.getAttribute("value") === null ? "on" : elem.value; |
7463 return elem.getAttribute("value") === null ? "on" : elem.value; |
7443 }; |
7464 }; |
7444 } |
7465 } |
7445 }); |
7466 }); |
7446 |
7467 |
7518 return xml; |
7539 return xml; |
7519 }; |
7540 }; |
7520 |
7541 |
7521 |
7542 |
7522 var |
7543 var |
7523 // Document location |
|
7524 ajaxLocParts, |
|
7525 ajaxLocation, |
|
7526 |
|
7527 rhash = /#.*$/, |
7544 rhash = /#.*$/, |
7528 rts = /([?&])_=[^&]*/, |
7545 rts = /([?&])_=[^&]*/, |
7529 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, |
7546 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, |
7530 // #7653, #8125, #8152: local protocol detection |
7547 // #7653, #8125, #8152: local protocol detection |
7531 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, |
7548 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, |
7550 * 3) selection will start with transport dataType and THEN go to "*" if needed |
7567 * 3) selection will start with transport dataType and THEN go to "*" if needed |
7551 */ |
7568 */ |
7552 transports = {}, |
7569 transports = {}, |
7553 |
7570 |
7554 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression |
7571 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression |
7555 allTypes = "*/".concat("*"); |
7572 allTypes = "*/".concat( "*" ), |
7556 |
7573 |
7557 // #8138, IE may throw an exception when accessing |
7574 // Document location |
7558 // a field from window.location if document.domain has been set |
7575 ajaxLocation = window.location.href, |
7559 try { |
7576 |
7560 ajaxLocation = location.href; |
7577 // Segment location into parts |
7561 } catch( e ) { |
7578 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; |
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; |
|
7567 } |
|
7568 |
|
7569 // Segment location into parts |
|
7570 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; |
|
7571 |
7579 |
7572 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport |
7580 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport |
7573 function addToPrefiltersOrTransports( structure ) { |
7581 function addToPrefiltersOrTransports( structure ) { |
7574 |
7582 |
7575 // dataTypeExpression is optional and defaults to "*" |
7583 // dataTypeExpression is optional and defaults to "*" |
8044 if ( state === 2 ) { |
8052 if ( state === 2 ) { |
8045 return jqXHR; |
8053 return jqXHR; |
8046 } |
8054 } |
8047 |
8055 |
8048 // We can fire global events as of now if asked to |
8056 // We can fire global events as of now if asked to |
8049 fireGlobals = s.global; |
8057 // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) |
8058 fireGlobals = jQuery.event && s.global; |
|
8050 |
8059 |
8051 // Watch for a new set of requests |
8060 // Watch for a new set of requests |
8052 if ( fireGlobals && jQuery.active++ === 0 ) { |
8061 if ( fireGlobals && jQuery.active++ === 0 ) { |
8053 jQuery.event.trigger("ajaxStart"); |
8062 jQuery.event.trigger("ajaxStart"); |
8054 } |
8063 } |
8117 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { |
8126 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { |
8118 // Abort if not done already and return |
8127 // Abort if not done already and return |
8119 return jqXHR.abort(); |
8128 return jqXHR.abort(); |
8120 } |
8129 } |
8121 |
8130 |
8122 // aborting is no longer a cancellation |
8131 // Aborting is no longer a cancellation |
8123 strAbort = "abort"; |
8132 strAbort = "abort"; |
8124 |
8133 |
8125 // Install callbacks on deferreds |
8134 // Install callbacks on deferreds |
8126 for ( i in { success: 1, error: 1, complete: 1 } ) { |
8135 for ( i in { success: 1, error: 1, complete: 1 } ) { |
8127 jqXHR[ i ]( s[ i ] ); |
8136 jqXHR[ i ]( s[ i ] ); |
8229 success = response.data; |
8238 success = response.data; |
8230 error = response.error; |
8239 error = response.error; |
8231 isSuccess = !error; |
8240 isSuccess = !error; |
8232 } |
8241 } |
8233 } else { |
8242 } else { |
8234 // We extract error from statusText |
8243 // Extract error from statusText and normalize for non-aborts |
8235 // then normalize statusText and status for non-aborts |
|
8236 error = statusText; |
8244 error = statusText; |
8237 if ( status || !statusText ) { |
8245 if ( status || !statusText ) { |
8238 statusText = "error"; |
8246 statusText = "error"; |
8239 if ( status < 0 ) { |
8247 if ( status < 0 ) { |
8240 status = 0; |
8248 status = 0; |
8286 } |
8294 } |
8287 }); |
8295 }); |
8288 |
8296 |
8289 jQuery.each( [ "get", "post" ], function( i, method ) { |
8297 jQuery.each( [ "get", "post" ], function( i, method ) { |
8290 jQuery[ method ] = function( url, data, callback, type ) { |
8298 jQuery[ method ] = function( url, data, callback, type ) { |
8291 // shift arguments if data argument was omitted |
8299 // Shift arguments if data argument was omitted |
8292 if ( jQuery.isFunction( data ) ) { |
8300 if ( jQuery.isFunction( data ) ) { |
8293 type = type || callback; |
8301 type = type || callback; |
8294 callback = data; |
8302 callback = data; |
8295 data = undefined; |
8303 data = undefined; |
8296 } |
8304 } |
8300 type: method, |
8308 type: method, |
8301 dataType: type, |
8309 dataType: type, |
8302 data: data, |
8310 data: data, |
8303 success: callback |
8311 success: callback |
8304 }); |
8312 }); |
8305 }; |
|
8306 }); |
|
8307 |
|
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 ); |
|
8312 }; |
8313 }; |
8313 }); |
8314 }); |
8314 |
8315 |
8315 |
8316 |
8316 jQuery._evalUrl = function( url ) { |
8317 jQuery._evalUrl = function( url ) { |
8527 }, |
8528 }, |
8528 xhrSupported = jQuery.ajaxSettings.xhr(); |
8529 xhrSupported = jQuery.ajaxSettings.xhr(); |
8529 |
8530 |
8530 // Support: IE9 |
8531 // Support: IE9 |
8531 // Open requests must be manually aborted on unload (#5280) |
8532 // Open requests must be manually aborted on unload (#5280) |
8532 if ( window.ActiveXObject ) { |
8533 // See https://support.microsoft.com/kb/2856746 for more info |
8533 jQuery( window ).on( "unload", function() { |
8534 if ( window.attachEvent ) { |
8535 window.attachEvent( "onunload", function() { |
|
8534 for ( var key in xhrCallbacks ) { |
8536 for ( var key in xhrCallbacks ) { |
8535 xhrCallbacks[ key ](); |
8537 xhrCallbacks[ key ](); |
8536 } |
8538 } |
8537 }); |
8539 }); |
8538 } |
8540 } |
8881 }; |
8883 }; |
8882 |
8884 |
8883 |
8885 |
8884 |
8886 |
8885 |
8887 |
8888 // Attach a bunch of functions for handling common AJAX events |
|
8889 jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) { |
|
8890 jQuery.fn[ type ] = function( fn ) { |
|
8891 return this.on( type, fn ); |
|
8892 }; |
|
8893 }); |
|
8894 |
|
8895 |
|
8896 |
|
8897 |
|
8886 jQuery.expr.filters.animated = function( elem ) { |
8898 jQuery.expr.filters.animated = function( elem ) { |
8887 return jQuery.grep(jQuery.timers, function( fn ) { |
8899 return jQuery.grep(jQuery.timers, function( fn ) { |
8888 return elem === fn.elem; |
8900 return elem === fn.elem; |
8889 }).length; |
8901 }).length; |
8890 }; |
8902 }; |
8917 curCSSTop = jQuery.css( elem, "top" ); |
8929 curCSSTop = jQuery.css( elem, "top" ); |
8918 curCSSLeft = jQuery.css( elem, "left" ); |
8930 curCSSLeft = jQuery.css( elem, "left" ); |
8919 calculatePosition = ( position === "absolute" || position === "fixed" ) && |
8931 calculatePosition = ( position === "absolute" || position === "fixed" ) && |
8920 ( curCSSTop + curCSSLeft ).indexOf("auto") > -1; |
8932 ( curCSSTop + curCSSLeft ).indexOf("auto") > -1; |
8921 |
8933 |
8922 // Need to be able to calculate position if either top or left is auto and position is either absolute or fixed |
8934 // Need to be able to calculate position if either |
8935 // top or left is auto and position is either absolute or fixed |
|
8923 if ( calculatePosition ) { |
8936 if ( calculatePosition ) { |
8924 curPosition = curElem.position(); |
8937 curPosition = curElem.position(); |
8925 curTop = curPosition.top; |
8938 curTop = curPosition.top; |
8926 curLeft = curPosition.left; |
8939 curLeft = curPosition.left; |
8927 |
8940 |
8974 // Make sure it's not a disconnected DOM node |
8987 // Make sure it's not a disconnected DOM node |
8975 if ( !jQuery.contains( docElem, elem ) ) { |
8988 if ( !jQuery.contains( docElem, elem ) ) { |
8976 return box; |
8989 return box; |
8977 } |
8990 } |
8978 |
8991 |
8992 // Support: BlackBerry 5, iOS 3 (original iPhone) |
|
8979 // If we don't have gBCR, just use 0,0 rather than error |
8993 // 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 ) { |
8994 if ( typeof elem.getBoundingClientRect !== strundefined ) { |
8982 box = elem.getBoundingClientRect(); |
8995 box = elem.getBoundingClientRect(); |
8983 } |
8996 } |
8984 win = getWindow( doc ); |
8997 win = getWindow( doc ); |
8985 return { |
8998 return { |
8997 elem = this[ 0 ], |
9010 elem = this[ 0 ], |
8998 parentOffset = { top: 0, left: 0 }; |
9011 parentOffset = { top: 0, left: 0 }; |
8999 |
9012 |
9000 // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent |
9013 // 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" ) { |
9014 if ( jQuery.css( elem, "position" ) === "fixed" ) { |
9002 // We assume that getBoundingClientRect is available when computed position is fixed |
9015 // Assume getBoundingClientRect is there when computed position is fixed |
9003 offset = elem.getBoundingClientRect(); |
9016 offset = elem.getBoundingClientRect(); |
9004 |
9017 |
9005 } else { |
9018 } else { |
9006 // Get *real* offsetParent |
9019 // Get *real* offsetParent |
9007 offsetParent = this.offsetParent(); |
9020 offsetParent = this.offsetParent(); |
9060 } |
9073 } |
9061 }, method, val, arguments.length, null ); |
9074 }, method, val, arguments.length, null ); |
9062 }; |
9075 }; |
9063 }); |
9076 }); |
9064 |
9077 |
9078 // Support: Safari<7+, Chrome<37+ |
|
9065 // Add the top/left cssHooks using jQuery.fn.position |
9079 // Add the top/left cssHooks using jQuery.fn.position |
9066 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 |
9080 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 |
9067 // getComputedStyle returns percent when specified for top/left/bottom/right |
9081 // Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 |
9068 // rather than make the css module depend on the offset module, we just check for it here |
9082 // getComputedStyle returns percent when specified for top/left/bottom/right; |
9083 // rather than make the css module depend on the offset module, just check for it here |
|
9069 jQuery.each( [ "top", "left" ], function( i, prop ) { |
9084 jQuery.each( [ "top", "left" ], function( i, prop ) { |
9070 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, |
9085 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, |
9071 function( elem, computed ) { |
9086 function( elem, computed ) { |
9072 if ( computed ) { |
9087 if ( computed ) { |
9073 computed = curCSS( elem, prop ); |
9088 computed = curCSS( elem, prop ); |
9074 // if curCSS returns percentage, fallback to offset |
9089 // If curCSS returns percentage, fallback to offset |
9075 return rnumnonpx.test( computed ) ? |
9090 return rnumnonpx.test( computed ) ? |
9076 jQuery( elem ).position()[ prop ] + "px" : |
9091 jQuery( elem ).position()[ prop ] + "px" : |
9077 computed; |
9092 computed; |
9078 } |
9093 } |
9079 } |
9094 } |
9082 |
9097 |
9083 |
9098 |
9084 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods |
9099 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods |
9085 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { |
9100 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { |
9086 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { |
9101 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { |
9087 // margin is only for outerHeight, outerWidth |
9102 // Margin is only for outerHeight, outerWidth |
9088 jQuery.fn[ funcName ] = function( margin, value ) { |
9103 jQuery.fn[ funcName ] = function( margin, value ) { |
9089 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), |
9104 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), |
9090 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); |
9105 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); |
9091 |
9106 |
9092 return access( this, function( elem, type, value ) { |
9107 return access( this, function( elem, type, value ) { |
9173 } |
9188 } |
9174 |
9189 |
9175 return jQuery; |
9190 return jQuery; |
9176 }; |
9191 }; |
9177 |
9192 |
9178 // Expose jQuery and $ identifiers, even in |
9193 // Expose jQuery and $ identifiers, even in AMD |
9179 // AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557) |
9194 // (#7102#comment:10, https://github.com/jquery/jquery/pull/557) |
9180 // and CommonJS for browser emulators (#13566) |
9195 // and CommonJS for browser emulators (#13566) |
9181 if ( typeof noGlobal === strundefined ) { |
9196 if ( typeof noGlobal === strundefined ) { |
9182 window.jQuery = window.$ = jQuery; |
9197 window.jQuery = window.$ = jQuery; |
9183 } |
9198 } |
9184 |
9199 |
9188 return jQuery; |
9203 return jQuery; |
9189 |
9204 |
9190 })); |
9205 })); |
9191 |
9206 |
9192 /** |
9207 /** |
9193 * @license AngularJS v1.3.0-rc.5 |
9208 * @license AngularJS v1.3.8 |
9194 * (c) 2010-2014 Google, Inc. http://angularjs.org |
9209 * (c) 2010-2014 Google, Inc. http://angularjs.org |
9195 * License: MIT |
9210 * License: MIT |
9196 */ |
9211 */ |
9197 (function(window, document, undefined) {'use strict'; |
9212 (function(window, document, undefined) {'use strict'; |
9198 |
9213 |
9226 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance |
9241 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance |
9227 */ |
9242 */ |
9228 |
9243 |
9229 function minErr(module, ErrorConstructor) { |
9244 function minErr(module, ErrorConstructor) { |
9230 ErrorConstructor = ErrorConstructor || Error; |
9245 ErrorConstructor = ErrorConstructor || Error; |
9231 return function () { |
9246 return function() { |
9232 var code = arguments[0], |
9247 var code = arguments[0], |
9233 prefix = '[' + (module ? module + ':' : '') + code + '] ', |
9248 prefix = '[' + (module ? module + ':' : '') + code + '] ', |
9234 template = arguments[1], |
9249 template = arguments[1], |
9235 templateArgs = arguments, |
9250 templateArgs = arguments, |
9236 stringify = function (obj) { |
9251 |
9237 if (typeof obj === 'function') { |
|
9238 return obj.toString().replace(/ \{[\s\S]*$/, ''); |
|
9239 } else if (typeof obj === 'undefined') { |
|
9240 return 'undefined'; |
|
9241 } else if (typeof obj !== 'string') { |
|
9242 return JSON.stringify(obj); |
|
9243 } |
|
9244 return obj; |
|
9245 }, |
|
9246 message, i; |
9252 message, i; |
9247 |
9253 |
9248 message = prefix + template.replace(/\{\d+\}/g, function (match) { |
9254 message = prefix + template.replace(/\{\d+\}/g, function(match) { |
9249 var index = +match.slice(1, -1), arg; |
9255 var index = +match.slice(1, -1), arg; |
9250 |
9256 |
9251 if (index + 2 < templateArgs.length) { |
9257 if (index + 2 < templateArgs.length) { |
9252 arg = templateArgs[index + 2]; |
9258 return toDebugString(templateArgs[index + 2]); |
9253 if (typeof arg === 'function') { |
|
9254 return arg.toString().replace(/ ?\{[\s\S]*$/, ''); |
|
9255 } else if (typeof arg === 'undefined') { |
|
9256 return 'undefined'; |
|
9257 } else if (typeof arg !== 'string') { |
|
9258 return toJson(arg); |
|
9259 } |
|
9260 return arg; |
|
9261 } |
9259 } |
9262 return match; |
9260 return match; |
9263 }); |
9261 }); |
9264 |
9262 |
9265 message = message + '\nhttp://errors.angularjs.org/1.3.0-rc.5/' + |
9263 message = message + '\nhttp://errors.angularjs.org/1.3.8/' + |
9266 (module ? module + '/' : '') + code; |
9264 (module ? module + '/' : '') + code; |
9267 for (i = 2; i < arguments.length; i++) { |
9265 for (i = 2; i < arguments.length; i++) { |
9268 message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + |
9266 message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + |
9269 encodeURIComponent(stringify(arguments[i])); |
9267 encodeURIComponent(toDebugString(arguments[i])); |
9270 } |
9268 } |
9271 return new ErrorConstructor(message); |
9269 return new ErrorConstructor(message); |
9272 }; |
9270 }; |
9273 } |
9271 } |
9274 |
9272 |
9315 isFunction: true, |
9313 isFunction: true, |
9316 isRegExp: true, |
9314 isRegExp: true, |
9317 isWindow: true, |
9315 isWindow: true, |
9318 isScope: true, |
9316 isScope: true, |
9319 isFile: true, |
9317 isFile: true, |
9318 isFormData: true, |
|
9320 isBlob: true, |
9319 isBlob: true, |
9321 isBoolean: true, |
9320 isBoolean: true, |
9322 isPromiseLike: true, |
9321 isPromiseLike: true, |
9323 trim: true, |
9322 trim: true, |
9323 escapeForRegexp: true, |
|
9324 isElement: true, |
9324 isElement: true, |
9325 makeMap: true, |
9325 makeMap: true, |
9326 size: true, |
|
9327 includes: true, |
9326 includes: true, |
9328 arrayRemove: true, |
9327 arrayRemove: true, |
9329 isLeafNode: true, |
|
9330 copy: true, |
9328 copy: true, |
9331 shallowCopy: true, |
9329 shallowCopy: true, |
9332 equals: true, |
9330 equals: true, |
9333 csp: true, |
9331 csp: true, |
9334 concat: true, |
9332 concat: true, |
9394 * |
9392 * |
9395 * @description Converts the specified string to lowercase. |
9393 * @description Converts the specified string to lowercase. |
9396 * @param {string} string String to be converted to lowercase. |
9394 * @param {string} string String to be converted to lowercase. |
9397 * @returns {string} Lowercased string. |
9395 * @returns {string} Lowercased string. |
9398 */ |
9396 */ |
9399 var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; |
9397 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; |
9400 var hasOwnProperty = Object.prototype.hasOwnProperty; |
9398 var hasOwnProperty = Object.prototype.hasOwnProperty; |
9401 |
9399 |
9402 /** |
9400 /** |
9403 * @ngdoc function |
9401 * @ngdoc function |
9404 * @name angular.uppercase |
9402 * @name angular.uppercase |
9407 * |
9405 * |
9408 * @description Converts the specified string to uppercase. |
9406 * @description Converts the specified string to uppercase. |
9409 * @param {string} string String to be converted to uppercase. |
9407 * @param {string} string String to be converted to uppercase. |
9410 * @returns {string} Uppercased string. |
9408 * @returns {string} Uppercased string. |
9411 */ |
9409 */ |
9412 var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; |
9410 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; |
9413 |
9411 |
9414 |
9412 |
9415 var manualLowercase = function(s) { |
9413 var manualLowercase = function(s) { |
9416 /* jshint bitwise: false */ |
9414 /* jshint bitwise: false */ |
9417 return isString(s) |
9415 return isString(s) |
9433 lowercase = manualLowercase; |
9431 lowercase = manualLowercase; |
9434 uppercase = manualUppercase; |
9432 uppercase = manualUppercase; |
9435 } |
9433 } |
9436 |
9434 |
9437 |
9435 |
9438 var /** holds major version number for IE or NaN for real browsers */ |
9436 var |
9439 msie, |
9437 msie, // holds major version number for IE, or NaN if UA is not IE. |
9440 jqLite, // delay binding since jQuery could be loaded after us. |
9438 jqLite, // delay binding since jQuery could be loaded after us. |
9441 jQuery, // delay binding |
9439 jQuery, // delay binding |
9442 slice = [].slice, |
9440 slice = [].slice, |
9443 splice = [].splice, |
9441 splice = [].splice, |
9444 push = [].push, |
9442 push = [].push, |
9490 * is the value of an object property or an array element, `key` is the object property key or |
9488 * is the value of an object property or an array element, `key` is the object property key or |
9491 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. |
9489 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. |
9492 * |
9490 * |
9493 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters |
9491 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters |
9494 * using the `hasOwnProperty` method. |
9492 * using the `hasOwnProperty` method. |
9493 * |
|
9494 * Unlike ES262's |
|
9495 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), |
|
9496 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just |
|
9497 * return the value provided. |
|
9495 * |
9498 * |
9496 ```js |
9499 ```js |
9497 var values = {name: 'misko', gender: 'male'}; |
9500 var values = {name: 'misko', gender: 'male'}; |
9498 var log = []; |
9501 var log = []; |
9499 angular.forEach(values, function(value, key) { |
9502 angular.forEach(values, function(value, key) { |
9538 } |
9541 } |
9539 return obj; |
9542 return obj; |
9540 } |
9543 } |
9541 |
9544 |
9542 function sortedKeys(obj) { |
9545 function sortedKeys(obj) { |
9543 var keys = []; |
9546 return Object.keys(obj).sort(); |
9544 for (var key in obj) { |
|
9545 if (obj.hasOwnProperty(key)) { |
|
9546 keys.push(key); |
|
9547 } |
|
9548 } |
|
9549 return keys.sort(); |
|
9550 } |
9547 } |
9551 |
9548 |
9552 function forEachSorted(obj, iterator, context) { |
9549 function forEachSorted(obj, iterator, context) { |
9553 var keys = sortedKeys(obj); |
9550 var keys = sortedKeys(obj); |
9554 for ( var i = 0; i < keys.length; i++) { |
9551 for (var i = 0; i < keys.length; i++) { |
9555 iterator.call(context, obj[keys[i]], keys[i]); |
9552 iterator.call(context, obj[keys[i]], keys[i]); |
9556 } |
9553 } |
9557 return keys; |
9554 return keys; |
9558 } |
9555 } |
9559 |
9556 |
9602 * @module ng |
9599 * @module ng |
9603 * @kind function |
9600 * @kind function |
9604 * |
9601 * |
9605 * @description |
9602 * @description |
9606 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) |
9603 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) |
9607 * to `dst`. You can specify multiple `src` objects. |
9604 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so |
9605 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. |
|
9606 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy). |
|
9608 * |
9607 * |
9609 * @param {Object} dst Destination object. |
9608 * @param {Object} dst Destination object. |
9610 * @param {...Object} src Source object(s). |
9609 * @param {...Object} src Source object(s). |
9611 * @returns {Object} Reference to `dst`. |
9610 * @returns {Object} Reference to `dst`. |
9612 */ |
9611 */ |
9632 return parseInt(str, 10); |
9631 return parseInt(str, 10); |
9633 } |
9632 } |
9634 |
9633 |
9635 |
9634 |
9636 function inherit(parent, extra) { |
9635 function inherit(parent, extra) { |
9637 return extend(new (extend(function() {}, {prototype:parent}))(), extra); |
9636 return extend(Object.create(parent), extra); |
9638 } |
9637 } |
9639 |
9638 |
9640 /** |
9639 /** |
9641 * @ngdoc function |
9640 * @ngdoc function |
9642 * @name angular.noop |
9641 * @name angular.noop |
9670 ```js |
9669 ```js |
9671 function transformer(transformationFn, value) { |
9670 function transformer(transformationFn, value) { |
9672 return (transformationFn || angular.identity)(value); |
9671 return (transformationFn || angular.identity)(value); |
9673 }; |
9672 }; |
9674 ``` |
9673 ``` |
9674 * @param {*} value to be returned. |
|
9675 * @returns {*} the value passed in. |
|
9675 */ |
9676 */ |
9676 function identity($) {return $;} |
9677 function identity($) {return $;} |
9677 identity.$inject = []; |
9678 identity.$inject = []; |
9678 |
9679 |
9679 |
9680 |
9689 * Determines if a reference is undefined. |
9690 * Determines if a reference is undefined. |
9690 * |
9691 * |
9691 * @param {*} value Reference to check. |
9692 * @param {*} value Reference to check. |
9692 * @returns {boolean} True if `value` is undefined. |
9693 * @returns {boolean} True if `value` is undefined. |
9693 */ |
9694 */ |
9694 function isUndefined(value){return typeof value === 'undefined';} |
9695 function isUndefined(value) {return typeof value === 'undefined';} |
9695 |
9696 |
9696 |
9697 |
9697 /** |
9698 /** |
9698 * @ngdoc function |
9699 * @ngdoc function |
9699 * @name angular.isDefined |
9700 * @name angular.isDefined |
9704 * Determines if a reference is defined. |
9705 * Determines if a reference is defined. |
9705 * |
9706 * |
9706 * @param {*} value Reference to check. |
9707 * @param {*} value Reference to check. |
9707 * @returns {boolean} True if `value` is defined. |
9708 * @returns {boolean} True if `value` is defined. |
9708 */ |
9709 */ |
9709 function isDefined(value){return typeof value !== 'undefined';} |
9710 function isDefined(value) {return typeof value !== 'undefined';} |
9710 |
9711 |
9711 |
9712 |
9712 /** |
9713 /** |
9713 * @ngdoc function |
9714 * @ngdoc function |
9714 * @name angular.isObject |
9715 * @name angular.isObject |
9720 * considered to be objects. Note that JavaScript arrays are objects. |
9721 * considered to be objects. Note that JavaScript arrays are objects. |
9721 * |
9722 * |
9722 * @param {*} value Reference to check. |
9723 * @param {*} value Reference to check. |
9723 * @returns {boolean} True if `value` is an `Object` but not `null`. |
9724 * @returns {boolean} True if `value` is an `Object` but not `null`. |
9724 */ |
9725 */ |
9725 function isObject(value){ |
9726 function isObject(value) { |
9726 // http://jsperf.com/isobject4 |
9727 // http://jsperf.com/isobject4 |
9727 return value !== null && typeof value === 'object'; |
9728 return value !== null && typeof value === 'object'; |
9728 } |
9729 } |
9729 |
9730 |
9730 |
9731 |
9738 * Determines if a reference is a `String`. |
9739 * Determines if a reference is a `String`. |
9739 * |
9740 * |
9740 * @param {*} value Reference to check. |
9741 * @param {*} value Reference to check. |
9741 * @returns {boolean} True if `value` is a `String`. |
9742 * @returns {boolean} True if `value` is a `String`. |
9742 */ |
9743 */ |
9743 function isString(value){return typeof value === 'string';} |
9744 function isString(value) {return typeof value === 'string';} |
9744 |
9745 |
9745 |
9746 |
9746 /** |
9747 /** |
9747 * @ngdoc function |
9748 * @ngdoc function |
9748 * @name angular.isNumber |
9749 * @name angular.isNumber |
9753 * Determines if a reference is a `Number`. |
9754 * Determines if a reference is a `Number`. |
9754 * |
9755 * |
9755 * @param {*} value Reference to check. |
9756 * @param {*} value Reference to check. |
9756 * @returns {boolean} True if `value` is a `Number`. |
9757 * @returns {boolean} True if `value` is a `Number`. |
9757 */ |
9758 */ |
9758 function isNumber(value){return typeof value === 'number';} |
9759 function isNumber(value) {return typeof value === 'number';} |
9759 |
9760 |
9760 |
9761 |
9761 /** |
9762 /** |
9762 * @ngdoc function |
9763 * @ngdoc function |
9763 * @name angular.isDate |
9764 * @name angular.isDate |
9799 * Determines if a reference is a `Function`. |
9800 * Determines if a reference is a `Function`. |
9800 * |
9801 * |
9801 * @param {*} value Reference to check. |
9802 * @param {*} value Reference to check. |
9802 * @returns {boolean} True if `value` is a `Function`. |
9803 * @returns {boolean} True if `value` is a `Function`. |
9803 */ |
9804 */ |
9804 function isFunction(value){return typeof value === 'function';} |
9805 function isFunction(value) {return typeof value === 'function';} |
9805 |
9806 |
9806 |
9807 |
9807 /** |
9808 /** |
9808 * Determines if a value is a regular expression object. |
9809 * Determines if a value is a regular expression object. |
9809 * |
9810 * |
9836 function isFile(obj) { |
9837 function isFile(obj) { |
9837 return toString.call(obj) === '[object File]'; |
9838 return toString.call(obj) === '[object File]'; |
9838 } |
9839 } |
9839 |
9840 |
9840 |
9841 |
9842 function isFormData(obj) { |
|
9843 return toString.call(obj) === '[object FormData]'; |
|
9844 } |
|
9845 |
|
9846 |
|
9841 function isBlob(obj) { |
9847 function isBlob(obj) { |
9842 return toString.call(obj) === '[object Blob]'; |
9848 return toString.call(obj) === '[object Blob]'; |
9843 } |
9849 } |
9844 |
9850 |
9845 |
9851 |
9853 } |
9859 } |
9854 |
9860 |
9855 |
9861 |
9856 var trim = function(value) { |
9862 var trim = function(value) { |
9857 return isString(value) ? value.trim() : value; |
9863 return isString(value) ? value.trim() : value; |
9864 }; |
|
9865 |
|
9866 // Copied from: |
|
9867 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 |
|
9868 // Prereq: s is a string. |
|
9869 var escapeForRegexp = function(s) { |
|
9870 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). |
|
9871 replace(/\x08/g, '\\x08'); |
|
9858 }; |
9872 }; |
9859 |
9873 |
9860 |
9874 |
9861 /** |
9875 /** |
9862 * @ngdoc function |
9876 * @ngdoc function |
9880 * @param str 'key1,key2,...' |
9894 * @param str 'key1,key2,...' |
9881 * @returns {object} in the form of {key1:true, key2:true, ...} |
9895 * @returns {object} in the form of {key1:true, key2:true, ...} |
9882 */ |
9896 */ |
9883 function makeMap(str) { |
9897 function makeMap(str) { |
9884 var obj = {}, items = str.split(","), i; |
9898 var obj = {}, items = str.split(","), i; |
9885 for ( i = 0; i < items.length; i++ ) |
9899 for (i = 0; i < items.length; i++) |
9886 obj[ items[i] ] = true; |
9900 obj[ items[i] ] = true; |
9887 return obj; |
9901 return obj; |
9888 } |
9902 } |
9889 |
9903 |
9890 |
9904 |
9891 function nodeName_(element) { |
9905 function nodeName_(element) { |
9892 return lowercase(element.nodeName || element[0].nodeName); |
9906 return lowercase(element.nodeName || (element[0] && element[0].nodeName)); |
9893 } |
9907 } |
9894 |
|
9895 |
|
9896 /** |
|
9897 * @description |
|
9898 * Determines the number of elements in an array, the number of properties an object has, or |
|
9899 * the length of a string. |
|
9900 * |
|
9901 * Note: This function is used to augment the Object type in Angular expressions. See |
|
9902 * {@link angular.Object} for more information about Angular arrays. |
|
9903 * |
|
9904 * @param {Object|Array|string} obj Object, array, or string to inspect. |
|
9905 * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object |
|
9906 * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. |
|
9907 */ |
|
9908 function size(obj, ownPropsOnly) { |
|
9909 var count = 0, key; |
|
9910 |
|
9911 if (isArray(obj) || isString(obj)) { |
|
9912 return obj.length; |
|
9913 } else if (isObject(obj)) { |
|
9914 for (key in obj) |
|
9915 if (!ownPropsOnly || obj.hasOwnProperty(key)) |
|
9916 count++; |
|
9917 } |
|
9918 |
|
9919 return count; |
|
9920 } |
|
9921 |
|
9922 |
9908 |
9923 function includes(array, obj) { |
9909 function includes(array, obj) { |
9924 return Array.prototype.indexOf.call(array, obj) != -1; |
9910 return Array.prototype.indexOf.call(array, obj) != -1; |
9925 } |
9911 } |
9926 |
9912 |
9927 function arrayRemove(array, value) { |
9913 function arrayRemove(array, value) { |
9928 var index = array.indexOf(value); |
9914 var index = array.indexOf(value); |
9929 if (index >=0) |
9915 if (index >= 0) |
9930 array.splice(index, 1); |
9916 array.splice(index, 1); |
9931 return value; |
9917 return value; |
9932 } |
|
9933 |
|
9934 function isLeafNode (node) { |
|
9935 if (node) { |
|
9936 switch (nodeName_(node)) { |
|
9937 case "option": |
|
9938 case "pre": |
|
9939 case "title": |
|
9940 return true; |
|
9941 } |
|
9942 } |
|
9943 return false; |
|
9944 } |
9918 } |
9945 |
9919 |
9946 /** |
9920 /** |
9947 * @ngdoc function |
9921 * @ngdoc function |
9948 * @name angular.copy |
9922 * @name angular.copy |
9951 * |
9925 * |
9952 * @description |
9926 * @description |
9953 * Creates a deep copy of `source`, which should be an object or an array. |
9927 * Creates a deep copy of `source`, which should be an object or an array. |
9954 * |
9928 * |
9955 * * If no destination is supplied, a copy of the object or array is created. |
9929 * * If no destination is supplied, a copy of the object or array is created. |
9956 * * If a destination is provided, all of its elements (for array) or properties (for objects) |
9930 * * If a destination is provided, all of its elements (for arrays) or properties (for objects) |
9957 * are deleted and then all elements/properties from the source are copied to it. |
9931 * are deleted and then all elements/properties from the source are copied to it. |
9958 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. |
9932 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. |
9959 * * If `source` is identical to 'destination' an exception will be thrown. |
9933 * * If `source` is identical to 'destination' an exception will be thrown. |
9960 * |
9934 * |
9961 * @param {*} source The source that will be used to make a copy. |
9935 * @param {*} source The source that will be used to make a copy. |
10038 } |
10012 } |
10039 |
10013 |
10040 var result; |
10014 var result; |
10041 if (isArray(source)) { |
10015 if (isArray(source)) { |
10042 destination.length = 0; |
10016 destination.length = 0; |
10043 for ( var i = 0; i < source.length; i++) { |
10017 for (var i = 0; i < source.length; i++) { |
10044 result = copy(source[i], null, stackSource, stackDest); |
10018 result = copy(source[i], null, stackSource, stackDest); |
10045 if (isObject(source[i])) { |
10019 if (isObject(source[i])) { |
10046 stackSource.push(source[i]); |
10020 stackSource.push(source[i]); |
10047 stackDest.push(result); |
10021 stackDest.push(result); |
10048 } |
10022 } |
10055 } else { |
10029 } else { |
10056 forEach(destination, function(value, key) { |
10030 forEach(destination, function(value, key) { |
10057 delete destination[key]; |
10031 delete destination[key]; |
10058 }); |
10032 }); |
10059 } |
10033 } |
10060 for ( var key in source) { |
10034 for (var key in source) { |
10061 if(source.hasOwnProperty(key)) { |
10035 if (source.hasOwnProperty(key)) { |
10062 result = copy(source[key], null, stackSource, stackDest); |
10036 result = copy(source[key], null, stackSource, stackDest); |
10063 if (isObject(source[key])) { |
10037 if (isObject(source[key])) { |
10064 stackSource.push(source[key]); |
10038 stackSource.push(source[key]); |
10065 stackDest.push(result); |
10039 stackDest.push(result); |
10066 } |
10040 } |
10137 if (t1 == t2) { |
10111 if (t1 == t2) { |
10138 if (t1 == 'object') { |
10112 if (t1 == 'object') { |
10139 if (isArray(o1)) { |
10113 if (isArray(o1)) { |
10140 if (!isArray(o2)) return false; |
10114 if (!isArray(o2)) return false; |
10141 if ((length = o1.length) == o2.length) { |
10115 if ((length = o1.length) == o2.length) { |
10142 for(key=0; key<length; key++) { |
10116 for (key = 0; key < length; key++) { |
10143 if (!equals(o1[key], o2[key])) return false; |
10117 if (!equals(o1[key], o2[key])) return false; |
10144 } |
10118 } |
10145 return true; |
10119 return true; |
10146 } |
10120 } |
10147 } else if (isDate(o1)) { |
10121 } else if (isDate(o1)) { |
10150 } else if (isRegExp(o1) && isRegExp(o2)) { |
10124 } else if (isRegExp(o1) && isRegExp(o2)) { |
10151 return o1.toString() == o2.toString(); |
10125 return o1.toString() == o2.toString(); |
10152 } else { |
10126 } else { |
10153 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false; |
10127 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false; |
10154 keySet = {}; |
10128 keySet = {}; |
10155 for(key in o1) { |
10129 for (key in o1) { |
10156 if (key.charAt(0) === '$' || isFunction(o1[key])) continue; |
10130 if (key.charAt(0) === '$' || isFunction(o1[key])) continue; |
10157 if (!equals(o1[key], o2[key])) return false; |
10131 if (!equals(o1[key], o2[key])) return false; |
10158 keySet[key] = true; |
10132 keySet[key] = true; |
10159 } |
10133 } |
10160 for(key in o2) { |
10134 for (key in o2) { |
10161 if (!keySet.hasOwnProperty(key) && |
10135 if (!keySet.hasOwnProperty(key) && |
10162 key.charAt(0) !== '$' && |
10136 key.charAt(0) !== '$' && |
10163 o2[key] !== undefined && |
10137 o2[key] !== undefined && |
10164 !isFunction(o2[key])) return false; |
10138 !isFunction(o2[key])) return false; |
10165 } |
10139 } |
10223 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; |
10197 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; |
10224 if (isFunction(fn) && !(fn instanceof RegExp)) { |
10198 if (isFunction(fn) && !(fn instanceof RegExp)) { |
10225 return curryArgs.length |
10199 return curryArgs.length |
10226 ? function() { |
10200 ? function() { |
10227 return arguments.length |
10201 return arguments.length |
10228 ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) |
10202 ? fn.apply(self, concat(curryArgs, arguments, 0)) |
10229 : fn.apply(self, curryArgs); |
10203 : fn.apply(self, curryArgs); |
10230 } |
10204 } |
10231 : function() { |
10205 : function() { |
10232 return arguments.length |
10206 return arguments.length |
10233 ? fn.apply(self, arguments) |
10207 ? fn.apply(self, arguments) |
10266 * @description |
10240 * @description |
10267 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be |
10241 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be |
10268 * stripped since angular uses this notation internally. |
10242 * stripped since angular uses this notation internally. |
10269 * |
10243 * |
10270 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. |
10244 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. |
10271 * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. |
10245 * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace. |
10246 * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2). |
|
10272 * @returns {string|undefined} JSON-ified string representing `obj`. |
10247 * @returns {string|undefined} JSON-ified string representing `obj`. |
10273 */ |
10248 */ |
10274 function toJson(obj, pretty) { |
10249 function toJson(obj, pretty) { |
10275 if (typeof obj === 'undefined') return undefined; |
10250 if (typeof obj === 'undefined') return undefined; |
10276 return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); |
10251 if (!isNumber(pretty)) { |
10252 pretty = pretty ? 2 : null; |
|
10253 } |
|
10254 return JSON.stringify(obj, toJsonReplacer, pretty); |
|
10277 } |
10255 } |
10278 |
10256 |
10279 |
10257 |
10280 /** |
10258 /** |
10281 * @ngdoc function |
10259 * @ngdoc function |
10285 * |
10263 * |
10286 * @description |
10264 * @description |
10287 * Deserializes a JSON string. |
10265 * Deserializes a JSON string. |
10288 * |
10266 * |
10289 * @param {string} json JSON string to deserialize. |
10267 * @param {string} json JSON string to deserialize. |
10290 * @returns {Object|Array|string|number} Deserialized thingy. |
10268 * @returns {Object|Array|string|number} Deserialized JSON string. |
10291 */ |
10269 */ |
10292 function fromJson(json) { |
10270 function fromJson(json) { |
10293 return isString(json) |
10271 return isString(json) |
10294 ? JSON.parse(json) |
10272 ? JSON.parse(json) |
10295 : json; |
10273 : json; |
10303 element = jqLite(element).clone(); |
10281 element = jqLite(element).clone(); |
10304 try { |
10282 try { |
10305 // turns out IE does not let you set .html() on elements which |
10283 // turns out IE does not let you set .html() on elements which |
10306 // are not allowed to have children. So we just ignore it. |
10284 // are not allowed to have children. So we just ignore it. |
10307 element.empty(); |
10285 element.empty(); |
10308 } catch(e) {} |
10286 } catch (e) {} |
10309 var elemHtml = jqLite('<div>').append(element).html(); |
10287 var elemHtml = jqLite('<div>').append(element).html(); |
10310 try { |
10288 try { |
10311 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : |
10289 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : |
10312 elemHtml. |
10290 elemHtml. |
10313 match(/^(<[^>]+>)/)[1]. |
10291 match(/^(<[^>]+>)/)[1]. |
10314 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); |
10292 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); |
10315 } catch(e) { |
10293 } catch (e) { |
10316 return lowercase(elemHtml); |
10294 return lowercase(elemHtml); |
10317 } |
10295 } |
10318 |
10296 |
10319 } |
10297 } |
10320 |
10298 |
10330 * with the decodeURIComponent function. |
10308 * with the decodeURIComponent function. |
10331 */ |
10309 */ |
10332 function tryDecodeURIComponent(value) { |
10310 function tryDecodeURIComponent(value) { |
10333 try { |
10311 try { |
10334 return decodeURIComponent(value); |
10312 return decodeURIComponent(value); |
10335 } catch(e) { |
10313 } catch (e) { |
10336 // Ignore any invalid uri component |
10314 // Ignore any invalid uri component |
10337 } |
10315 } |
10338 } |
10316 } |
10339 |
10317 |
10340 |
10318 |
10343 * @returns {Object.<string,boolean|Array>} |
10321 * @returns {Object.<string,boolean|Array>} |
10344 */ |
10322 */ |
10345 function parseKeyValue(/**string*/keyValue) { |
10323 function parseKeyValue(/**string*/keyValue) { |
10346 var obj = {}, key_value, key; |
10324 var obj = {}, key_value, key; |
10347 forEach((keyValue || "").split('&'), function(keyValue) { |
10325 forEach((keyValue || "").split('&'), function(keyValue) { |
10348 if ( keyValue ) { |
10326 if (keyValue) { |
10349 key_value = keyValue.replace(/\+/g,'%20').split('='); |
10327 key_value = keyValue.replace(/\+/g,'%20').split('='); |
10350 key = tryDecodeURIComponent(key_value[0]); |
10328 key = tryDecodeURIComponent(key_value[0]); |
10351 if ( isDefined(key) ) { |
10329 if (isDefined(key)) { |
10352 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; |
10330 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; |
10353 if (!hasOwnProperty.call(obj, key)) { |
10331 if (!hasOwnProperty.call(obj, key)) { |
10354 obj[key] = val; |
10332 obj[key] = val; |
10355 } else if(isArray(obj[key])) { |
10333 } else if (isArray(obj[key])) { |
10356 obj[key].push(val); |
10334 obj[key].push(val); |
10357 } else { |
10335 } else { |
10358 obj[key] = [obj[key],val]; |
10336 obj[key] = [obj[key],val]; |
10359 } |
10337 } |
10360 } |
10338 } |
10423 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; |
10401 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; |
10424 |
10402 |
10425 function getNgAttribute(element, ngAttr) { |
10403 function getNgAttribute(element, ngAttr) { |
10426 var attr, i, ii = ngAttrPrefixes.length; |
10404 var attr, i, ii = ngAttrPrefixes.length; |
10427 element = jqLite(element); |
10405 element = jqLite(element); |
10428 for (i=0; i<ii; ++i) { |
10406 for (i = 0; i < ii; ++i) { |
10429 attr = ngAttrPrefixes[i] + ngAttr; |
10407 attr = ngAttrPrefixes[i] + ngAttr; |
10430 if (isString(attr = element.attr(attr))) { |
10408 if (isString(attr = element.attr(attr))) { |
10431 return attr; |
10409 return attr; |
10432 } |
10410 } |
10433 } |
10411 } |
10458 * found in the document will be used to define the root element to auto-bootstrap as an |
10436 * found in the document will be used to define the root element to auto-bootstrap as an |
10459 * application. To run multiple applications in an HTML document you must manually bootstrap them using |
10437 * application. To run multiple applications in an HTML document you must manually bootstrap them using |
10460 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. |
10438 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. |
10461 * |
10439 * |
10462 * You can specify an **AngularJS module** to be used as the root module for the application. This |
10440 * You can specify an **AngularJS module** to be used as the root module for the application. This |
10463 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and |
10441 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It |
10464 * should contain the application code needed or have dependencies on other modules that will |
10442 * should contain the application code needed or have dependencies on other modules that will |
10465 * contain the code. See {@link angular.module} for more information. |
10443 * contain the code. See {@link angular.module} for more information. |
10466 * |
10444 * |
10467 * In the example below if the `ngApp` directive were not placed on the `html` element then the |
10445 * In the example below if the `ngApp` directive were not placed on the `html` element then the |
10468 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` |
10446 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` |
10469 * would not be resolved to `3`. |
10447 * would not be resolved to `3`. |
10470 * |
10448 * |
10471 * `ngApp` is the easiest, and most common, way to bootstrap an application. |
10449 * `ngApp` is the easiest, and most common way to bootstrap an application. |
10472 * |
10450 * |
10473 <example module="ngAppDemo"> |
10451 <example module="ngAppDemo"> |
10474 <file name="index.html"> |
10452 <file name="index.html"> |
10475 <div ng-controller="ngAppDemoController"> |
10453 <div ng-controller="ngAppDemoController"> |
10476 I can add: {{a}} + {{b}} = {{ a+b }} |
10454 I can add: {{a}} + {{b}} = {{ a+b }} |
10633 * function that will be invoked by the injector as a run block. |
10611 * function that will be invoked by the injector as a run block. |
10634 * See: {@link angular.module modules} |
10612 * See: {@link angular.module modules} |
10635 * @param {Object=} config an object for defining configuration options for the application. The |
10613 * @param {Object=} config an object for defining configuration options for the application. The |
10636 * following keys are supported: |
10614 * following keys are supported: |
10637 * |
10615 * |
10638 * - `strictDi`: disable automatic function annotation for the application. This is meant to |
10616 * * `strictDi` - disable automatic function annotation for the application. This is meant to |
10639 * assist in finding bugs which break minified code. |
10617 * assist in finding bugs which break minified code. Defaults to `false`. |
10640 * |
10618 * |
10641 * @returns {auto.$injector} Returns the newly created injector for this app. |
10619 * @returns {auto.$injector} Returns the newly created injector for this app. |
10642 */ |
10620 */ |
10643 function bootstrap(element, modules, config) { |
10621 function bootstrap(element, modules, config) { |
10644 if (!isObject(config)) config = {}; |
10622 if (!isObject(config)) config = {}; |
10726 * Get the testability service for the instance of Angular on the given |
10704 * Get the testability service for the instance of Angular on the given |
10727 * element. |
10705 * element. |
10728 * @param {DOMElement} element DOM element which is the root of angular application. |
10706 * @param {DOMElement} element DOM element which is the root of angular application. |
10729 */ |
10707 */ |
10730 function getTestability(rootElement) { |
10708 function getTestability(rootElement) { |
10731 return angular.element(rootElement).injector().get('$$testability'); |
10709 var injector = angular.element(rootElement).injector(); |
10710 if (!injector) { |
|
10711 throw ngMinErr('test', |
|
10712 'no injector found for element argument to getTestability'); |
|
10713 } |
|
10714 return injector.get('$$testability'); |
|
10732 } |
10715 } |
10733 |
10716 |
10734 var SNAKE_CASE_REGEXP = /[A-Z]/g; |
10717 var SNAKE_CASE_REGEXP = /[A-Z]/g; |
10735 function snake_case(name, separator) { |
10718 function snake_case(name, separator) { |
10736 separator = separator || '_'; |
10719 separator = separator || '_'; |
11113 * } |
11096 * } |
11114 * } |
11097 * } |
11115 * }) |
11098 * }) |
11116 * ``` |
11099 * ``` |
11117 * |
11100 * |
11118 * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and |
11101 * See {@link ng.$animateProvider#register $animateProvider.register()} and |
11119 * {@link ngAnimate ngAnimate module} for more information. |
11102 * {@link ngAnimate ngAnimate module} for more information. |
11120 */ |
11103 */ |
11121 animation: invokeLater('$animateProvider', 'register'), |
11104 animation: invokeLater('$animateProvider', 'register'), |
11122 |
11105 |
11123 /** |
11106 /** |
11163 * @param {Function} configFn Execute this function on module load. Useful for service |
11146 * @param {Function} configFn Execute this function on module load. Useful for service |
11164 * configuration. |
11147 * configuration. |
11165 * @description |
11148 * @description |
11166 * Use this method to register work which needs to be performed on module loading. |
11149 * Use this method to register work which needs to be performed on module loading. |
11167 * For more about how to configure services, see |
11150 * For more about how to configure services, see |
11168 * {@link providers#providers_provider-recipe Provider Recipe}. |
11151 * {@link providers#provider-recipe Provider Recipe}. |
11169 */ |
11152 */ |
11170 config: config, |
11153 config: config, |
11171 |
11154 |
11172 /** |
11155 /** |
11173 * @ngdoc method |
11156 * @ngdoc method |
11187 |
11170 |
11188 if (configFn) { |
11171 if (configFn) { |
11189 config(configFn); |
11172 config(configFn); |
11190 } |
11173 } |
11191 |
11174 |
11192 return moduleInstance; |
11175 return moduleInstance; |
11193 |
11176 |
11194 /** |
11177 /** |
11195 * @param {string} provider |
11178 * @param {string} provider |
11196 * @param {string} method |
11179 * @param {string} method |
11197 * @param {String=} insertMethod |
11180 * @param {String=} insertMethod |
11206 } |
11189 } |
11207 }); |
11190 }); |
11208 }; |
11191 }; |
11209 }); |
11192 }); |
11210 |
11193 |
11194 } |
|
11195 |
|
11196 /* global: toDebugString: true */ |
|
11197 |
|
11198 function serializeObject(obj) { |
|
11199 var seen = []; |
|
11200 |
|
11201 return JSON.stringify(obj, function(key, val) { |
|
11202 val = toJsonReplacer(key, val); |
|
11203 if (isObject(val)) { |
|
11204 |
|
11205 if (seen.indexOf(val) >= 0) return '<<already seen>>'; |
|
11206 |
|
11207 seen.push(val); |
|
11208 } |
|
11209 return val; |
|
11210 }); |
|
11211 } |
|
11212 |
|
11213 function toDebugString(obj) { |
|
11214 if (typeof obj === 'function') { |
|
11215 return obj.toString().replace(/ \{[\s\S]*$/, ''); |
|
11216 } else if (typeof obj === 'undefined') { |
|
11217 return 'undefined'; |
|
11218 } else if (typeof obj !== 'string') { |
|
11219 return serializeObject(obj); |
|
11220 } |
|
11221 return obj; |
|
11211 } |
11222 } |
11212 |
11223 |
11213 /* global angularModule: true, |
11224 /* global angularModule: true, |
11214 version: true, |
11225 version: true, |
11215 |
11226 |
11291 $TemplateRequestProvider, |
11302 $TemplateRequestProvider, |
11292 $$TestabilityProvider, |
11303 $$TestabilityProvider, |
11293 $TimeoutProvider, |
11304 $TimeoutProvider, |
11294 $$RAFProvider, |
11305 $$RAFProvider, |
11295 $$AsyncCallbackProvider, |
11306 $$AsyncCallbackProvider, |
11296 $WindowProvider |
11307 $WindowProvider, |
11308 $$jqLiteProvider |
|
11297 */ |
11309 */ |
11298 |
11310 |
11299 |
11311 |
11300 /** |
11312 /** |
11301 * @ngdoc object |
11313 * @ngdoc object |
11310 * - `minor` – `{number}` – Minor version number, such as "9". |
11322 * - `minor` – `{number}` – Minor version number, such as "9". |
11311 * - `dot` – `{number}` – Dot version number, such as "18". |
11323 * - `dot` – `{number}` – Dot version number, such as "18". |
11312 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". |
11324 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". |
11313 */ |
11325 */ |
11314 var version = { |
11326 var version = { |
11315 full: '1.3.0-rc.5', // all of these placeholder strings will be replaced by grunt's |
11327 full: '1.3.8', // all of these placeholder strings will be replaced by grunt's |
11316 major: 1, // package task |
11328 major: 1, // package task |
11317 minor: 3, |
11329 minor: 3, |
11318 dot: 0, |
11330 dot: 8, |
11319 codeName: 'impossible-choreography' |
11331 codeName: 'prophetic-narwhal' |
11320 }; |
11332 }; |
11321 |
11333 |
11322 |
11334 |
11323 function publishExternalAPI(angular){ |
11335 function publishExternalAPI(angular) { |
11324 extend(angular, { |
11336 extend(angular, { |
11325 'bootstrap': bootstrap, |
11337 'bootstrap': bootstrap, |
11326 'copy': copy, |
11338 'copy': copy, |
11327 'extend': extend, |
11339 'extend': extend, |
11328 'equals': equals, |
11340 'equals': equals, |
11444 $templateRequest: $TemplateRequestProvider, |
11456 $templateRequest: $TemplateRequestProvider, |
11445 $$testability: $$TestabilityProvider, |
11457 $$testability: $$TestabilityProvider, |
11446 $timeout: $TimeoutProvider, |
11458 $timeout: $TimeoutProvider, |
11447 $window: $WindowProvider, |
11459 $window: $WindowProvider, |
11448 $$rAF: $$RAFProvider, |
11460 $$rAF: $$RAFProvider, |
11449 $$asyncCallback : $$AsyncCallbackProvider |
11461 $$asyncCallback: $$AsyncCallbackProvider, |
11462 $$jqLite: $$jqLiteProvider |
|
11450 }); |
11463 }); |
11451 } |
11464 } |
11452 ]); |
11465 ]); |
11453 } |
11466 } |
11454 |
11467 |
11489 * jqLite provides only the following jQuery methods: |
11502 * jqLite provides only the following jQuery methods: |
11490 * |
11503 * |
11491 * - [`addClass()`](http://api.jquery.com/addClass/) |
11504 * - [`addClass()`](http://api.jquery.com/addClass/) |
11492 * - [`after()`](http://api.jquery.com/after/) |
11505 * - [`after()`](http://api.jquery.com/after/) |
11493 * - [`append()`](http://api.jquery.com/append/) |
11506 * - [`append()`](http://api.jquery.com/append/) |
11494 * - [`attr()`](http://api.jquery.com/attr/) |
11507 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters |
11495 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData |
11508 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData |
11496 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors |
11509 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors |
11497 * - [`clone()`](http://api.jquery.com/clone/) |
11510 * - [`clone()`](http://api.jquery.com/clone/) |
11498 * - [`contents()`](http://api.jquery.com/contents/) |
11511 * - [`contents()`](http://api.jquery.com/contents/) |
11499 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()` |
11512 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()` |
11500 * - [`data()`](http://api.jquery.com/data/) |
11513 * - [`data()`](http://api.jquery.com/data/) |
11501 * - [`detach()`](http://api.jquery.com/detach/) |
11514 * - [`detach()`](http://api.jquery.com/detach/) |
11502 * - [`empty()`](http://api.jquery.com/empty/) |
11515 * - [`empty()`](http://api.jquery.com/empty/) |
11503 * - [`eq()`](http://api.jquery.com/eq/) |
11516 * - [`eq()`](http://api.jquery.com/eq/) |
11504 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name |
11517 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name |
11537 * retrieves controller associated with the `ngController` directive. If `name` is provided as |
11550 * retrieves controller associated with the `ngController` directive. If `name` is provided as |
11538 * camelCase directive name, then the controller for this directive will be retrieved (e.g. |
11551 * camelCase directive name, then the controller for this directive will be retrieved (e.g. |
11539 * `'ngModel'`). |
11552 * `'ngModel'`). |
11540 * - `injector()` - retrieves the injector of the current element or its parent. |
11553 * - `injector()` - retrieves the injector of the current element or its parent. |
11541 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current |
11554 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current |
11542 * element or its parent. |
11555 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to |
11556 * be enabled. |
|
11543 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the |
11557 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the |
11544 * current element. This getter should be used only on elements that contain a directive which starts a new isolate |
11558 * current element. This getter should be used only on elements that contain a directive which starts a new isolate |
11545 * scope. Calling `scope()` on this element always returns the original non-isolate scope. |
11559 * scope. Calling `scope()` on this element always returns the original non-isolate scope. |
11560 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. |
|
11546 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top |
11561 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top |
11547 * parent element is reached. |
11562 * parent element is reached. |
11548 * |
11563 * |
11549 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. |
11564 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. |
11550 * @returns {Object} jQuery object. |
11565 * @returns {Object} jQuery object. |
11572 function jqNextId() { return ++jqId; } |
11587 function jqNextId() { return ++jqId; } |
11573 |
11588 |
11574 |
11589 |
11575 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; |
11590 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; |
11576 var MOZ_HACK_REGEXP = /^moz([A-Z])/; |
11591 var MOZ_HACK_REGEXP = /^moz([A-Z])/; |
11577 var MOUSE_EVENT_MAP= { mouseleave : "mouseout", mouseenter : "mouseover"}; |
11592 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; |
11578 var jqLiteMinErr = minErr('jqLite'); |
11593 var jqLiteMinErr = minErr('jqLite'); |
11579 |
11594 |
11580 /** |
11595 /** |
11581 * Converts snake_case to camelCase. |
11596 * Converts snake_case to camelCase. |
11582 * Also there is special case for Moz prefix starting with upper case letter. |
11597 * Also there is special case for Moz prefix starting with upper case letter. |
11701 |
11716 |
11702 function jqLiteClone(element) { |
11717 function jqLiteClone(element) { |
11703 return element.cloneNode(true); |
11718 return element.cloneNode(true); |
11704 } |
11719 } |
11705 |
11720 |
11706 function jqLiteDealoc(element, onlyDescendants){ |
11721 function jqLiteDealoc(element, onlyDescendants) { |
11707 if (!onlyDescendants) jqLiteRemoveData(element); |
11722 if (!onlyDescendants) jqLiteRemoveData(element); |
11708 |
11723 |
11709 if (element.querySelectorAll) { |
11724 if (element.querySelectorAll) { |
11710 var descendants = element.querySelectorAll('*'); |
11725 var descendants = element.querySelectorAll('*'); |
11711 for (var i = 0, l = descendants.length; i < l; i++) { |
11726 for (var i = 0, l = descendants.length; i < l; i++) { |
11724 if (!handle) return; //no listeners registered |
11739 if (!handle) return; //no listeners registered |
11725 |
11740 |
11726 if (!type) { |
11741 if (!type) { |
11727 for (type in events) { |
11742 for (type in events) { |
11728 if (type !== '$destroy') { |
11743 if (type !== '$destroy') { |
11729 removeEventListenerFn(element, type, events[type]); |
11744 removeEventListenerFn(element, type, handle); |
11730 } |
11745 } |
11731 delete events[type]; |
11746 delete events[type]; |
11732 } |
11747 } |
11733 } else { |
11748 } else { |
11734 forEach(type.split(' '), function(type) { |
11749 forEach(type.split(' '), function(type) { |
11735 if (isUndefined(fn)) { |
11750 if (isDefined(fn)) { |
11736 removeEventListenerFn(element, type, events[type]); |
11751 var listenerFns = events[type]; |
11737 delete events[type]; |
11752 arrayRemove(listenerFns || [], fn); |
11738 } else { |
11753 if (listenerFns && listenerFns.length > 0) { |
11739 arrayRemove(events[type] || [], fn); |
11754 return; |
11755 } |
|
11740 } |
11756 } |
11757 |
|
11758 removeEventListenerFn(element, type, handle); |
|
11759 delete events[type]; |
|
11741 }); |
11760 }); |
11742 } |
11761 } |
11743 } |
11762 } |
11744 |
11763 |
11745 function jqLiteRemoveData(element, name) { |
11764 function jqLiteRemoveData(element, name) { |
11804 } |
11823 } |
11805 |
11824 |
11806 function jqLiteHasClass(element, selector) { |
11825 function jqLiteHasClass(element, selector) { |
11807 if (!element.getAttribute) return false; |
11826 if (!element.getAttribute) return false; |
11808 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). |
11827 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). |
11809 indexOf( " " + selector + " " ) > -1); |
11828 indexOf(" " + selector + " ") > -1); |
11810 } |
11829 } |
11811 |
11830 |
11812 function jqLiteRemoveClass(element, cssClasses) { |
11831 function jqLiteRemoveClass(element, cssClasses) { |
11813 if (cssClasses && element.setAttribute) { |
11832 if (cssClasses && element.setAttribute) { |
11814 forEach(cssClasses.split(' '), function(cssClass) { |
11833 forEach(cssClasses.split(' '), function(cssClass) { |
11863 } |
11882 } |
11864 } |
11883 } |
11865 |
11884 |
11866 |
11885 |
11867 function jqLiteController(element, name) { |
11886 function jqLiteController(element, name) { |
11868 return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); |
11887 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); |
11869 } |
11888 } |
11870 |
11889 |
11871 function jqLiteInheritedData(element, name, value) { |
11890 function jqLiteInheritedData(element, name, value) { |
11872 // if element is the document object work with the html element instead |
11891 // if element is the document object work with the html element instead |
11873 // this makes $(document).scope() possible |
11892 // this makes $(document).scope() possible |
11874 if(element.nodeType == NODE_TYPE_DOCUMENT) { |
11893 if (element.nodeType == NODE_TYPE_DOCUMENT) { |
11875 element = element.documentElement; |
11894 element = element.documentElement; |
11876 } |
11895 } |
11877 var names = isArray(name) ? name : [name]; |
11896 var names = isArray(name) ? name : [name]; |
11878 |
11897 |
11879 while (element) { |
11898 while (element) { |
11899 if (!keepData) jqLiteDealoc(element); |
11918 if (!keepData) jqLiteDealoc(element); |
11900 var parent = element.parentNode; |
11919 var parent = element.parentNode; |
11901 if (parent) parent.removeChild(element); |
11920 if (parent) parent.removeChild(element); |
11902 } |
11921 } |
11903 |
11922 |
11923 |
|
11924 function jqLiteDocumentLoaded(action, win) { |
|
11925 win = win || window; |
|
11926 if (win.document.readyState === 'complete') { |
|
11927 // Force the action to be run async for consistent behaviour |
|
11928 // from the action's point of view |
|
11929 // i.e. it will definitely not be in a $apply |
|
11930 win.setTimeout(action); |
|
11931 } else { |
|
11932 // No need to unbind this handler as load is only ever called once |
|
11933 jqLite(win).on('load', action); |
|
11934 } |
|
11935 } |
|
11936 |
|
11904 ////////////////////////////////////////// |
11937 ////////////////////////////////////////// |
11905 // Functions which are declared directly. |
11938 // Functions which are declared directly. |
11906 ////////////////////////////////////////// |
11939 ////////////////////////////////////////// |
11907 var JQLitePrototype = JQLite.prototype = { |
11940 var JQLitePrototype = JQLite.prototype = { |
11908 ready: function(fn) { |
11941 ready: function(fn) { |
11913 fired = true; |
11946 fired = true; |
11914 fn(); |
11947 fn(); |
11915 } |
11948 } |
11916 |
11949 |
11917 // check if document is already loaded |
11950 // check if document is already loaded |
11918 if (document.readyState === 'complete'){ |
11951 if (document.readyState === 'complete') { |
11919 setTimeout(trigger); |
11952 setTimeout(trigger); |
11920 } else { |
11953 } else { |
11921 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 |
11954 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 |
11922 // we can not use jqLite since we are not done loading and jQuery could be loaded later. |
11955 // we can not use jqLite since we are not done loading and jQuery could be loaded later. |
11923 // jshint -W064 |
11956 // jshint -W064 |
11924 JQLite(window).on('load', trigger); // fallback to window.onload for others |
11957 JQLite(window).on('load', trigger); // fallback to window.onload for others |
11925 // jshint +W064 |
11958 // jshint +W064 |
11926 this.on('DOMContentLoaded', trigger); |
|
11927 } |
11959 } |
11928 }, |
11960 }, |
11929 toString: function() { |
11961 toString: function() { |
11930 var value = []; |
11962 var value = []; |
11931 forEach(this, function(e){ value.push('' + e);}); |
11963 forEach(this, function(e) { value.push('' + e);}); |
11932 return '[' + value.join(', ') + ']'; |
11964 return '[' + value.join(', ') + ']'; |
11933 }, |
11965 }, |
11934 |
11966 |
11935 eq: function(index) { |
11967 eq: function(index) { |
11936 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); |
11968 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); |
11954 var BOOLEAN_ELEMENTS = {}; |
11986 var BOOLEAN_ELEMENTS = {}; |
11955 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { |
11987 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { |
11956 BOOLEAN_ELEMENTS[value] = true; |
11988 BOOLEAN_ELEMENTS[value] = true; |
11957 }); |
11989 }); |
11958 var ALIASED_ATTR = { |
11990 var ALIASED_ATTR = { |
11959 'ngMinlength' : 'minlength', |
11991 'ngMinlength': 'minlength', |
11960 'ngMaxlength' : 'maxlength', |
11992 'ngMaxlength': 'maxlength', |
11961 'ngMin' : 'min', |
11993 'ngMin': 'min', |
11962 'ngMax' : 'max', |
11994 'ngMax': 'max', |
11963 'ngPattern' : 'pattern' |
11995 'ngPattern': 'pattern' |
11964 }; |
11996 }; |
11965 |
11997 |
11966 function getBooleanAttrName(element, name) { |
11998 function getBooleanAttrName(element, name) { |
11967 // check dom last since we will most likely fail on name |
11999 // check dom last since we will most likely fail on name |
11968 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; |
12000 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; |
12017 } else { |
12049 } else { |
12018 return element.style[name]; |
12050 return element.style[name]; |
12019 } |
12051 } |
12020 }, |
12052 }, |
12021 |
12053 |
12022 attr: function(element, name, value){ |
12054 attr: function(element, name, value) { |
12023 var lowercasedName = lowercase(name); |
12055 var lowercasedName = lowercase(name); |
12024 if (BOOLEAN_ATTR[lowercasedName]) { |
12056 if (BOOLEAN_ATTR[lowercasedName]) { |
12025 if (isDefined(value)) { |
12057 if (isDefined(value)) { |
12026 if (!!value) { |
12058 if (!!value) { |
12027 element[name] = true; |
12059 element[name] = true; |
12030 element[name] = false; |
12062 element[name] = false; |
12031 element.removeAttribute(lowercasedName); |
12063 element.removeAttribute(lowercasedName); |
12032 } |
12064 } |
12033 } else { |
12065 } else { |
12034 return (element[name] || |
12066 return (element[name] || |
12035 (element.attributes.getNamedItem(name)|| noop).specified) |
12067 (element.attributes.getNamedItem(name) || noop).specified) |
12036 ? lowercasedName |
12068 ? lowercasedName |
12037 : undefined; |
12069 : undefined; |
12038 } |
12070 } |
12039 } else if (isDefined(value)) { |
12071 } else if (isDefined(value)) { |
12040 element.setAttribute(name, value); |
12072 element.setAttribute(name, value); |
12070 |
12102 |
12071 val: function(element, value) { |
12103 val: function(element, value) { |
12072 if (isUndefined(value)) { |
12104 if (isUndefined(value)) { |
12073 if (element.multiple && nodeName_(element) === 'select') { |
12105 if (element.multiple && nodeName_(element) === 'select') { |
12074 var result = []; |
12106 var result = []; |
12075 forEach(element.options, function (option) { |
12107 forEach(element.options, function(option) { |
12076 if (option.selected) { |
12108 if (option.selected) { |
12077 result.push(option.value || option.text); |
12109 result.push(option.value || option.text); |
12078 } |
12110 } |
12079 }); |
12111 }); |
12080 return result.length === 0 ? null : result; |
12112 return result.length === 0 ? null : result; |
12091 jqLiteDealoc(element, true); |
12123 jqLiteDealoc(element, true); |
12092 element.innerHTML = value; |
12124 element.innerHTML = value; |
12093 }, |
12125 }, |
12094 |
12126 |
12095 empty: jqLiteEmpty |
12127 empty: jqLiteEmpty |
12096 }, function(fn, name){ |
12128 }, function(fn, name) { |
12097 /** |
12129 /** |
12098 * Properties: writes return selection, reads return first value |
12130 * Properties: writes return selection, reads return first value |
12099 */ |
12131 */ |
12100 JQLite.prototype[name] = function(arg1, arg2) { |
12132 JQLite.prototype[name] = function(arg1, arg2) { |
12101 var i, key; |
12133 var i, key; |
12143 } |
12175 } |
12144 }; |
12176 }; |
12145 }); |
12177 }); |
12146 |
12178 |
12147 function createEventHandler(element, events) { |
12179 function createEventHandler(element, events) { |
12148 var eventHandler = function (event, type) { |
12180 var eventHandler = function(event, type) { |
12149 // jQuery specific api |
12181 // jQuery specific api |
12150 event.isDefaultPrevented = function() { |
12182 event.isDefaultPrevented = function() { |
12151 return event.defaultPrevented; |
12183 return event.defaultPrevented; |
12152 }; |
12184 }; |
12153 |
12185 |
12199 // selector. |
12231 // selector. |
12200 ////////////////////////////////////////// |
12232 ////////////////////////////////////////// |
12201 forEach({ |
12233 forEach({ |
12202 removeData: jqLiteRemoveData, |
12234 removeData: jqLiteRemoveData, |
12203 |
12235 |
12204 on: function jqLiteOn(element, type, fn, unsupported){ |
12236 on: function jqLiteOn(element, type, fn, unsupported) { |
12205 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); |
12237 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); |
12206 |
12238 |
12207 // Do not add event handlers to non-elements because they will not be cleaned up. |
12239 // Do not add event handlers to non-elements because they will not be cleaned up. |
12208 if (!jqLiteAcceptsData(element)) { |
12240 if (!jqLiteAcceptsData(element)) { |
12209 return; |
12241 return; |
12235 |
12267 |
12236 jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { |
12268 jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { |
12237 var target = this, related = event.relatedTarget; |
12269 var target = this, related = event.relatedTarget; |
12238 // For mousenter/leave call the handler if related is outside the target. |
12270 // For mousenter/leave call the handler if related is outside the target. |
12239 // NB: No relatedTarget if the mouse left/entered the browser window |
12271 // NB: No relatedTarget if the mouse left/entered the browser window |
12240 if ( !related || (related !== target && !target.contains(related)) ){ |
12272 if (!related || (related !== target && !target.contains(related))) { |
12241 handle(event, type); |
12273 handle(event, type); |
12242 } |
12274 } |
12243 }); |
12275 }); |
12244 |
12276 |
12245 } else { |
12277 } else { |
12269 }, |
12301 }, |
12270 |
12302 |
12271 replaceWith: function(element, replaceNode) { |
12303 replaceWith: function(element, replaceNode) { |
12272 var index, parent = element.parentNode; |
12304 var index, parent = element.parentNode; |
12273 jqLiteDealoc(element); |
12305 jqLiteDealoc(element); |
12274 forEach(new JQLite(replaceNode), function(node){ |
12306 forEach(new JQLite(replaceNode), function(node) { |
12275 if (index) { |
12307 if (index) { |
12276 parent.insertBefore(node, index.nextSibling); |
12308 parent.insertBefore(node, index.nextSibling); |
12277 } else { |
12309 } else { |
12278 parent.replaceChild(node, element); |
12310 parent.replaceChild(node, element); |
12279 } |
12311 } |
12281 }); |
12313 }); |
12282 }, |
12314 }, |
12283 |
12315 |
12284 children: function(element) { |
12316 children: function(element) { |
12285 var children = []; |
12317 var children = []; |
12286 forEach(element.childNodes, function(element){ |
12318 forEach(element.childNodes, function(element) { |
12287 if (element.nodeType === NODE_TYPE_ELEMENT) |
12319 if (element.nodeType === NODE_TYPE_ELEMENT) |
12288 children.push(element); |
12320 children.push(element); |
12289 }); |
12321 }); |
12290 return children; |
12322 return children; |
12291 }, |
12323 }, |
12307 }, |
12339 }, |
12308 |
12340 |
12309 prepend: function(element, node) { |
12341 prepend: function(element, node) { |
12310 if (element.nodeType === NODE_TYPE_ELEMENT) { |
12342 if (element.nodeType === NODE_TYPE_ELEMENT) { |
12311 var index = element.firstChild; |
12343 var index = element.firstChild; |
12312 forEach(new JQLite(node), function(child){ |
12344 forEach(new JQLite(node), function(child) { |
12313 element.insertBefore(child, index); |
12345 element.insertBefore(child, index); |
12314 }); |
12346 }); |
12315 } |
12347 } |
12316 }, |
12348 }, |
12317 |
12349 |
12344 addClass: jqLiteAddClass, |
12376 addClass: jqLiteAddClass, |
12345 removeClass: jqLiteRemoveClass, |
12377 removeClass: jqLiteRemoveClass, |
12346 |
12378 |
12347 toggleClass: function(element, selector, condition) { |
12379 toggleClass: function(element, selector, condition) { |
12348 if (selector) { |
12380 if (selector) { |
12349 forEach(selector.split(' '), function(className){ |
12381 forEach(selector.split(' '), function(className) { |
12350 var classCondition = condition; |
12382 var classCondition = condition; |
12351 if (isUndefined(classCondition)) { |
12383 if (isUndefined(classCondition)) { |
12352 classCondition = !jqLiteHasClass(element, className); |
12384 classCondition = !jqLiteHasClass(element, className); |
12353 } |
12385 } |
12354 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); |
12386 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); |
12409 fn.apply(element, handlerArgs); |
12441 fn.apply(element, handlerArgs); |
12410 } |
12442 } |
12411 }); |
12443 }); |
12412 } |
12444 } |
12413 } |
12445 } |
12414 }, function(fn, name){ |
12446 }, function(fn, name) { |
12415 /** |
12447 /** |
12416 * chaining functions |
12448 * chaining functions |
12417 */ |
12449 */ |
12418 JQLite.prototype[name] = function(arg1, arg2, arg3) { |
12450 JQLite.prototype[name] = function(arg1, arg2, arg3) { |
12419 var value; |
12451 var value; |
12420 |
12452 |
12421 for(var i = 0, ii = this.length; i < ii; i++) { |
12453 for (var i = 0, ii = this.length; i < ii; i++) { |
12422 if (isUndefined(value)) { |
12454 if (isUndefined(value)) { |
12423 value = fn(this[i], arg1, arg2, arg3); |
12455 value = fn(this[i], arg1, arg2, arg3); |
12424 if (isDefined(value)) { |
12456 if (isDefined(value)) { |
12425 // any function which returns a value needs to be wrapped |
12457 // any function which returns a value needs to be wrapped |
12426 value = jqLite(value); |
12458 value = jqLite(value); |
12434 |
12466 |
12435 // bind legacy bind/unbind to on/off |
12467 // bind legacy bind/unbind to on/off |
12436 JQLite.prototype.bind = JQLite.prototype.on; |
12468 JQLite.prototype.bind = JQLite.prototype.on; |
12437 JQLite.prototype.unbind = JQLite.prototype.off; |
12469 JQLite.prototype.unbind = JQLite.prototype.off; |
12438 }); |
12470 }); |
12471 |
|
12472 |
|
12473 // Provider for private $$jqLite service |
|
12474 function $$jqLiteProvider() { |
|
12475 this.$get = function $$jqLite() { |
|
12476 return extend(JQLite, { |
|
12477 hasClass: function(node, classes) { |
|
12478 if (node.attr) node = node[0]; |
|
12479 return jqLiteHasClass(node, classes); |
|
12480 }, |
|
12481 addClass: function(node, classes) { |
|
12482 if (node.attr) node = node[0]; |
|
12483 return jqLiteAddClass(node, classes); |
|
12484 }, |
|
12485 removeClass: function(node, classes) { |
|
12486 if (node.attr) node = node[0]; |
|
12487 return jqLiteRemoveClass(node, classes); |
|
12488 } |
|
12489 }); |
|
12490 }; |
|
12491 } |
|
12439 |
12492 |
12440 /** |
12493 /** |
12441 * Computes a hash of an 'obj'. |
12494 * Computes a hash of an 'obj'. |
12442 * Hash of a: |
12495 * Hash of a: |
12443 * string is string |
12496 * string is string |
12518 * |
12571 * |
12519 * @description |
12572 * @description |
12520 * Creates an injector object that can be used for retrieving services as well as for |
12573 * Creates an injector object that can be used for retrieving services as well as for |
12521 * dependency injection (see {@link guide/di dependency injection}). |
12574 * dependency injection (see {@link guide/di dependency injection}). |
12522 * |
12575 * |
12523 |
|
12524 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See |
12576 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See |
12525 * {@link angular.module}. The `ng` module must be explicitly added. |
12577 * {@link angular.module}. The `ng` module must be explicitly added. |
12526 * @returns {function()} Injector object. See {@link auto.$injector $injector}. |
12578 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which |
12579 * disallows argument name annotation inference. |
|
12580 * @returns {injector} Injector object. See {@link auto.$injector $injector}. |
|
12527 * |
12581 * |
12528 * @example |
12582 * @example |
12529 * Typical usage |
12583 * Typical usage |
12530 * ```js |
12584 * ```js |
12531 * // create an injector |
12585 * // create an injector |
12666 * ``` |
12720 * ``` |
12667 * |
12721 * |
12668 * ## Inference |
12722 * ## Inference |
12669 * |
12723 * |
12670 * In JavaScript calling `toString()` on a function returns the function definition. The definition |
12724 * In JavaScript calling `toString()` on a function returns the function definition. The definition |
12671 * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with |
12725 * can then be parsed and the function arguments can be extracted. This method of discovering |
12672 * minification, and obfuscation tools since these tools change the argument names. |
12726 * annotations is disallowed when the injector is in strict mode. |
12727 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the |
|
12728 * argument names. |
|
12673 * |
12729 * |
12674 * ## `$inject` Annotation |
12730 * ## `$inject` Annotation |
12675 * By adding an `$inject` property onto a function the injection parameters can be specified. |
12731 * By adding an `$inject` property onto a function the injection parameters can be specified. |
12676 * |
12732 * |
12677 * ## Inline |
12733 * ## Inline |
12684 * |
12740 * |
12685 * @description |
12741 * @description |
12686 * Return an instance of the service. |
12742 * Return an instance of the service. |
12687 * |
12743 * |
12688 * @param {string} name The name of the instance to retrieve. |
12744 * @param {string} name The name of the instance to retrieve. |
12745 * @param {string} caller An optional string to provide the origin of the function call for error messages. |
|
12689 * @return {*} The instance. |
12746 * @return {*} The instance. |
12690 */ |
12747 */ |
12691 |
12748 |
12692 /** |
12749 /** |
12693 * @ngdoc method |
12750 * @ngdoc method |
12751 * } |
12808 * } |
12752 * |
12809 * |
12753 * // Then |
12810 * // Then |
12754 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); |
12811 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); |
12755 * ``` |
12812 * ``` |
12813 * |
|
12814 * You can disallow this method by using strict injection mode. |
|
12756 * |
12815 * |
12757 * This method does not work with code minification / obfuscation. For this reason the following |
12816 * This method does not work with code minification / obfuscation. For this reason the following |
12758 * annotation strategies are supported. |
12817 * annotation strategies are supported. |
12759 * |
12818 * |
12760 * # The `$inject` property |
12819 * # The `$inject` property |
12803 * ).toEqual(['$compile', '$rootScope']); |
12862 * ).toEqual(['$compile', '$rootScope']); |
12804 * ``` |
12863 * ``` |
12805 * |
12864 * |
12806 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to |
12865 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to |
12807 * be retrieved as described above. |
12866 * be retrieved as described above. |
12867 * |
|
12868 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. |
|
12808 * |
12869 * |
12809 * @returns {Array.<string>} The names of the services which the function requires. |
12870 * @returns {Array.<string>} The names of the services which the function requires. |
12810 */ |
12871 */ |
12811 |
12872 |
12812 |
12873 |
13130 constant: supportObject(constant), |
13191 constant: supportObject(constant), |
13131 decorator: decorator |
13192 decorator: decorator |
13132 } |
13193 } |
13133 }, |
13194 }, |
13134 providerInjector = (providerCache.$injector = |
13195 providerInjector = (providerCache.$injector = |
13135 createInternalInjector(providerCache, function() { |
13196 createInternalInjector(providerCache, function(serviceName, caller) { |
13197 if (angular.isString(caller)) { |
|
13198 path.push(caller); |
|
13199 } |
|
13136 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); |
13200 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); |
13137 })), |
13201 })), |
13138 instanceCache = {}, |
13202 instanceCache = {}, |
13139 instanceInjector = (instanceCache.$injector = |
13203 instanceInjector = (instanceCache.$injector = |
13140 createInternalInjector(instanceCache, function(servicename) { |
13204 createInternalInjector(instanceCache, function(serviceName, caller) { |
13141 var provider = providerInjector.get(servicename + providerSuffix); |
13205 var provider = providerInjector.get(serviceName + providerSuffix, caller); |
13142 return instanceInjector.invoke(provider.$get, provider, undefined, servicename); |
13206 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); |
13143 })); |
13207 })); |
13144 |
13208 |
13145 |
13209 |
13146 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); |
13210 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); |
13147 |
13211 |
13172 return providerCache[name + providerSuffix] = provider_; |
13236 return providerCache[name + providerSuffix] = provider_; |
13173 } |
13237 } |
13174 |
13238 |
13175 function enforceReturnValue(name, factory) { |
13239 function enforceReturnValue(name, factory) { |
13176 return function enforcedReturnValue() { |
13240 return function enforcedReturnValue() { |
13177 var result = instanceInjector.invoke(factory); |
13241 var result = instanceInjector.invoke(factory, this); |
13178 if (isUndefined(result)) { |
13242 if (isUndefined(result)) { |
13179 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); |
13243 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); |
13180 } |
13244 } |
13181 return result; |
13245 return result; |
13182 }; |
13246 }; |
13213 } |
13277 } |
13214 |
13278 |
13215 //////////////////////////////////// |
13279 //////////////////////////////////// |
13216 // Module Loading |
13280 // Module Loading |
13217 //////////////////////////////////// |
13281 //////////////////////////////////// |
13218 function loadModules(modulesToLoad){ |
13282 function loadModules(modulesToLoad) { |
13219 var runBlocks = [], moduleFn; |
13283 var runBlocks = [], moduleFn; |
13220 forEach(modulesToLoad, function(module) { |
13284 forEach(modulesToLoad, function(module) { |
13221 if (loadedModules.get(module)) return; |
13285 if (loadedModules.get(module)) return; |
13222 loadedModules.put(module, true); |
13286 loadedModules.put(module, true); |
13223 |
13287 |
13224 function runInvokeQueue(queue) { |
13288 function runInvokeQueue(queue) { |
13225 var i, ii; |
13289 var i, ii; |
13226 for(i = 0, ii = queue.length; i < ii; i++) { |
13290 for (i = 0, ii = queue.length; i < ii; i++) { |
13227 var invokeArgs = queue[i], |
13291 var invokeArgs = queue[i], |
13228 provider = providerInjector.get(invokeArgs[0]); |
13292 provider = providerInjector.get(invokeArgs[0]); |
13229 |
13293 |
13230 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); |
13294 provider[invokeArgs[1]].apply(provider, invokeArgs[2]); |
13231 } |
13295 } |
13267 // internal Injector |
13331 // internal Injector |
13268 //////////////////////////////////// |
13332 //////////////////////////////////// |
13269 |
13333 |
13270 function createInternalInjector(cache, factory) { |
13334 function createInternalInjector(cache, factory) { |
13271 |
13335 |
13272 function getService(serviceName) { |
13336 function getService(serviceName, caller) { |
13273 if (cache.hasOwnProperty(serviceName)) { |
13337 if (cache.hasOwnProperty(serviceName)) { |
13274 if (cache[serviceName] === INSTANTIATING) { |
13338 if (cache[serviceName] === INSTANTIATING) { |
13275 throw $injectorMinErr('cdep', 'Circular dependency found: {0}', |
13339 throw $injectorMinErr('cdep', 'Circular dependency found: {0}', |
13276 serviceName + ' <- ' + path.join(' <- ')); |
13340 serviceName + ' <- ' + path.join(' <- ')); |
13277 } |
13341 } |
13278 return cache[serviceName]; |
13342 return cache[serviceName]; |
13279 } else { |
13343 } else { |
13280 try { |
13344 try { |
13281 path.unshift(serviceName); |
13345 path.unshift(serviceName); |
13282 cache[serviceName] = INSTANTIATING; |
13346 cache[serviceName] = INSTANTIATING; |
13283 return cache[serviceName] = factory(serviceName); |
13347 return cache[serviceName] = factory(serviceName, caller); |
13284 } catch (err) { |
13348 } catch (err) { |
13285 if (cache[serviceName] === INSTANTIATING) { |
13349 if (cache[serviceName] === INSTANTIATING) { |
13286 delete cache[serviceName]; |
13350 delete cache[serviceName]; |
13287 } |
13351 } |
13288 throw err; |
13352 throw err; |
13301 var args = [], |
13365 var args = [], |
13302 $inject = annotate(fn, strictDi, serviceName), |
13366 $inject = annotate(fn, strictDi, serviceName), |
13303 length, i, |
13367 length, i, |
13304 key; |
13368 key; |
13305 |
13369 |
13306 for(i = 0, length = $inject.length; i < length; i++) { |
13370 for (i = 0, length = $inject.length; i < length; i++) { |
13307 key = $inject[i]; |
13371 key = $inject[i]; |
13308 if (typeof key !== 'string') { |
13372 if (typeof key !== 'string') { |
13309 throw $injectorMinErr('itkn', |
13373 throw $injectorMinErr('itkn', |
13310 'Incorrect injection token! Expected service name as string, got {0}', key); |
13374 'Incorrect injection token! Expected service name as string, got {0}', key); |
13311 } |
13375 } |
13312 args.push( |
13376 args.push( |
13313 locals && locals.hasOwnProperty(key) |
13377 locals && locals.hasOwnProperty(key) |
13314 ? locals[key] |
13378 ? locals[key] |
13315 : getService(key) |
13379 : getService(key, serviceName) |
13316 ); |
13380 ); |
13317 } |
13381 } |
13318 if (isArray(fn)) { |
13382 if (isArray(fn)) { |
13319 fn = fn[length]; |
13383 fn = fn[length]; |
13320 } |
13384 } |
13323 // #5388 |
13387 // #5388 |
13324 return fn.apply(self, args); |
13388 return fn.apply(self, args); |
13325 } |
13389 } |
13326 |
13390 |
13327 function instantiate(Type, locals, serviceName) { |
13391 function instantiate(Type, locals, serviceName) { |
13328 var Constructor = function() {}, |
|
13329 instance, returnedValue; |
|
13330 |
|
13331 // Check if Type is annotated and use just the given function at n-1 as parameter |
13392 // Check if Type is annotated and use just the given function at n-1 as parameter |
13332 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); |
13393 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); |
13333 Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; |
13394 // Object creation: http://jsperf.com/create-constructor/2 |
13334 instance = new Constructor(); |
13395 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype); |
13335 returnedValue = invoke(Type, instance, locals, serviceName); |
13396 var returnedValue = invoke(Type, instance, locals, serviceName); |
13336 |
13397 |
13337 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; |
13398 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; |
13338 } |
13399 } |
13339 |
13400 |
13340 return { |
13401 return { |
13350 } |
13411 } |
13351 |
13412 |
13352 createInjector.$$annotate = annotate; |
13413 createInjector.$$annotate = annotate; |
13353 |
13414 |
13354 /** |
13415 /** |
13355 * @ngdoc service |
13416 * @ngdoc provider |
13356 * @name $anchorScroll |
13417 * @name $anchorScrollProvider |
13357 * @kind function |
|
13358 * @requires $window |
|
13359 * @requires $location |
|
13360 * @requires $rootScope |
|
13361 * |
13418 * |
13362 * @description |
13419 * @description |
13363 * When called, it checks current value of `$location.hash()` and scrolls to the related element, |
13420 * Use `$anchorScrollProvider` to disable automatic scrolling whenever |
13364 * according to rules specified in |
13421 * {@link ng.$location#hash $location.hash()} changes. |
13365 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). |
|
13366 * |
|
13367 * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. |
|
13368 * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. |
|
13369 * |
|
13370 * @example |
|
13371 <example module="anchorScrollExample"> |
|
13372 <file name="index.html"> |
|
13373 <div id="scrollArea" ng-controller="ScrollController"> |
|
13374 <a ng-click="gotoBottom()">Go to bottom</a> |
|
13375 <a id="bottom"></a> You're at the bottom! |
|
13376 </div> |
|
13377 </file> |
|
13378 <file name="script.js"> |
|
13379 angular.module('anchorScrollExample', []) |
|
13380 .controller('ScrollController', ['$scope', '$location', '$anchorScroll', |
|
13381 function ($scope, $location, $anchorScroll) { |
|
13382 $scope.gotoBottom = function() { |
|
13383 // set the location.hash to the id of |
|
13384 // the element you wish to scroll to. |
|
13385 $location.hash('bottom'); |
|
13386 |
|
13387 // call $anchorScroll() |
|
13388 $anchorScroll(); |
|
13389 }; |
|
13390 }]); |
|
13391 </file> |
|
13392 <file name="style.css"> |
|
13393 #scrollArea { |
|
13394 height: 350px; |
|
13395 overflow: auto; |
|
13396 } |
|
13397 |
|
13398 #bottom { |
|
13399 display: block; |
|
13400 margin-top: 2000px; |
|
13401 } |
|
13402 </file> |
|
13403 </example> |
|
13404 */ |
13422 */ |
13405 function $AnchorScrollProvider() { |
13423 function $AnchorScrollProvider() { |
13406 |
13424 |
13407 var autoScrollingEnabled = true; |
13425 var autoScrollingEnabled = true; |
13408 |
13426 |
13427 /** |
|
13428 * @ngdoc method |
|
13429 * @name $anchorScrollProvider#disableAutoScrolling |
|
13430 * |
|
13431 * @description |
|
13432 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to |
|
13433 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> |
|
13434 * Use this method to disable automatic scrolling. |
|
13435 * |
|
13436 * If automatic scrolling is disabled, one must explicitly call |
|
13437 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the |
|
13438 * current hash. |
|
13439 */ |
|
13409 this.disableAutoScrolling = function() { |
13440 this.disableAutoScrolling = function() { |
13410 autoScrollingEnabled = false; |
13441 autoScrollingEnabled = false; |
13411 }; |
13442 }; |
13412 |
13443 |
13444 /** |
|
13445 * @ngdoc service |
|
13446 * @name $anchorScroll |
|
13447 * @kind function |
|
13448 * @requires $window |
|
13449 * @requires $location |
|
13450 * @requires $rootScope |
|
13451 * |
|
13452 * @description |
|
13453 * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and |
|
13454 * scrolls to the related element, according to the rules specified in the |
|
13455 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). |
|
13456 * |
|
13457 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to |
|
13458 * match any anchor whenever it changes. This can be disabled by calling |
|
13459 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. |
|
13460 * |
|
13461 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a |
|
13462 * vertical scroll-offset (either fixed or dynamic). |
|
13463 * |
|
13464 * @property {(number|function|jqLite)} yOffset |
|
13465 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed |
|
13466 * positioned elements at the top of the page, such as navbars, headers etc. |
|
13467 * |
|
13468 * `yOffset` can be specified in various ways: |
|
13469 * - **number**: A fixed number of pixels to be used as offset.<br /><br /> |
|
13470 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return |
|
13471 * a number representing the offset (in pixels).<br /><br /> |
|
13472 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from |
|
13473 * the top of the page to the element's bottom will be used as offset.<br /> |
|
13474 * **Note**: The element will be taken into account only as long as its `position` is set to |
|
13475 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust |
|
13476 * their height and/or positioning according to the viewport's size. |
|
13477 * |
|
13478 * <br /> |
|
13479 * <div class="alert alert-warning"> |
|
13480 * In order for `yOffset` to work properly, scrolling should take place on the document's root and |
|
13481 * not some child element. |
|
13482 * </div> |
|
13483 * |
|
13484 * @example |
|
13485 <example module="anchorScrollExample"> |
|
13486 <file name="index.html"> |
|
13487 <div id="scrollArea" ng-controller="ScrollController"> |
|
13488 <a ng-click="gotoBottom()">Go to bottom</a> |
|
13489 <a id="bottom"></a> You're at the bottom! |
|
13490 </div> |
|
13491 </file> |
|
13492 <file name="script.js"> |
|
13493 angular.module('anchorScrollExample', []) |
|
13494 .controller('ScrollController', ['$scope', '$location', '$anchorScroll', |
|
13495 function ($scope, $location, $anchorScroll) { |
|
13496 $scope.gotoBottom = function() { |
|
13497 // set the location.hash to the id of |
|
13498 // the element you wish to scroll to. |
|
13499 $location.hash('bottom'); |
|
13500 |
|
13501 // call $anchorScroll() |
|
13502 $anchorScroll(); |
|
13503 }; |
|
13504 }]); |
|
13505 </file> |
|
13506 <file name="style.css"> |
|
13507 #scrollArea { |
|
13508 height: 280px; |
|
13509 overflow: auto; |
|
13510 } |
|
13511 |
|
13512 #bottom { |
|
13513 display: block; |
|
13514 margin-top: 2000px; |
|
13515 } |
|
13516 </file> |
|
13517 </example> |
|
13518 * |
|
13519 * <hr /> |
|
13520 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). |
|
13521 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. |
|
13522 * |
|
13523 * @example |
|
13524 <example module="anchorScrollOffsetExample"> |
|
13525 <file name="index.html"> |
|
13526 <div class="fixed-header" ng-controller="headerCtrl"> |
|
13527 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]"> |
|
13528 Go to anchor {{x}} |
|
13529 </a> |
|
13530 </div> |
|
13531 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]"> |
|
13532 Anchor {{x}} of 5 |
|
13533 </div> |
|
13534 </file> |
|
13535 <file name="script.js"> |
|
13536 angular.module('anchorScrollOffsetExample', []) |
|
13537 .run(['$anchorScroll', function($anchorScroll) { |
|
13538 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels |
|
13539 }]) |
|
13540 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', |
|
13541 function ($anchorScroll, $location, $scope) { |
|
13542 $scope.gotoAnchor = function(x) { |
|
13543 var newHash = 'anchor' + x; |
|
13544 if ($location.hash() !== newHash) { |
|
13545 // set the $location.hash to `newHash` and |
|
13546 // $anchorScroll will automatically scroll to it |
|
13547 $location.hash('anchor' + x); |
|
13548 } else { |
|
13549 // call $anchorScroll() explicitly, |
|
13550 // since $location.hash hasn't changed |
|
13551 $anchorScroll(); |
|
13552 } |
|
13553 }; |
|
13554 } |
|
13555 ]); |
|
13556 </file> |
|
13557 <file name="style.css"> |
|
13558 body { |
|
13559 padding-top: 50px; |
|
13560 } |
|
13561 |
|
13562 .anchor { |
|
13563 border: 2px dashed DarkOrchid; |
|
13564 padding: 10px 10px 200px 10px; |
|
13565 } |
|
13566 |
|
13567 .fixed-header { |
|
13568 background-color: rgba(0, 0, 0, 0.2); |
|
13569 height: 50px; |
|
13570 position: fixed; |
|
13571 top: 0; left: 0; right: 0; |
|
13572 } |
|
13573 |
|
13574 .fixed-header > a { |
|
13575 display: inline-block; |
|
13576 margin: 5px 15px; |
|
13577 } |
|
13578 </file> |
|
13579 </example> |
|
13580 */ |
|
13413 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { |
13581 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { |
13414 var document = $window.document; |
13582 var document = $window.document; |
13415 |
13583 |
13416 // helper function to get first anchor from a NodeList |
13584 // Helper function to get first anchor from a NodeList |
13417 // can't use filter.filter, as it accepts only instances of Array |
13585 // (using `Array#some()` instead of `angular#forEach()` since it's more performant |
13418 // and IE can't convert NodeList to an array using [].slice |
13586 // and working in all supported browsers.) |
13419 // TODO(vojta): use filter if we change it to accept lists as well |
|
13420 function getFirstAnchor(list) { |
13587 function getFirstAnchor(list) { |
13421 var result = null; |
13588 var result = null; |
13422 forEach(list, function(element) { |
13589 Array.prototype.some.call(list, function(element) { |
13423 if (!result && nodeName_(element) === 'a') result = element; |
13590 if (nodeName_(element) === 'a') { |
13591 result = element; |
|
13592 return true; |
|
13593 } |
|
13424 }); |
13594 }); |
13425 return result; |
13595 return result; |
13426 } |
13596 } |
13427 |
13597 |
13598 function getYOffset() { |
|
13599 |
|
13600 var offset = scroll.yOffset; |
|
13601 |
|
13602 if (isFunction(offset)) { |
|
13603 offset = offset(); |
|
13604 } else if (isElement(offset)) { |
|
13605 var elem = offset[0]; |
|
13606 var style = $window.getComputedStyle(elem); |
|
13607 if (style.position !== 'fixed') { |
|
13608 offset = 0; |
|
13609 } else { |
|
13610 offset = elem.getBoundingClientRect().bottom; |
|
13611 } |
|
13612 } else if (!isNumber(offset)) { |
|
13613 offset = 0; |
|
13614 } |
|
13615 |
|
13616 return offset; |
|
13617 } |
|
13618 |
|
13619 function scrollTo(elem) { |
|
13620 if (elem) { |
|
13621 elem.scrollIntoView(); |
|
13622 |
|
13623 var offset = getYOffset(); |
|
13624 |
|
13625 if (offset) { |
|
13626 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. |
|
13627 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the |
|
13628 // top of the viewport. |
|
13629 // |
|
13630 // IF the number of pixels from the top of `elem` to the end of the page's content is less |
|
13631 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some |
|
13632 // way down the page. |
|
13633 // |
|
13634 // This is often the case for elements near the bottom of the page. |
|
13635 // |
|
13636 // In such cases we do not need to scroll the whole `offset` up, just the difference between |
|
13637 // the top of the element and the offset, which is enough to align the top of `elem` at the |
|
13638 // desired position. |
|
13639 var elemTop = elem.getBoundingClientRect().top; |
|
13640 $window.scrollBy(0, elemTop - offset); |
|
13641 } |
|
13642 } else { |
|
13643 $window.scrollTo(0, 0); |
|
13644 } |
|
13645 } |
|
13646 |
|
13428 function scroll() { |
13647 function scroll() { |
13429 var hash = $location.hash(), elm; |
13648 var hash = $location.hash(), elm; |
13430 |
13649 |
13431 // empty hash, scroll to the top of the page |
13650 // empty hash, scroll to the top of the page |
13432 if (!hash) $window.scrollTo(0, 0); |
13651 if (!hash) scrollTo(null); |
13433 |
13652 |
13434 // element with given id |
13653 // element with given id |
13435 else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); |
13654 else if ((elm = document.getElementById(hash))) scrollTo(elm); |
13436 |
13655 |
13437 // first anchor with given name :-D |
13656 // first anchor with given name :-D |
13438 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); |
13657 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); |
13439 |
13658 |
13440 // no element and hash == 'top', scroll to the top of the page |
13659 // no element and hash == 'top', scroll to the top of the page |
13441 else if (hash === 'top') $window.scrollTo(0, 0); |
13660 else if (hash === 'top') scrollTo(null); |
13442 } |
13661 } |
13443 |
13662 |
13444 // does not scroll when user clicks on anchor link that is currently on |
13663 // does not scroll when user clicks on anchor link that is currently on |
13445 // (no url change, no $location.hash() change), browser native does scroll |
13664 // (no url change, no $location.hash() change), browser native does scroll |
13446 if (autoScrollingEnabled) { |
13665 if (autoScrollingEnabled) { |
13447 $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, |
13666 $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, |
13448 function autoScrollWatchAction(newVal, oldVal) { |
13667 function autoScrollWatchAction(newVal, oldVal) { |
13449 // skip the initial scroll if $location.hash is empty |
13668 // skip the initial scroll if $location.hash is empty |
13450 if (newVal === oldVal && newVal === '') return; |
13669 if (newVal === oldVal && newVal === '') return; |
13451 |
13670 |
13452 $rootScope.$evalAsync(scroll); |
13671 jqLiteDocumentLoaded(function() { |
13672 $rootScope.$evalAsync(scroll); |
|
13673 }); |
|
13453 }); |
13674 }); |
13454 } |
13675 } |
13455 |
13676 |
13456 return scroll; |
13677 return scroll; |
13457 }]; |
13678 }]; |
13530 * for low-powered devices as well as applications containing a lot of structural operations. |
13751 * for low-powered devices as well as applications containing a lot of structural operations. |
13531 * @param {RegExp=} expression The className expression which will be checked against all animations |
13752 * @param {RegExp=} expression The className expression which will be checked against all animations |
13532 * @return {RegExp} The current CSS className expression value. If null then there is no expression value |
13753 * @return {RegExp} The current CSS className expression value. If null then there is no expression value |
13533 */ |
13754 */ |
13534 this.classNameFilter = function(expression) { |
13755 this.classNameFilter = function(expression) { |
13535 if(arguments.length === 1) { |
13756 if (arguments.length === 1) { |
13536 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; |
13757 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; |
13537 } |
13758 } |
13538 return this.$$classNameFilter; |
13759 return this.$$classNameFilter; |
13539 }; |
13760 }; |
13540 |
13761 |
13555 }); |
13776 }); |
13556 |
13777 |
13557 return defer.promise; |
13778 return defer.promise; |
13558 } |
13779 } |
13559 |
13780 |
13560 function resolveElementClasses(element, cache) { |
13781 function resolveElementClasses(element, classes) { |
13561 var toAdd = [], toRemove = []; |
13782 var toAdd = [], toRemove = []; |
13562 |
13783 |
13563 var hasClasses = createMap(); |
13784 var hasClasses = createMap(); |
13564 forEach((element.attr('class') || '').split(/\s+/), function(className) { |
13785 forEach((element.attr('class') || '').split(/\s+/), function(className) { |
13565 hasClasses[className] = true; |
13786 hasClasses[className] = true; |
13566 }); |
13787 }); |
13567 |
13788 |
13568 forEach(cache.classes, function(status, className) { |
13789 forEach(classes, function(status, className) { |
13569 var hasClass = hasClasses[className]; |
13790 var hasClass = hasClasses[className]; |
13570 |
13791 |
13571 // If the most recent class manipulation (via $animate) was to remove the class, and the |
13792 // If the most recent class manipulation (via $animate) was to remove the class, and the |
13572 // element currently has the class, the class is scheduled for removal. Otherwise, if |
13793 // element currently has the class, the class is scheduled for removal. Otherwise, if |
13573 // the most recent class manipulation (via $animate) was to add the class, and the |
13794 // the most recent class manipulation (via $animate) was to add the class, and the |
13577 } else if (status === true && !hasClass) { |
13798 } else if (status === true && !hasClass) { |
13578 toAdd.push(className); |
13799 toAdd.push(className); |
13579 } |
13800 } |
13580 }); |
13801 }); |
13581 |
13802 |
13582 return (toAdd.length + toRemove.length) > 0 && [toAdd.length && toAdd, toRemove.length && toRemove]; |
13803 return (toAdd.length + toRemove.length) > 0 && |
13804 [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null]; |
|
13583 } |
13805 } |
13584 |
13806 |
13585 function cachedClassManipulation(cache, classes, op) { |
13807 function cachedClassManipulation(cache, classes, op) { |
13586 for (var i=0, ii = classes.length; i < ii; ++i) { |
13808 for (var i=0, ii = classes.length; i < ii; ++i) { |
13587 var className = classes[i]; |
13809 var className = classes[i]; |
13597 currentDefer.resolve(); |
13819 currentDefer.resolve(); |
13598 currentDefer = null; |
13820 currentDefer = null; |
13599 }); |
13821 }); |
13600 } |
13822 } |
13601 return currentDefer.promise; |
13823 return currentDefer.promise; |
13824 } |
|
13825 |
|
13826 function applyStyles(element, options) { |
|
13827 if (angular.isObject(options)) { |
|
13828 var styles = extend(options.from || {}, options.to || {}); |
|
13829 element.css(styles); |
|
13830 } |
|
13602 } |
13831 } |
13603 |
13832 |
13604 /** |
13833 /** |
13605 * |
13834 * |
13606 * @ngdoc service |
13835 * @ngdoc service |
13617 * To learn more about enabling animation support, click here to visit the {@link ngAnimate |
13846 * To learn more about enabling animation support, click here to visit the {@link ngAnimate |
13618 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service |
13847 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service |
13619 * page}. |
13848 * page}. |
13620 */ |
13849 */ |
13621 return { |
13850 return { |
13851 animate: function(element, from, to) { |
|
13852 applyStyles(element, { from: from, to: to }); |
|
13853 return asyncPromise(); |
|
13854 }, |
|
13622 |
13855 |
13623 /** |
13856 /** |
13624 * |
13857 * |
13625 * @ngdoc method |
13858 * @ngdoc method |
13626 * @name $animate#enter |
13859 * @name $animate#enter |
13631 * @param {DOMElement} element the element which will be inserted into the DOM |
13864 * @param {DOMElement} element the element which will be inserted into the DOM |
13632 * @param {DOMElement} parent the parent element which will append the element as |
13865 * @param {DOMElement} parent the parent element which will append the element as |
13633 * a child (if the after element is not present) |
13866 * a child (if the after element is not present) |
13634 * @param {DOMElement} after the sibling element which will append the element |
13867 * @param {DOMElement} after the sibling element which will append the element |
13635 * after itself |
13868 * after itself |
13869 * @param {object=} options an optional collection of styles that will be applied to the element. |
|
13636 * @return {Promise} the animation callback promise |
13870 * @return {Promise} the animation callback promise |
13637 */ |
13871 */ |
13638 enter : function(element, parent, after) { |
13872 enter: function(element, parent, after, options) { |
13873 applyStyles(element, options); |
|
13639 after ? after.after(element) |
13874 after ? after.after(element) |
13640 : parent.prepend(element); |
13875 : parent.prepend(element); |
13641 return asyncPromise(); |
13876 return asyncPromise(); |
13642 }, |
13877 }, |
13643 |
13878 |
13647 * @name $animate#leave |
13882 * @name $animate#leave |
13648 * @kind function |
13883 * @kind function |
13649 * @description Removes the element from the DOM. When the function is called a promise |
13884 * @description Removes the element from the DOM. When the function is called a promise |
13650 * is returned that will be resolved at a later time. |
13885 * is returned that will be resolved at a later time. |
13651 * @param {DOMElement} element the element which will be removed from the DOM |
13886 * @param {DOMElement} element the element which will be removed from the DOM |
13887 * @param {object=} options an optional collection of options that will be applied to the element. |
|
13652 * @return {Promise} the animation callback promise |
13888 * @return {Promise} the animation callback promise |
13653 */ |
13889 */ |
13654 leave : function(element) { |
13890 leave: function(element, options) { |
13655 element.remove(); |
13891 element.remove(); |
13656 return asyncPromise(); |
13892 return asyncPromise(); |
13657 }, |
13893 }, |
13658 |
13894 |
13659 /** |
13895 /** |
13669 * DOM |
13905 * DOM |
13670 * @param {DOMElement} parent the parent element where the element will be |
13906 * @param {DOMElement} parent the parent element where the element will be |
13671 * inserted into (if the after element is not present) |
13907 * inserted into (if the after element is not present) |
13672 * @param {DOMElement} after the sibling element where the element will be |
13908 * @param {DOMElement} after the sibling element where the element will be |
13673 * positioned next to |
13909 * positioned next to |
13910 * @param {object=} options an optional collection of options that will be applied to the element. |
|
13674 * @return {Promise} the animation callback promise |
13911 * @return {Promise} the animation callback promise |
13675 */ |
13912 */ |
13676 move : function(element, parent, after) { |
13913 move: function(element, parent, after, options) { |
13677 // Do not remove element before insert. Removing will cause data associated with the |
13914 // Do not remove element before insert. Removing will cause data associated with the |
13678 // element to be dropped. Insert will implicitly do the remove. |
13915 // element to be dropped. Insert will implicitly do the remove. |
13679 return this.enter(element, parent, after); |
13916 return this.enter(element, parent, after, options); |
13680 }, |
13917 }, |
13681 |
13918 |
13682 /** |
13919 /** |
13683 * |
13920 * |
13684 * @ngdoc method |
13921 * @ngdoc method |
13687 * @description Adds the provided className CSS class value to the provided element. |
13924 * @description Adds the provided className CSS class value to the provided element. |
13688 * When the function is called a promise is returned that will be resolved at a later time. |
13925 * When the function is called a promise is returned that will be resolved at a later time. |
13689 * @param {DOMElement} element the element which will have the className value |
13926 * @param {DOMElement} element the element which will have the className value |
13690 * added to it |
13927 * added to it |
13691 * @param {string} className the CSS class which will be added to the element |
13928 * @param {string} className the CSS class which will be added to the element |
13929 * @param {object=} options an optional collection of options that will be applied to the element. |
|
13692 * @return {Promise} the animation callback promise |
13930 * @return {Promise} the animation callback promise |
13693 */ |
13931 */ |
13694 addClass : function(element, className) { |
13932 addClass: function(element, className, options) { |
13695 return this.setClass(element, className, []); |
13933 return this.setClass(element, className, [], options); |
13696 }, |
13934 }, |
13697 |
13935 |
13698 $$addClassImmediately : function addClassImmediately(element, className) { |
13936 $$addClassImmediately: function(element, className, options) { |
13699 element = jqLite(element); |
13937 element = jqLite(element); |
13700 className = !isString(className) |
13938 className = !isString(className) |
13701 ? (isArray(className) ? className.join(' ') : '') |
13939 ? (isArray(className) ? className.join(' ') : '') |
13702 : className; |
13940 : className; |
13703 forEach(element, function (element) { |
13941 forEach(element, function(element) { |
13704 jqLiteAddClass(element, className); |
13942 jqLiteAddClass(element, className); |
13705 }); |
13943 }); |
13944 applyStyles(element, options); |
|
13945 return asyncPromise(); |
|
13706 }, |
13946 }, |
13707 |
13947 |
13708 /** |
13948 /** |
13709 * |
13949 * |
13710 * @ngdoc method |
13950 * @ngdoc method |
13713 * @description Removes the provided className CSS class value from the provided element. |
13953 * @description Removes the provided className CSS class value from the provided element. |
13714 * When the function is called a promise is returned that will be resolved at a later time. |
13954 * When the function is called a promise is returned that will be resolved at a later time. |
13715 * @param {DOMElement} element the element which will have the className value |
13955 * @param {DOMElement} element the element which will have the className value |
13716 * removed from it |
13956 * removed from it |
13717 * @param {string} className the CSS class which will be removed from the element |
13957 * @param {string} className the CSS class which will be removed from the element |
13958 * @param {object=} options an optional collection of options that will be applied to the element. |
|
13718 * @return {Promise} the animation callback promise |
13959 * @return {Promise} the animation callback promise |
13719 */ |
13960 */ |
13720 removeClass : function(element, className) { |
13961 removeClass: function(element, className, options) { |
13721 return this.setClass(element, [], className); |
13962 return this.setClass(element, [], className, options); |
13722 }, |
13963 }, |
13723 |
13964 |
13724 $$removeClassImmediately : function removeClassImmediately(element, className) { |
13965 $$removeClassImmediately: function(element, className, options) { |
13725 element = jqLite(element); |
13966 element = jqLite(element); |
13726 className = !isString(className) |
13967 className = !isString(className) |
13727 ? (isArray(className) ? className.join(' ') : '') |
13968 ? (isArray(className) ? className.join(' ') : '') |
13728 : className; |
13969 : className; |
13729 forEach(element, function (element) { |
13970 forEach(element, function(element) { |
13730 jqLiteRemoveClass(element, className); |
13971 jqLiteRemoveClass(element, className); |
13731 }); |
13972 }); |
13973 applyStyles(element, options); |
|
13732 return asyncPromise(); |
13974 return asyncPromise(); |
13733 }, |
13975 }, |
13734 |
13976 |
13735 /** |
13977 /** |
13736 * |
13978 * |
13741 * When the function is called a promise is returned that will be resolved at a later time. |
13983 * When the function is called a promise is returned that will be resolved at a later time. |
13742 * @param {DOMElement} element the element which will have its CSS classes changed |
13984 * @param {DOMElement} element the element which will have its CSS classes changed |
13743 * removed from it |
13985 * removed from it |
13744 * @param {string} add the CSS classes which will be added to the element |
13986 * @param {string} add the CSS classes which will be added to the element |
13745 * @param {string} remove the CSS class which will be removed from the element |
13987 * @param {string} remove the CSS class which will be removed from the element |
13988 * @param {object=} options an optional collection of options that will be applied to the element. |
|
13746 * @return {Promise} the animation callback promise |
13989 * @return {Promise} the animation callback promise |
13747 */ |
13990 */ |
13748 setClass : function(element, add, remove, runSynchronously) { |
13991 setClass: function(element, add, remove, options) { |
13749 var self = this; |
13992 var self = this; |
13750 var STORAGE_KEY = '$$animateClasses'; |
13993 var STORAGE_KEY = '$$animateClasses'; |
13751 var createdCache = false; |
13994 var createdCache = false; |
13752 element = jqLite(element); |
13995 element = jqLite(element); |
13753 |
13996 |
13754 if (runSynchronously) { |
|
13755 // TODO(@caitp/@matsko): Remove undocumented `runSynchronously` parameter, and always |
|
13756 // perform DOM manipulation asynchronously or in postDigest. |
|
13757 self.$$addClassImmediately(element, add); |
|
13758 self.$$removeClassImmediately(element, remove); |
|
13759 return asyncPromise(); |
|
13760 } |
|
13761 |
|
13762 var cache = element.data(STORAGE_KEY); |
13997 var cache = element.data(STORAGE_KEY); |
13763 if (!cache) { |
13998 if (!cache) { |
13764 cache = { |
13999 cache = { |
13765 classes: {} |
14000 classes: {}, |
14001 options: options |
|
13766 }; |
14002 }; |
13767 createdCache = true; |
14003 createdCache = true; |
14004 } else if (options && cache.options) { |
|
14005 cache.options = angular.extend(cache.options || {}, options); |
|
13768 } |
14006 } |
13769 |
14007 |
13770 var classes = cache.classes; |
14008 var classes = cache.classes; |
13771 |
14009 |
13772 add = isArray(add) ? add : add.split(' '); |
14010 add = isArray(add) ? add : add.split(' '); |
13777 if (createdCache) { |
14015 if (createdCache) { |
13778 cache.promise = runAnimationPostDigest(function(done) { |
14016 cache.promise = runAnimationPostDigest(function(done) { |
13779 var cache = element.data(STORAGE_KEY); |
14017 var cache = element.data(STORAGE_KEY); |
13780 element.removeData(STORAGE_KEY); |
14018 element.removeData(STORAGE_KEY); |
13781 |
14019 |
13782 var classes = cache && resolveElementClasses(element, cache); |
14020 // in the event that the element is removed before postDigest |
13783 |
14021 // is run then the cache will be undefined and there will be |
13784 if (classes) { |
14022 // no need anymore to add or remove and of the element classes |
13785 if (classes[0]) self.$$addClassImmediately(element, classes[0]); |
14023 if (cache) { |
13786 if (classes[1]) self.$$removeClassImmediately(element, classes[1]); |
14024 var classes = resolveElementClasses(element, cache.classes); |
14025 if (classes) { |
|
14026 self.$$setClassImmediately(element, classes[0], classes[1], cache.options); |
|
14027 } |
|
13787 } |
14028 } |
13788 |
14029 |
13789 done(); |
14030 done(); |
13790 }); |
14031 }); |
13791 element.data(STORAGE_KEY, cache); |
14032 element.data(STORAGE_KEY, cache); |
13792 } |
14033 } |
13793 |
14034 |
13794 return cache.promise; |
14035 return cache.promise; |
13795 }, |
14036 }, |
13796 |
14037 |
13797 enabled : noop, |
14038 $$setClassImmediately: function(element, add, remove, options) { |
13798 cancel : noop |
14039 add && this.$$addClassImmediately(element, add); |
14040 remove && this.$$removeClassImmediately(element, remove); |
|
14041 applyStyles(element, options); |
|
14042 return asyncPromise(); |
|
14043 }, |
|
14044 |
|
14045 enabled: noop, |
|
14046 cancel: noop |
|
13799 }; |
14047 }; |
13800 }]; |
14048 }]; |
13801 }]; |
14049 }]; |
13802 |
14050 |
13803 function $$AsyncCallbackProvider(){ |
14051 function $$AsyncCallbackProvider() { |
13804 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { |
14052 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { |
13805 return $$rAF.supported |
14053 return $$rAF.supported |
13806 ? function(fn) { return $$rAF(fn); } |
14054 ? function(fn) { return $$rAF(fn); } |
13807 : function(fn) { |
14055 : function(fn) { |
13808 return $timeout(fn, 0, false); |
14056 return $timeout(fn, 0, false); |
13828 * the real browser apis. |
14076 * the real browser apis. |
13829 */ |
14077 */ |
13830 /** |
14078 /** |
13831 * @param {object} window The global window object. |
14079 * @param {object} window The global window object. |
13832 * @param {object} document jQuery wrapped document. |
14080 * @param {object} document jQuery wrapped document. |
13833 * @param {function()} XHR XMLHttpRequest constructor. |
14081 * @param {object} $log window.console or an object with the same interface. |
13834 * @param {object} $log console.log or an object with the same interface. |
|
13835 * @param {object} $sniffer $sniffer service |
14082 * @param {object} $sniffer $sniffer service |
13836 */ |
14083 */ |
13837 function Browser(window, document, $log, $sniffer) { |
14084 function Browser(window, document, $log, $sniffer) { |
13838 var self = this, |
14085 var self = this, |
13839 rawDocument = document[0], |
14086 rawDocument = document[0], |
13860 try { |
14107 try { |
13861 fn.apply(null, sliceArgs(arguments, 1)); |
14108 fn.apply(null, sliceArgs(arguments, 1)); |
13862 } finally { |
14109 } finally { |
13863 outstandingRequestCount--; |
14110 outstandingRequestCount--; |
13864 if (outstandingRequestCount === 0) { |
14111 if (outstandingRequestCount === 0) { |
13865 while(outstandingRequestCallbacks.length) { |
14112 while (outstandingRequestCallbacks.length) { |
13866 try { |
14113 try { |
13867 outstandingRequestCallbacks.pop()(); |
14114 outstandingRequestCallbacks.pop()(); |
13868 } catch (e) { |
14115 } catch (e) { |
13869 $log.error(e); |
14116 $log.error(e); |
13870 } |
14117 } |
13871 } |
14118 } |
13872 } |
14119 } |
13873 } |
14120 } |
14121 } |
|
14122 |
|
14123 function getHash(url) { |
|
14124 var index = url.indexOf('#'); |
|
14125 return index === -1 ? '' : url.substr(index + 1); |
|
13874 } |
14126 } |
13875 |
14127 |
13876 /** |
14128 /** |
13877 * @private |
14129 * @private |
13878 * Note: this method is used only by scenario runner |
14130 * Note: this method is used only by scenario runner |
13881 */ |
14133 */ |
13882 self.notifyWhenNoOutstandingRequests = function(callback) { |
14134 self.notifyWhenNoOutstandingRequests = function(callback) { |
13883 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire |
14135 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire |
13884 // at some deterministic time in respect to the test runner's actions. Leaving things up to the |
14136 // at some deterministic time in respect to the test runner's actions. Leaving things up to the |
13885 // regular poller would result in flaky tests. |
14137 // regular poller would result in flaky tests. |
13886 forEach(pollFns, function(pollFn){ pollFn(); }); |
14138 forEach(pollFns, function(pollFn) { pollFn(); }); |
13887 |
14139 |
13888 if (outstandingRequestCount === 0) { |
14140 if (outstandingRequestCount === 0) { |
13889 callback(); |
14141 callback(); |
13890 } else { |
14142 } else { |
13891 outstandingRequestCallbacks.push(callback); |
14143 outstandingRequestCallbacks.push(callback); |
13923 * Configures the poller to run in the specified intervals, using the specified |
14175 * Configures the poller to run in the specified intervals, using the specified |
13924 * setTimeout fn and kicks it off. |
14176 * setTimeout fn and kicks it off. |
13925 */ |
14177 */ |
13926 function startPoller(interval, setTimeout) { |
14178 function startPoller(interval, setTimeout) { |
13927 (function check() { |
14179 (function check() { |
13928 forEach(pollFns, function(pollFn){ pollFn(); }); |
14180 forEach(pollFns, function(pollFn) { pollFn(); }); |
13929 pollTimeout = setTimeout(check, interval); |
14181 pollTimeout = setTimeout(check, interval); |
13930 })(); |
14182 })(); |
13931 } |
14183 } |
13932 |
14184 |
13933 ////////////////////////////////////////////////////////////// |
14185 ////////////////////////////////////////////////////////////// |
13934 // URL API |
14186 // URL API |
13935 ////////////////////////////////////////////////////////////// |
14187 ////////////////////////////////////////////////////////////// |
13936 |
14188 |
13937 var lastBrowserUrl = location.href, |
14189 var cachedState, lastHistoryState, |
13938 lastHistoryState = history.state, |
14190 lastBrowserUrl = location.href, |
13939 baseElement = document.find('base'), |
14191 baseElement = document.find('base'), |
13940 reloadLocation = null; |
14192 reloadLocation = null; |
14193 |
|
14194 cacheState(); |
|
14195 lastHistoryState = cachedState; |
|
13941 |
14196 |
13942 /** |
14197 /** |
13943 * @name $browser#url |
14198 * @name $browser#url |
13944 * |
14199 * |
13945 * @description |
14200 * @description |
13971 if (location !== window.location) location = window.location; |
14226 if (location !== window.location) location = window.location; |
13972 if (history !== window.history) history = window.history; |
14227 if (history !== window.history) history = window.history; |
13973 |
14228 |
13974 // setter |
14229 // setter |
13975 if (url) { |
14230 if (url) { |
14231 var sameState = lastHistoryState === state; |
|
14232 |
|
13976 // Don't change anything if previous and current URLs and states match. This also prevents |
14233 // Don't change anything if previous and current URLs and states match. This also prevents |
13977 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. |
14234 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. |
13978 // See https://github.com/angular/angular.js/commit/ffb2701 |
14235 // See https://github.com/angular/angular.js/commit/ffb2701 |
13979 if (lastBrowserUrl === url && (!$sniffer.history || history.state === state)) { |
14236 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { |
13980 return; |
14237 return self; |
13981 } |
14238 } |
13982 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); |
14239 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); |
13983 lastBrowserUrl = url; |
14240 lastBrowserUrl = url; |
14241 lastHistoryState = state; |
|
13984 // Don't use history API if only the hash changed |
14242 // Don't use history API if only the hash changed |
13985 // due to a bug in IE10/IE11 which leads |
14243 // due to a bug in IE10/IE11 which leads |
13986 // to not firing a `hashchange` nor `popstate` event |
14244 // to not firing a `hashchange` nor `popstate` event |
13987 // in some cases (see #9143). |
14245 // in some cases (see #9143). |
13988 if ($sniffer.history && (!sameBase || history.state !== state)) { |
14246 if ($sniffer.history && (!sameBase || !sameState)) { |
13989 history[replace ? 'replaceState' : 'pushState'](state, '', url); |
14247 history[replace ? 'replaceState' : 'pushState'](state, '', url); |
13990 lastHistoryState = history.state; |
14248 cacheState(); |
14249 // Do the assignment again so that those two variables are referentially identical. |
|
14250 lastHistoryState = cachedState; |
|
13991 } else { |
14251 } else { |
13992 if (!sameBase) { |
14252 if (!sameBase) { |
13993 reloadLocation = url; |
14253 reloadLocation = url; |
13994 } |
14254 } |
13995 if (replace) { |
14255 if (replace) { |
13996 location.replace(url); |
14256 location.replace(url); |
14257 } else if (!sameBase) { |
|
14258 location.href = url; |
|
13997 } else { |
14259 } else { |
13998 location.href = url; |
14260 location.hash = getHash(url); |
13999 } |
14261 } |
14000 } |
14262 } |
14001 return self; |
14263 return self; |
14002 // getter |
14264 // getter |
14003 } else { |
14265 } else { |
14017 * Return history.state or null if history.state is undefined. |
14279 * Return history.state or null if history.state is undefined. |
14018 * |
14280 * |
14019 * @returns {object} state |
14281 * @returns {object} state |
14020 */ |
14282 */ |
14021 self.state = function() { |
14283 self.state = function() { |
14022 return isUndefined(history.state) ? null : history.state; |
14284 return cachedState; |
14023 }; |
14285 }; |
14024 |
14286 |
14025 var urlChangeListeners = [], |
14287 var urlChangeListeners = [], |
14026 urlChangeInit = false; |
14288 urlChangeInit = false; |
14027 |
14289 |
14290 function cacheStateAndFireUrlChange() { |
|
14291 cacheState(); |
|
14292 fireUrlChange(); |
|
14293 } |
|
14294 |
|
14295 // This variable should be used *only* inside the cacheState function. |
|
14296 var lastCachedState = null; |
|
14297 function cacheState() { |
|
14298 // This should be the only place in $browser where `history.state` is read. |
|
14299 cachedState = window.history.state; |
|
14300 cachedState = isUndefined(cachedState) ? null : cachedState; |
|
14301 |
|
14302 // Prevent callbacks fo fire twice if both hashchange & popstate were fired. |
|
14303 if (equals(cachedState, lastCachedState)) { |
|
14304 cachedState = lastCachedState; |
|
14305 } |
|
14306 lastCachedState = cachedState; |
|
14307 } |
|
14308 |
|
14028 function fireUrlChange() { |
14309 function fireUrlChange() { |
14029 if (lastBrowserUrl === self.url() && lastHistoryState === history.state) { |
14310 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { |
14030 return; |
14311 return; |
14031 } |
14312 } |
14032 |
14313 |
14033 lastBrowserUrl = self.url(); |
14314 lastBrowserUrl = self.url(); |
14315 lastHistoryState = cachedState; |
|
14034 forEach(urlChangeListeners, function(listener) { |
14316 forEach(urlChangeListeners, function(listener) { |
14035 listener(self.url(), history.state); |
14317 listener(self.url(), cachedState); |
14036 }); |
14318 }); |
14037 } |
14319 } |
14038 |
14320 |
14039 /** |
14321 /** |
14040 * @name $browser#onUrlChange |
14322 * @name $browser#onUrlChange |
14063 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) |
14345 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) |
14064 // don't fire popstate when user change the address bar and don't fire hashchange when url |
14346 // don't fire popstate when user change the address bar and don't fire hashchange when url |
14065 // changed by push/replaceState |
14347 // changed by push/replaceState |
14066 |
14348 |
14067 // html5 history api - popstate event |
14349 // html5 history api - popstate event |
14068 if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange); |
14350 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); |
14069 // hashchange event |
14351 // hashchange event |
14070 jqLite(window).on('hashchange', fireUrlChange); |
14352 jqLite(window).on('hashchange', cacheStateAndFireUrlChange); |
14071 |
14353 |
14072 urlChangeInit = true; |
14354 urlChangeInit = true; |
14073 } |
14355 } |
14074 |
14356 |
14075 urlChangeListeners.push(callback); |
14357 urlChangeListeners.push(callback); |
14105 // Cookies API |
14387 // Cookies API |
14106 ////////////////////////////////////////////////////////////// |
14388 ////////////////////////////////////////////////////////////// |
14107 var lastCookies = {}; |
14389 var lastCookies = {}; |
14108 var lastCookieString = ''; |
14390 var lastCookieString = ''; |
14109 var cookiePath = self.baseHref(); |
14391 var cookiePath = self.baseHref(); |
14392 |
|
14393 function safeDecodeURIComponent(str) { |
|
14394 try { |
|
14395 return decodeURIComponent(str); |
|
14396 } catch (e) { |
|
14397 return str; |
|
14398 } |
|
14399 } |
|
14110 |
14400 |
14111 /** |
14401 /** |
14112 * @name $browser#cookies |
14402 * @name $browser#cookies |
14113 * |
14403 * |
14114 * @param {string=} name Cookie name |
14404 * @param {string=} name Cookie name |
14143 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: |
14433 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: |
14144 // - 300 cookies |
14434 // - 300 cookies |
14145 // - 20 cookies per unique domain |
14435 // - 20 cookies per unique domain |
14146 // - 4096 bytes per cookie |
14436 // - 4096 bytes per cookie |
14147 if (cookieLength > 4096) { |
14437 if (cookieLength > 4096) { |
14148 $log.warn("Cookie '"+ name + |
14438 $log.warn("Cookie '" + name + |
14149 "' possibly not set or overflowed because it was too large ("+ |
14439 "' possibly not set or overflowed because it was too large (" + |
14150 cookieLength + " > 4096 bytes)!"); |
14440 cookieLength + " > 4096 bytes)!"); |
14151 } |
14441 } |
14152 } |
14442 } |
14153 } |
14443 } |
14154 } else { |
14444 } else { |
14159 |
14449 |
14160 for (i = 0; i < cookieArray.length; i++) { |
14450 for (i = 0; i < cookieArray.length; i++) { |
14161 cookie = cookieArray[i]; |
14451 cookie = cookieArray[i]; |
14162 index = cookie.indexOf('='); |
14452 index = cookie.indexOf('='); |
14163 if (index > 0) { //ignore nameless cookies |
14453 if (index > 0) { //ignore nameless cookies |
14164 name = decodeURIComponent(cookie.substring(0, index)); |
14454 name = safeDecodeURIComponent(cookie.substring(0, index)); |
14165 // the first value that is seen for a cookie is the most |
14455 // the first value that is seen for a cookie is the most |
14166 // specific one. values for the same cookie name that |
14456 // specific one. values for the same cookie name that |
14167 // follow are for less specific paths. |
14457 // follow are for less specific paths. |
14168 if (lastCookies[name] === undefined) { |
14458 if (lastCookies[name] === undefined) { |
14169 lastCookies[name] = decodeURIComponent(cookie.substring(index + 1)); |
14459 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); |
14170 } |
14460 } |
14171 } |
14461 } |
14172 } |
14462 } |
14173 } |
14463 } |
14174 return lastCookies; |
14464 return lastCookies; |
14222 return false; |
14512 return false; |
14223 }; |
14513 }; |
14224 |
14514 |
14225 } |
14515 } |
14226 |
14516 |
14227 function $BrowserProvider(){ |
14517 function $BrowserProvider() { |
14228 this.$get = ['$window', '$log', '$sniffer', '$document', |
14518 this.$get = ['$window', '$log', '$sniffer', '$document', |
14229 function( $window, $log, $sniffer, $document){ |
14519 function($window, $log, $sniffer, $document) { |
14230 return new Browser($window, $document, $log, $sniffer); |
14520 return new Browser($window, $document, $log, $sniffer); |
14231 }]; |
14521 }]; |
14232 } |
14522 } |
14233 |
14523 |
14234 /** |
14524 /** |
14598 * <p>This is the content of the template</p> |
14888 * <p>This is the content of the template</p> |
14599 * </script> |
14889 * </script> |
14600 * ``` |
14890 * ``` |
14601 * |
14891 * |
14602 * **Note:** the `script` tag containing the template does not need to be included in the `head` of |
14892 * **Note:** the `script` tag containing the template does not need to be included in the `head` of |
14603 * the document, but it must be below the `ng-app` definition. |
14893 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, |
14894 * element with ng-app attribute), otherwise the template will be ignored. |
|
14604 * |
14895 * |
14605 * Adding via the $templateCache service: |
14896 * Adding via the $templateCache service: |
14606 * |
14897 * |
14607 * ```js |
14898 * ```js |
14608 * var myApp = angular.module('myApp', []); |
14899 * var myApp = angular.module('myApp', []); |
14689 * template: '<div></div>', // or // function(tElement, tAttrs) { ... }, |
14980 * template: '<div></div>', // or // function(tElement, tAttrs) { ... }, |
14690 * // or |
14981 * // or |
14691 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, |
14982 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, |
14692 * transclude: false, |
14983 * transclude: false, |
14693 * restrict: 'A', |
14984 * restrict: 'A', |
14985 * templateNamespace: 'html', |
|
14694 * scope: false, |
14986 * scope: false, |
14695 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, |
14987 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, |
14696 * controllerAs: 'stringAlias', |
14988 * controllerAs: 'stringAlias', |
14697 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], |
14989 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], |
14698 * compile: function compile(tElement, tAttrs, transclude) { |
14990 * compile: function compile(tElement, tAttrs, transclude) { |
14742 * compiler}. The attributes are: |
15034 * compiler}. The attributes are: |
14743 * |
15035 * |
14744 * #### `multiElement` |
15036 * #### `multiElement` |
14745 * When this property is set to true, the HTML compiler will collect DOM nodes between |
15037 * When this property is set to true, the HTML compiler will collect DOM nodes between |
14746 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them |
15038 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them |
14747 * together as the directive elements. It is recomended that this feature be used on directives |
15039 * together as the directive elements. It is recommended that this feature be used on directives |
14748 * which are not strictly behavioural (such as {@link api/ng.directive:ngClick ngClick}), and which |
15040 * which are not strictly behavioural (such as {@link ngClick}), and which |
14749 * do not manipulate or replace child nodes (such as {@link api/ng.directive:ngInclude ngInclude}). |
15041 * do not manipulate or replace child nodes (such as {@link ngInclude}). |
14750 * |
15042 * |
14751 * #### `priority` |
15043 * #### `priority` |
14752 * When there are multiple directives defined on a single DOM element, sometimes it |
15044 * When there are multiple directives defined on a single DOM element, sometimes it |
14753 * is necessary to specify the order in which the directives are applied. The `priority` is used |
15045 * is necessary to specify the order in which the directives are applied. The `priority` is used |
14754 * to sort the directives before their `compile` functions get called. Priority is defined as a |
15046 * to sort the directives before their `compile` functions get called. Priority is defined as a |
14757 * of directives with the same priority is undefined. The default priority is `0`. |
15049 * of directives with the same priority is undefined. The default priority is `0`. |
14758 * |
15050 * |
14759 * #### `terminal` |
15051 * #### `terminal` |
14760 * If set to true then the current `priority` will be the last set of directives |
15052 * If set to true then the current `priority` will be the last set of directives |
14761 * which will execute (any directives at the current priority will still execute |
15053 * which will execute (any directives at the current priority will still execute |
14762 * as the order of execution on same `priority` is undefined). |
15054 * as the order of execution on same `priority` is undefined). Note that expressions |
15055 * and other directives used in the directive's template will also be excluded from execution. |
|
14763 * |
15056 * |
14764 * #### `scope` |
15057 * #### `scope` |
14765 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the |
15058 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the |
14766 * same element request a new scope, only one new scope is created. The new scope rule does not |
15059 * same element request a new scope, only one new scope is created. The new scope rule does not |
14767 * apply for the root of the template since the root of the template always gets a new scope. |
15060 * apply for the root of the template since the root of the template always gets a new scope. |
14790 * Given `<widget my-attr="parentModel">` and widget definition of |
15083 * Given `<widget my-attr="parentModel">` and widget definition of |
14791 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the |
15084 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the |
14792 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected |
15085 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected |
14793 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent |
15086 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent |
14794 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You |
15087 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You |
14795 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. |
15088 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If |
15089 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use |
|
15090 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional). |
|
14796 * |
15091 * |
14797 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. |
15092 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. |
14798 * If no `attr` name is specified then the attribute name is assumed to be the same as the |
15093 * If no `attr` name is specified then the attribute name is assumed to be the same as the |
14799 * local name. Given `<widget my-attr="count = count + value">` and widget definition of |
15094 * local name. Given `<widget my-attr="count = count + value">` and widget definition of |
14800 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to |
15095 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to |
14804 * For example, if the expression is `increment(amount)` then we can specify the amount value |
15099 * For example, if the expression is `increment(amount)` then we can specify the amount value |
14805 * by calling the `localFn` as `localFn({amount: 22})`. |
15100 * by calling the `localFn` as `localFn({amount: 22})`. |
14806 * |
15101 * |
14807 * |
15102 * |
14808 * #### `bindToController` |
15103 * #### `bindToController` |
14809 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will |
15104 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will |
14810 * allow a component to have its properties bound to the controller, rather than to scope. When the controller |
15105 * allow a component to have its properties bound to the controller, rather than to scope. When the controller |
14811 * is instantiated, the initial values of the isolate scope bindings are already available. |
15106 * is instantiated, the initial values of the isolate scope bindings are already available. |
14812 * |
15107 * |
14813 * #### `controller` |
15108 * #### `controller` |
14814 * Controller constructor function. The controller is instantiated before the |
15109 * Controller constructor function. The controller is instantiated before the |
14904 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} |
15199 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} |
14905 * |
15200 * |
14906 * You can specify `templateUrl` as a string representing the URL or as a function which takes two |
15201 * You can specify `templateUrl` as a string representing the URL or as a function which takes two |
14907 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns |
15202 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns |
14908 * a string value representing the url. In either case, the template URL is passed through {@link |
15203 * a string value representing the url. In either case, the template URL is passed through {@link |
14909 * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. |
15204 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. |
14910 * |
15205 * |
14911 * |
15206 * |
14912 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) |
15207 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) |
14913 * specify what the template should replace. Defaults to `false`. |
15208 * specify what the template should replace. Defaults to `false`. |
14914 * |
15209 * |
14915 * * `true` - the template will replace the directive's element. |
15210 * * `true` - the template will replace the directive's element. |
14916 * * `false` - the template will replace the contents of the directive's element. |
15211 * * `false` - the template will replace the contents of the directive's element. |
14917 * |
15212 * |
14918 * The replacement process migrates all of the attributes / classes from the old element to the new |
15213 * The replacement process migrates all of the attributes / classes from the old element to the new |
14919 * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive |
15214 * one. See the {@link guide/directive#template-expanding-directive |
14920 * Directives Guide} for an example. |
15215 * Directives Guide} for an example. |
14921 * |
15216 * |
14922 * There are very few scenarios where element replacement is required for the application function, |
15217 * There are very few scenarios where element replacement is required for the application function, |
14923 * the main one being reusable custom components that are used within SVG contexts |
15218 * the main one being reusable custom components that are used within SVG contexts |
14924 * (because SVG doesn't work with custom elements in the DOM tree). |
15219 * (because SVG doesn't work with custom elements in the DOM tree). |
15069 * |
15364 * |
15070 * If you want to manually control the insertion and removal of the transcluded content in your directive |
15365 * If you want to manually control the insertion and removal of the transcluded content in your directive |
15071 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery |
15366 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery |
15072 * object that contains the compiled DOM, which is linked to the correct transclusion scope. |
15367 * object that contains the compiled DOM, which is linked to the correct transclusion scope. |
15073 * |
15368 * |
15074 * When you call a transclusion function you can pass in a **clone attach function**. This function is accepts |
15369 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts |
15075 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded |
15370 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded |
15076 * content and the `scope` is the newly created transclusion scope, to which the clone is bound. |
15371 * content and the `scope` is the newly created transclusion scope, to which the clone is bound. |
15077 * |
15372 * |
15078 * <div class="alert alert-info"> |
15373 * <div class="alert alert-info"> |
15079 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function |
15374 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function |
15246 </example> |
15541 </example> |
15247 |
15542 |
15248 * |
15543 * |
15249 * |
15544 * |
15250 * @param {string|DOMElement} element Element or HTML string to compile into a template function. |
15545 * @param {string|DOMElement} element Element or HTML string to compile into a template function. |
15251 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives. |
15546 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. |
15547 * |
|
15548 * <div class="alert alert-error"> |
|
15549 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it |
|
15550 * e.g. will not use the right outer scope. Please pass the transclude function as a |
|
15551 * `parentBoundTranscludeFn` to the link function instead. |
|
15552 * </div> |
|
15553 * |
|
15252 * @param {number} maxPriority only apply directives lower than given priority (Only effects the |
15554 * @param {number} maxPriority only apply directives lower than given priority (Only effects the |
15253 * root element(s), not their children) |
15555 * root element(s), not their children) |
15254 * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template |
15556 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template |
15255 * (a DOM element/tree) to a scope. Where: |
15557 * (a DOM element/tree) to a scope. Where: |
15256 * |
15558 * |
15257 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. |
15559 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. |
15258 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the |
15560 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the |
15259 * `template` and call the `cloneAttachFn` function allowing the caller to attach the |
15561 * `template` and call the `cloneAttachFn` function allowing the caller to attach the |
15260 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is |
15562 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is |
15261 * called as: <br> `cloneAttachFn(clonedElement, scope)` where: |
15563 * called as: <br> `cloneAttachFn(clonedElement, scope)` where: |
15262 * |
15564 * |
15263 * * `clonedElement` - is a clone of the original `element` passed into the compiler. |
15565 * * `clonedElement` - is a clone of the original `element` passed into the compiler. |
15264 * * `scope` - is the current scope with which the linking function is working with. |
15566 * * `scope` - is the current scope with which the linking function is working with. |
15567 * |
|
15568 * * `options` - An optional object hash with linking options. If `options` is provided, then the following |
|
15569 * keys may be used to control linking behavior: |
|
15570 * |
|
15571 * * `parentBoundTranscludeFn` - the transclude function made available to |
|
15572 * directives; if given, it will be passed through to the link functions of |
|
15573 * directives found in `element` during compilation. |
|
15574 * * `transcludeControllers` - an object hash with keys that map controller names |
|
15575 * to controller instances; if given, it will make the controllers |
|
15576 * available to directives. |
|
15577 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add |
|
15578 * the cloned elements; only needed for transcludes that are allowed to contain non html |
|
15579 * elements (e.g. SVG elements). See also the directive.controller property. |
|
15265 * |
15580 * |
15266 * Calling the linking function returns the element of the template. It is either the original |
15581 * Calling the linking function returns the element of the template. It is either the original |
15267 * element passed in, or the clone of the element if the `cloneAttachFn` is provided. |
15582 * element passed in, or the clone of the element if the `cloneAttachFn` is provided. |
15268 * |
15583 * |
15269 * After linking the view is not updated until after a call to $digest which typically is done by |
15584 * After linking the view is not updated until after a call to $digest which typically is done by |
15306 */ |
15621 */ |
15307 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; |
15622 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; |
15308 function $CompileProvider($provide, $$sanitizeUriProvider) { |
15623 function $CompileProvider($provide, $$sanitizeUriProvider) { |
15309 var hasDirectives = {}, |
15624 var hasDirectives = {}, |
15310 Suffix = 'Directive', |
15625 Suffix = 'Directive', |
15311 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/, |
15626 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/, |
15312 CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/, |
15627 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/, |
15313 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), |
15628 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), |
15314 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; |
15629 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; |
15315 |
15630 |
15316 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes |
15631 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes |
15317 // The assumption is that future DOM event attribute names will begin with |
15632 // The assumption is that future DOM event attribute names will begin with |
15318 // 'on' and be composed of only English letters. |
15633 // 'on' and be composed of only English letters. |
15319 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; |
15634 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; |
15320 |
15635 |
15321 function parseIsolateBindings(scope, directiveName) { |
15636 function parseIsolateBindings(scope, directiveName) { |
15322 var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; |
15637 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; |
15323 |
15638 |
15324 var bindings = {}; |
15639 var bindings = {}; |
15325 |
15640 |
15326 forEach(scope, function(definition, scopeName) { |
15641 forEach(scope, function(definition, scopeName) { |
15327 var match = definition.match(LOCAL_REGEXP); |
15642 var match = definition.match(LOCAL_REGEXP); |
15332 " Definition: {... {1}: '{2}' ...}", |
15647 " Definition: {... {1}: '{2}' ...}", |
15333 directiveName, scopeName, definition); |
15648 directiveName, scopeName, definition); |
15334 } |
15649 } |
15335 |
15650 |
15336 bindings[scopeName] = { |
15651 bindings[scopeName] = { |
15337 attrName: match[3] || scopeName, |
15652 mode: match[1][0], |
15338 mode: match[1], |
15653 collection: match[2] === '*', |
15339 optional: match[2] === '?' |
15654 optional: match[3] === '?', |
15655 attrName: match[4] || scopeName |
|
15340 }; |
15656 }; |
15341 }); |
15657 }); |
15342 |
15658 |
15343 return bindings; |
15659 return bindings; |
15344 } |
15660 } |
15406 * |
15722 * |
15407 * @description |
15723 * @description |
15408 * Retrieves or overrides the default regular expression that is used for whitelisting of safe |
15724 * Retrieves or overrides the default regular expression that is used for whitelisting of safe |
15409 * urls during a[href] sanitization. |
15725 * urls during a[href] sanitization. |
15410 * |
15726 * |
15411 * The sanitization is a security measure aimed at prevent XSS attacks via html links. |
15727 * The sanitization is a security measure aimed at preventing XSS attacks via html links. |
15412 * |
15728 * |
15413 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into |
15729 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into |
15414 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` |
15730 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` |
15415 * regular expression. If a match is found, the original url is written into the dom. Otherwise, |
15731 * regular expression. If a match is found, the original url is written into the dom. Otherwise, |
15416 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. |
15732 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. |
15473 * binding information and a reference to the current scope on to DOM elements. |
15789 * binding information and a reference to the current scope on to DOM elements. |
15474 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope |
15790 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope |
15475 * * `ng-binding` CSS class |
15791 * * `ng-binding` CSS class |
15476 * * `$binding` data property containing an array of the binding expressions |
15792 * * `$binding` data property containing an array of the binding expressions |
15477 * |
15793 * |
15478 * You may want to use this in production for a significant performance boost. See |
15794 * You may want to disable this in production for a significant performance boost. See |
15479 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. |
15795 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. |
15480 * |
15796 * |
15481 * The default value is true. |
15797 * The default value is true. |
15482 */ |
15798 */ |
15483 var debugInfoEnabled = true; |
15799 var debugInfoEnabled = true; |
15484 this.debugInfoEnabled = function(enabled) { |
15800 this.debugInfoEnabled = function(enabled) { |
15485 if(isDefined(enabled)) { |
15801 if (isDefined(enabled)) { |
15486 debugInfoEnabled = enabled; |
15802 debugInfoEnabled = enabled; |
15487 return this; |
15803 return this; |
15488 } |
15804 } |
15489 return debugInfoEnabled; |
15805 return debugInfoEnabled; |
15490 }; |
15806 }; |
15510 |
15826 |
15511 this.$$element = element; |
15827 this.$$element = element; |
15512 }; |
15828 }; |
15513 |
15829 |
15514 Attributes.prototype = { |
15830 Attributes.prototype = { |
15831 /** |
|
15832 * @ngdoc method |
|
15833 * @name $compile.directive.Attributes#$normalize |
|
15834 * @kind function |
|
15835 * |
|
15836 * @description |
|
15837 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or |
|
15838 * `data-`) to its normalized, camelCase form. |
|
15839 * |
|
15840 * Also there is special case for Moz prefix starting with upper case letter. |
|
15841 * |
|
15842 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} |
|
15843 * |
|
15844 * @param {string} name Name to normalize |
|
15845 */ |
|
15515 $normalize: directiveNormalize, |
15846 $normalize: directiveNormalize, |
15516 |
15847 |
15517 |
15848 |
15518 /** |
15849 /** |
15519 * @ngdoc method |
15850 * @ngdoc method |
15524 * Adds the CSS class value specified by the classVal parameter to the element. If animations |
15855 * Adds the CSS class value specified by the classVal parameter to the element. If animations |
15525 * are enabled then an animation will be triggered for the class addition. |
15856 * are enabled then an animation will be triggered for the class addition. |
15526 * |
15857 * |
15527 * @param {string} classVal The className value that will be added to the element |
15858 * @param {string} classVal The className value that will be added to the element |
15528 */ |
15859 */ |
15529 $addClass : function(classVal) { |
15860 $addClass: function(classVal) { |
15530 if(classVal && classVal.length > 0) { |
15861 if (classVal && classVal.length > 0) { |
15531 $animate.addClass(this.$$element, classVal); |
15862 $animate.addClass(this.$$element, classVal); |
15532 } |
15863 } |
15533 }, |
15864 }, |
15534 |
15865 |
15535 /** |
15866 /** |
15541 * Removes the CSS class value specified by the classVal parameter from the element. If |
15872 * Removes the CSS class value specified by the classVal parameter from the element. If |
15542 * animations are enabled then an animation will be triggered for the class removal. |
15873 * animations are enabled then an animation will be triggered for the class removal. |
15543 * |
15874 * |
15544 * @param {string} classVal The className value that will be removed from the element |
15875 * @param {string} classVal The className value that will be removed from the element |
15545 */ |
15876 */ |
15546 $removeClass : function(classVal) { |
15877 $removeClass: function(classVal) { |
15547 if(classVal && classVal.length > 0) { |
15878 if (classVal && classVal.length > 0) { |
15548 $animate.removeClass(this.$$element, classVal); |
15879 $animate.removeClass(this.$$element, classVal); |
15549 } |
15880 } |
15550 }, |
15881 }, |
15551 |
15882 |
15552 /** |
15883 /** |
15559 * between the new and old CSS class values (specified as newClasses and oldClasses). |
15890 * between the new and old CSS class values (specified as newClasses and oldClasses). |
15560 * |
15891 * |
15561 * @param {string} newClasses The current CSS className value |
15892 * @param {string} newClasses The current CSS className value |
15562 * @param {string} oldClasses The former CSS className value |
15893 * @param {string} oldClasses The former CSS className value |
15563 */ |
15894 */ |
15564 $updateClass : function(newClasses, oldClasses) { |
15895 $updateClass: function(newClasses, oldClasses) { |
15565 var toAdd = tokenDifference(newClasses, oldClasses); |
15896 var toAdd = tokenDifference(newClasses, oldClasses); |
15566 if (toAdd && toAdd.length) { |
15897 if (toAdd && toAdd.length) { |
15567 $animate.addClass(this.$$element, toAdd); |
15898 $animate.addClass(this.$$element, toAdd); |
15568 } |
15899 } |
15569 |
15900 |
15589 |
15920 |
15590 var node = this.$$element[0], |
15921 var node = this.$$element[0], |
15591 booleanKey = getBooleanAttrName(node, key), |
15922 booleanKey = getBooleanAttrName(node, key), |
15592 aliasedKey = getAliasedAttrName(node, key), |
15923 aliasedKey = getAliasedAttrName(node, key), |
15593 observer = key, |
15924 observer = key, |
15594 normalizedVal, |
|
15595 nodeName; |
15925 nodeName; |
15596 |
15926 |
15597 if (booleanKey) { |
15927 if (booleanKey) { |
15598 this.$$element.prop(key, value); |
15928 this.$$element.prop(key, value); |
15599 attrName = booleanKey; |
15929 attrName = booleanKey; |
15600 } else if(aliasedKey) { |
15930 } else if (aliasedKey) { |
15601 this[aliasedKey] = value; |
15931 this[aliasedKey] = value; |
15602 observer = aliasedKey; |
15932 observer = aliasedKey; |
15603 } |
15933 } |
15604 |
15934 |
15605 this[key] = value; |
15935 this[key] = value; |
15633 // split srcset into tuple of uri and descriptor except for the last item |
15963 // split srcset into tuple of uri and descriptor except for the last item |
15634 var rawUris = trimmedSrcset.split(pattern); |
15964 var rawUris = trimmedSrcset.split(pattern); |
15635 |
15965 |
15636 // for each tuples |
15966 // for each tuples |
15637 var nbrUrisWith2parts = Math.floor(rawUris.length / 2); |
15967 var nbrUrisWith2parts = Math.floor(rawUris.length / 2); |
15638 for (var i=0; i<nbrUrisWith2parts; i++) { |
15968 for (var i = 0; i < nbrUrisWith2parts; i++) { |
15639 var innerIdx = i*2; |
15969 var innerIdx = i * 2; |
15640 // sanitize the uri |
15970 // sanitize the uri |
15641 result += $$sanitizeUri(trim( rawUris[innerIdx]), true); |
15971 result += $$sanitizeUri(trim(rawUris[innerIdx]), true); |
15642 // add the descriptor |
15972 // add the descriptor |
15643 result += ( " " + trim(rawUris[innerIdx+1])); |
15973 result += (" " + trim(rawUris[innerIdx + 1])); |
15644 } |
15974 } |
15645 |
15975 |
15646 // split the last item into uri and descriptor |
15976 // split the last item into uri and descriptor |
15647 var lastTuple = trim(rawUris[i*2]).split(/\s/); |
15977 var lastTuple = trim(rawUris[i * 2]).split(/\s/); |
15648 |
15978 |
15649 // sanitize the last uri |
15979 // sanitize the last uri |
15650 result += $$sanitizeUri(trim(lastTuple[0]), true); |
15980 result += $$sanitizeUri(trim(lastTuple[0]), true); |
15651 |
15981 |
15652 // and add the last descriptor if any |
15982 // and add the last descriptor if any |
15653 if( lastTuple.length === 2) { |
15983 if (lastTuple.length === 2) { |
15654 result += (" " + trim(lastTuple[1])); |
15984 result += (" " + trim(lastTuple[1])); |
15655 } |
15985 } |
15656 this[key] = value = result; |
15986 this[key] = value = result; |
15657 } |
15987 } |
15658 |
15988 |
15689 * changes. |
16019 * changes. |
15690 * |
16020 * |
15691 * @param {string} key Normalized key. (ie ngAttribute) . |
16021 * @param {string} key Normalized key. (ie ngAttribute) . |
15692 * @param {function(interpolatedValue)} fn Function that will be called whenever |
16022 * @param {function(interpolatedValue)} fn Function that will be called whenever |
15693 the interpolated value of the attribute changes. |
16023 the interpolated value of the attribute changes. |
15694 * See {@link ng.$compile#attributes $compile} for more info. |
16024 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info. |
15695 * @returns {function()} Returns a deregistration function for this observer. |
16025 * @returns {function()} Returns a deregistration function for this observer. |
15696 */ |
16026 */ |
15697 $observe: function(key, fn) { |
16027 $observe: function(key, fn) { |
15698 var attrs = this, |
16028 var attrs = this, |
15699 $$observers = (attrs.$$observers || (attrs.$$observers = Object.create(null))), |
16029 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), |
15700 listeners = ($$observers[key] || ($$observers[key] = [])); |
16030 listeners = ($$observers[key] || ($$observers[key] = [])); |
15701 |
16031 |
15702 listeners.push(fn); |
16032 listeners.push(fn); |
15703 $rootScope.$evalAsync(function() { |
16033 $rootScope.$evalAsync(function() { |
15704 if (!listeners.$$inter) { |
16034 if (!listeners.$$inter && attrs.hasOwnProperty(key)) { |
15705 // no one registered attribute interpolation function, so lets call it manually |
16035 // no one registered attribute interpolation function, so lets call it manually |
15706 fn(attrs[key]); |
16036 fn(attrs[key]); |
15707 } |
16037 } |
15708 }); |
16038 }); |
15709 |
16039 |
15715 |
16045 |
15716 |
16046 |
15717 function safeAddClass($element, className) { |
16047 function safeAddClass($element, className) { |
15718 try { |
16048 try { |
15719 $element.addClass(className); |
16049 $element.addClass(className); |
15720 } catch(e) { |
16050 } catch (e) { |
15721 // ignore, since it means that we are trying to set class on |
16051 // ignore, since it means that we are trying to set class on |
15722 // SVG element, where class name is read-only. |
16052 // SVG element, where class name is read-only. |
15723 } |
16053 } |
15724 } |
16054 } |
15725 |
16055 |
15769 // modify it. |
16099 // modify it. |
15770 $compileNodes = jqLite($compileNodes); |
16100 $compileNodes = jqLite($compileNodes); |
15771 } |
16101 } |
15772 // We can not compile top level text elements since text nodes can be merged and we will |
16102 // We can not compile top level text elements since text nodes can be merged and we will |
15773 // not be able to attach scope data to them, so we will wrap them in <span> |
16103 // not be able to attach scope data to them, so we will wrap them in <span> |
15774 forEach($compileNodes, function(node, index){ |
16104 forEach($compileNodes, function(node, index) { |
15775 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) { |
16105 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) { |
15776 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; |
16106 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; |
15777 } |
16107 } |
15778 }); |
16108 }); |
15779 var compositeLinkFn = |
16109 var compositeLinkFn = |
15780 compileNodes($compileNodes, transcludeFn, $compileNodes, |
16110 compileNodes($compileNodes, transcludeFn, $compileNodes, |
15781 maxPriority, ignoreDirective, previousCompileContext); |
16111 maxPriority, ignoreDirective, previousCompileContext); |
15782 compile.$$addScopeClass($compileNodes); |
16112 compile.$$addScopeClass($compileNodes); |
15783 var namespace = null; |
16113 var namespace = null; |
15784 return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement){ |
16114 return function publicLinkFn(scope, cloneConnectFn, options) { |
15785 assertArg(scope, 'scope'); |
16115 assertArg(scope, 'scope'); |
16116 |
|
16117 options = options || {}; |
|
16118 var parentBoundTranscludeFn = options.parentBoundTranscludeFn, |
|
16119 transcludeControllers = options.transcludeControllers, |
|
16120 futureParentElement = options.futureParentElement; |
|
16121 |
|
16122 // When `parentBoundTranscludeFn` is passed, it is a |
|
16123 // `controllersBoundTransclude` function (it was previously passed |
|
16124 // as `transclude` to directive.link) so we must unwrap it to get |
|
16125 // its `boundTranscludeFn` |
|
16126 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { |
|
16127 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; |
|
16128 } |
|
16129 |
|
15786 if (!namespace) { |
16130 if (!namespace) { |
15787 namespace = detectNamespaceForChildElements(futureParentElement); |
16131 namespace = detectNamespaceForChildElements(futureParentElement); |
15788 } |
16132 } |
15789 var $linkNode; |
16133 var $linkNode; |
15790 if (namespace !== 'html') { |
16134 if (namespace !== 'html') { |
15822 // TODO: Make this detect MathML as well... |
16166 // TODO: Make this detect MathML as well... |
15823 var node = parentElement && parentElement[0]; |
16167 var node = parentElement && parentElement[0]; |
15824 if (!node) { |
16168 if (!node) { |
15825 return 'html'; |
16169 return 'html'; |
15826 } else { |
16170 } else { |
15827 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg': 'html'; |
16171 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html'; |
15828 } |
16172 } |
15829 } |
16173 } |
15830 |
16174 |
15831 /** |
16175 /** |
15832 * Compile function matches each node in nodeList against the directives. Once all directives |
16176 * Compile function matches each node in nodeList against the directives. Once all directives |
15904 } |
16248 } |
15905 } else { |
16249 } else { |
15906 stableNodeList = nodeList; |
16250 stableNodeList = nodeList; |
15907 } |
16251 } |
15908 |
16252 |
15909 for(i = 0, ii = linkFns.length; i < ii;) { |
16253 for (i = 0, ii = linkFns.length; i < ii;) { |
15910 node = stableNodeList[linkFns[i++]]; |
16254 node = stableNodeList[linkFns[i++]]; |
15911 nodeLinkFn = linkFns[i++]; |
16255 nodeLinkFn = linkFns[i++]; |
15912 childLinkFn = linkFns[i++]; |
16256 childLinkFn = linkFns[i++]; |
15913 |
16257 |
15914 if (nodeLinkFn) { |
16258 if (nodeLinkFn) { |
15917 compile.$$addScopeInfo(jqLite(node), childScope); |
16261 compile.$$addScopeInfo(jqLite(node), childScope); |
15918 } else { |
16262 } else { |
15919 childScope = scope; |
16263 childScope = scope; |
15920 } |
16264 } |
15921 |
16265 |
15922 if ( nodeLinkFn.transcludeOnThisElement ) { |
16266 if (nodeLinkFn.transcludeOnThisElement) { |
15923 childBoundTranscludeFn = createBoundTranscludeFn( |
16267 childBoundTranscludeFn = createBoundTranscludeFn( |
15924 scope, nodeLinkFn.transclude, parentBoundTranscludeFn, |
16268 scope, nodeLinkFn.transclude, parentBoundTranscludeFn, |
15925 nodeLinkFn.elementTranscludeOnThisElement); |
16269 nodeLinkFn.elementTranscludeOnThisElement); |
15926 |
16270 |
15927 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { |
16271 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { |
15950 if (!transcludedScope) { |
16294 if (!transcludedScope) { |
15951 transcludedScope = scope.$new(false, containingScope); |
16295 transcludedScope = scope.$new(false, containingScope); |
15952 transcludedScope.$$transcluded = true; |
16296 transcludedScope.$$transcluded = true; |
15953 } |
16297 } |
15954 |
16298 |
15955 return transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement); |
16299 return transcludeFn(transcludedScope, cloneFn, { |
16300 parentBoundTranscludeFn: previousBoundTranscludeFn, |
|
16301 transcludeControllers: controllers, |
|
16302 futureParentElement: futureParentElement |
|
16303 }); |
|
15956 }; |
16304 }; |
15957 |
16305 |
15958 return boundTranscludeFn; |
16306 return boundTranscludeFn; |
15959 } |
16307 } |
15960 |
16308 |
15972 var nodeType = node.nodeType, |
16320 var nodeType = node.nodeType, |
15973 attrsMap = attrs.$attr, |
16321 attrsMap = attrs.$attr, |
15974 match, |
16322 match, |
15975 className; |
16323 className; |
15976 |
16324 |
15977 switch(nodeType) { |
16325 switch (nodeType) { |
15978 case NODE_TYPE_ELEMENT: /* Element */ |
16326 case NODE_TYPE_ELEMENT: /* Element */ |
15979 // use the node name: <directive> |
16327 // use the node name: <directive> |
15980 addDirective(directives, |
16328 addDirective(directives, |
15981 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); |
16329 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); |
15982 |
16330 |
15991 value = trim(attr.value); |
16339 value = trim(attr.value); |
15992 |
16340 |
15993 // support ngAttr attribute binding |
16341 // support ngAttr attribute binding |
15994 ngAttrName = directiveNormalize(name); |
16342 ngAttrName = directiveNormalize(name); |
15995 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { |
16343 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { |
15996 name = snake_case(ngAttrName.substr(6), '-'); |
16344 name = name.replace(PREFIX_REGEXP, '') |
16345 .substr(8).replace(/_(.)/g, function(match, letter) { |
|
16346 return letter.toUpperCase(); |
|
16347 }); |
|
15997 } |
16348 } |
15998 |
16349 |
15999 var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); |
16350 var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); |
16000 if (directiveIsMultiElement(directiveNName)) { |
16351 if (directiveIsMultiElement(directiveNName)) { |
16001 if (ngAttrName === directiveNName + 'Start') { |
16352 if (ngAttrName === directiveNName + 'Start') { |
16064 */ |
16415 */ |
16065 function groupScan(node, attrStart, attrEnd) { |
16416 function groupScan(node, attrStart, attrEnd) { |
16066 var nodes = []; |
16417 var nodes = []; |
16067 var depth = 0; |
16418 var depth = 0; |
16068 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { |
16419 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { |
16069 var startNode = node; |
|
16070 do { |
16420 do { |
16071 if (!node) { |
16421 if (!node) { |
16072 throw $compileMinErr('uterdir', |
16422 throw $compileMinErr('uterdir', |
16073 "Unterminated attribute, found '{0}' but no matching '{1}' found.", |
16423 "Unterminated attribute, found '{0}' but no matching '{1}' found.", |
16074 attrStart, attrEnd); |
16424 attrStart, attrEnd); |
16148 childTranscludeFn = transcludeFn, |
16498 childTranscludeFn = transcludeFn, |
16149 linkFn, |
16499 linkFn, |
16150 directiveValue; |
16500 directiveValue; |
16151 |
16501 |
16152 // executes all directives on the current element |
16502 // executes all directives on the current element |
16153 for(var i = 0, ii = directives.length; i < ii; i++) { |
16503 for (var i = 0, ii = directives.length; i < ii; i++) { |
16154 directive = directives[i]; |
16504 directive = directives[i]; |
16155 var attrStart = directive.$$start; |
16505 var attrStart = directive.$$start; |
16156 var attrEnd = directive.$$end; |
16506 var attrEnd = directive.$$end; |
16157 |
16507 |
16158 // collect multiblock sections |
16508 // collect multiblock sections |
16392 if (!value && !optional) { |
16742 if (!value && !optional) { |
16393 throw $compileMinErr('ctreq', |
16743 throw $compileMinErr('ctreq', |
16394 "Controller '{0}', required by directive '{1}', can't be found!", |
16744 "Controller '{0}', required by directive '{1}', can't be found!", |
16395 require, directiveName); |
16745 require, directiveName); |
16396 } |
16746 } |
16397 return value; |
16747 return value || null; |
16398 } else if (isArray(require)) { |
16748 } else if (isArray(require)) { |
16399 value = []; |
16749 value = []; |
16400 forEach(require, function(require) { |
16750 forEach(require, function(require) { |
16401 value.push(getControllers(directiveName, require, $element, elementControllers)); |
16751 value.push(getControllers(directiveName, require, $element, elementControllers)); |
16402 }); |
16752 }); |
16419 |
16769 |
16420 if (newIsolateScopeDirective) { |
16770 if (newIsolateScopeDirective) { |
16421 isolateScope = scope.$new(true); |
16771 isolateScope = scope.$new(true); |
16422 } |
16772 } |
16423 |
16773 |
16424 transcludeFn = boundTranscludeFn && controllersBoundTransclude; |
16774 if (boundTranscludeFn) { |
16775 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` |
|
16776 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` |
|
16777 transcludeFn = controllersBoundTransclude; |
|
16778 transcludeFn.$$boundTransclude = boundTranscludeFn; |
|
16779 } |
|
16780 |
|
16425 if (controllerDirectives) { |
16781 if (controllerDirectives) { |
16426 // TODO: merge `controllers` and `elementControllers` into single object. |
16782 // TODO: merge `controllers` and `elementControllers` into single object. |
16427 controllers = {}; |
16783 controllers = {}; |
16428 elementControllers = {}; |
16784 elementControllers = {}; |
16429 forEach(controllerDirectives, function(directive) { |
16785 forEach(controllerDirectives, function(directive) { |
16454 controllers[directive.name] = controllerInstance; |
16810 controllers[directive.name] = controllerInstance; |
16455 }); |
16811 }); |
16456 } |
16812 } |
16457 |
16813 |
16458 if (newIsolateScopeDirective) { |
16814 if (newIsolateScopeDirective) { |
16459 var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; |
|
16460 |
|
16461 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || |
16815 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || |
16462 templateDirective === newIsolateScopeDirective.$$originalDirective))); |
16816 templateDirective === newIsolateScopeDirective.$$originalDirective))); |
16463 compile.$$addScopeClass($element, true); |
16817 compile.$$addScopeClass($element, true); |
16464 |
16818 |
16465 var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name]; |
16819 var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name]; |
16481 case '@': |
16835 case '@': |
16482 attrs.$observe(attrName, function(value) { |
16836 attrs.$observe(attrName, function(value) { |
16483 isolateBindingContext[scopeName] = value; |
16837 isolateBindingContext[scopeName] = value; |
16484 }); |
16838 }); |
16485 attrs.$$observers[attrName].$$scope = scope; |
16839 attrs.$$observers[attrName].$$scope = scope; |
16486 if( attrs[attrName] ) { |
16840 if (attrs[attrName]) { |
16487 // If the attribute has been provided then we trigger an interpolation to ensure |
16841 // If the attribute has been provided then we trigger an interpolation to ensure |
16488 // the value is there for use in the link fn |
16842 // the value is there for use in the link fn |
16489 isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope); |
16843 isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope); |
16490 } |
16844 } |
16491 break; |
16845 break; |
16496 } |
16850 } |
16497 parentGet = $parse(attrs[attrName]); |
16851 parentGet = $parse(attrs[attrName]); |
16498 if (parentGet.literal) { |
16852 if (parentGet.literal) { |
16499 compare = equals; |
16853 compare = equals; |
16500 } else { |
16854 } else { |
16501 compare = function(a,b) { return a === b || (a !== a && b !== b); }; |
16855 compare = function(a, b) { return a === b || (a !== a && b !== b); }; |
16502 } |
16856 } |
16503 parentSet = parentGet.assign || function() { |
16857 parentSet = parentGet.assign || function() { |
16504 // reset the change, or we will throw this exception on every $digest |
16858 // reset the change, or we will throw this exception on every $digest |
16505 lastValue = isolateBindingContext[scopeName] = parentGet(scope); |
16859 lastValue = isolateBindingContext[scopeName] = parentGet(scope); |
16506 throw $compileMinErr('nonassign', |
16860 throw $compileMinErr('nonassign', |
16520 } |
16874 } |
16521 } |
16875 } |
16522 return lastValue = parentValue; |
16876 return lastValue = parentValue; |
16523 }; |
16877 }; |
16524 parentValueWatch.$stateful = true; |
16878 parentValueWatch.$stateful = true; |
16525 var unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); |
16879 var unwatch; |
16880 if (definition.collection) { |
|
16881 unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); |
|
16882 } else { |
|
16883 unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); |
|
16884 } |
|
16526 isolateScope.$on('$destroy', unwatch); |
16885 isolateScope.$on('$destroy', unwatch); |
16527 break; |
16886 break; |
16528 |
16887 |
16529 case '&': |
16888 case '&': |
16530 parentGet = $parse(attrs[attrName]); |
16889 parentGet = $parse(attrs[attrName]); |
16541 }); |
16900 }); |
16542 controllers = null; |
16901 controllers = null; |
16543 } |
16902 } |
16544 |
16903 |
16545 // PRELINKING |
16904 // PRELINKING |
16546 for(i = 0, ii = preLinkFns.length; i < ii; i++) { |
16905 for (i = 0, ii = preLinkFns.length; i < ii; i++) { |
16547 linkFn = preLinkFns[i]; |
16906 linkFn = preLinkFns[i]; |
16548 invokeLinkFn(linkFn, |
16907 invokeLinkFn(linkFn, |
16549 linkFn.isolateScope ? isolateScope : scope, |
16908 linkFn.isolateScope ? isolateScope : scope, |
16550 $element, |
16909 $element, |
16551 attrs, |
16910 attrs, |
16562 scopeToChild = isolateScope; |
16921 scopeToChild = isolateScope; |
16563 } |
16922 } |
16564 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); |
16923 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); |
16565 |
16924 |
16566 // POSTLINKING |
16925 // POSTLINKING |
16567 for(i = postLinkFns.length - 1; i >= 0; i--) { |
16926 for (i = postLinkFns.length - 1; i >= 0; i--) { |
16568 linkFn = postLinkFns[i]; |
16927 linkFn = postLinkFns[i]; |
16569 invokeLinkFn(linkFn, |
16928 invokeLinkFn(linkFn, |
16570 linkFn.isolateScope ? isolateScope : scope, |
16929 linkFn.isolateScope ? isolateScope : scope, |
16571 $element, |
16930 $element, |
16572 attrs, |
16931 attrs, |
16622 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, |
16981 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, |
16623 endAttrName) { |
16982 endAttrName) { |
16624 if (name === ignoreDirective) return null; |
16983 if (name === ignoreDirective) return null; |
16625 var match = null; |
16984 var match = null; |
16626 if (hasDirectives.hasOwnProperty(name)) { |
16985 if (hasDirectives.hasOwnProperty(name)) { |
16627 for(var directive, directives = $injector.get(name + Suffix), |
16986 for (var directive, directives = $injector.get(name + Suffix), |
16628 i = 0, ii = directives.length; i<ii; i++) { |
16987 i = 0, ii = directives.length; i < ii; i++) { |
16629 try { |
16988 try { |
16630 directive = directives[i]; |
16989 directive = directives[i]; |
16631 if ( (maxPriority === undefined || maxPriority > directive.priority) && |
16990 if ((maxPriority === undefined || maxPriority > directive.priority) && |
16632 directive.restrict.indexOf(location) != -1) { |
16991 directive.restrict.indexOf(location) != -1) { |
16633 if (startAttrName) { |
16992 if (startAttrName) { |
16634 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); |
16993 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); |
16635 } |
16994 } |
16636 tDirectives.push(directive); |
16995 tDirectives.push(directive); |
16637 match = directive; |
16996 match = directive; |
16638 } |
16997 } |
16639 } catch(e) { $exceptionHandler(e); } |
16998 } catch (e) { $exceptionHandler(e); } |
16640 } |
16999 } |
16641 } |
17000 } |
16642 return match; |
17001 return match; |
16643 } |
17002 } |
16644 |
17003 |
16651 * @param {string} name name of the directive to look up. |
17010 * @param {string} name name of the directive to look up. |
16652 * @returns true if directive was registered as multi-element. |
17011 * @returns true if directive was registered as multi-element. |
16653 */ |
17012 */ |
16654 function directiveIsMultiElement(name) { |
17013 function directiveIsMultiElement(name) { |
16655 if (hasDirectives.hasOwnProperty(name)) { |
17014 if (hasDirectives.hasOwnProperty(name)) { |
16656 for(var directive, directives = $injector.get(name + Suffix), |
17015 for (var directive, directives = $injector.get(name + Suffix), |
16657 i = 0, ii = directives.length; i<ii; i++) { |
17016 i = 0, ii = directives.length; i < ii; i++) { |
16658 directive = directives[i]; |
17017 directive = directives[i]; |
16659 if (directive.multiElement) { |
17018 if (directive.multiElement) { |
16660 return true; |
17019 return true; |
16661 } |
17020 } |
16662 } |
17021 } |
16768 $rootElement[i] = $compileNode[0]; |
17127 $rootElement[i] = $compileNode[0]; |
16769 } |
17128 } |
16770 }); |
17129 }); |
16771 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); |
17130 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); |
16772 |
17131 |
16773 while(linkQueue.length) { |
17132 while (linkQueue.length) { |
16774 var scope = linkQueue.shift(), |
17133 var scope = linkQueue.shift(), |
16775 beforeTemplateLinkNode = linkQueue.shift(), |
17134 beforeTemplateLinkNode = linkQueue.shift(), |
16776 linkRootElement = linkQueue.shift(), |
17135 linkRootElement = linkQueue.shift(), |
16777 boundTranscludeFn = linkQueue.shift(), |
17136 boundTranscludeFn = linkQueue.shift(), |
16778 linkNode = $compileNode[0]; |
17137 linkNode = $compileNode[0]; |
16805 |
17164 |
16806 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { |
17165 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { |
16807 var childBoundTranscludeFn = boundTranscludeFn; |
17166 var childBoundTranscludeFn = boundTranscludeFn; |
16808 if (scope.$$destroyed) return; |
17167 if (scope.$$destroyed) return; |
16809 if (linkQueue) { |
17168 if (linkQueue) { |
16810 linkQueue.push(scope); |
17169 linkQueue.push(scope, |
16811 linkQueue.push(node); |
17170 node, |
16812 linkQueue.push(rootElement); |
17171 rootElement, |
16813 linkQueue.push(childBoundTranscludeFn); |
17172 childBoundTranscludeFn); |
16814 } else { |
17173 } else { |
16815 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { |
17174 if (afterTemplateNodeLinkFn.transcludeOnThisElement) { |
16816 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); |
17175 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); |
16817 } |
17176 } |
16818 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); |
17177 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); |
16867 } |
17226 } |
16868 |
17227 |
16869 |
17228 |
16870 function wrapTemplate(type, template) { |
17229 function wrapTemplate(type, template) { |
16871 type = lowercase(type || 'html'); |
17230 type = lowercase(type || 'html'); |
16872 switch(type) { |
17231 switch (type) { |
16873 case 'svg': |
17232 case 'svg': |
16874 case 'math': |
17233 case 'math': |
16875 var wrapper = document.createElement('div'); |
17234 var wrapper = document.createElement('div'); |
16876 wrapper.innerHTML = '<'+type+'>'+template+'</'+type+'>'; |
17235 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>'; |
16877 return wrapper.childNodes[0].childNodes; |
17236 return wrapper.childNodes[0].childNodes; |
16878 default: |
17237 default: |
16879 return template; |
17238 return template; |
16880 } |
17239 } |
16881 } |
17240 } |
16895 } |
17254 } |
16896 } |
17255 } |
16897 |
17256 |
16898 |
17257 |
16899 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { |
17258 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { |
16900 var interpolateFn = $interpolate(value, true); |
17259 var trustedContext = getTrustedContext(node, name); |
17260 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing; |
|
17261 |
|
17262 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing); |
|
16901 |
17263 |
16902 // no interpolation found -> ignore |
17264 // no interpolation found -> ignore |
16903 if (!interpolateFn) return; |
17265 if (!interpolateFn) return; |
16904 |
17266 |
16905 |
17267 |
16920 throw $compileMinErr('nodomevents', |
17282 throw $compileMinErr('nodomevents', |
16921 "Interpolations for HTML DOM event attributes are disallowed. Please use the " + |
17283 "Interpolations for HTML DOM event attributes are disallowed. Please use the " + |
16922 "ng- versions (such as ng-click instead of onclick) instead."); |
17284 "ng- versions (such as ng-click instead of onclick) instead."); |
16923 } |
17285 } |
16924 |
17286 |
16925 // If the attribute was removed, then we are done |
17287 // If the attribute has changed since last $interpolate()ed |
16926 if (!attr[name]) { |
17288 var newValue = attr[name]; |
16927 return; |
17289 if (newValue !== value) { |
17290 // we need to interpolate again since the attribute value has been updated |
|
17291 // (e.g. by another directive's compile function) |
|
17292 // ensure unset/empty values make interpolateFn falsy |
|
17293 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); |
|
17294 value = newValue; |
|
16928 } |
17295 } |
16929 |
|
16930 // we need to interpolate again, in case the attribute value has been updated |
|
16931 // (e.g. by another directive's compile function) |
|
16932 interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name), |
|
16933 ALL_OR_NOTHING_ATTRS[name] || allOrNothing); |
|
16934 |
17296 |
16935 // if attribute was updated so that there is no interpolation going on we don't want to |
17297 // if attribute was updated so that there is no interpolation going on we don't want to |
16936 // register any observers |
17298 // register any observers |
16937 if (!interpolateFn) return; |
17299 if (!interpolateFn) return; |
16938 |
17300 |
16948 //so that class changes can tap into the animation |
17310 //so that class changes can tap into the animation |
16949 //hooks provided by the $animate service. Be sure to |
17311 //hooks provided by the $animate service. Be sure to |
16950 //skip animations when the first digest occurs (when |
17312 //skip animations when the first digest occurs (when |
16951 //both the new and the old values are the same) since |
17313 //both the new and the old values are the same) since |
16952 //the CSS classes are the non-interpolated values |
17314 //the CSS classes are the non-interpolated values |
16953 if(name === 'class' && newValue != oldValue) { |
17315 if (name === 'class' && newValue != oldValue) { |
16954 attr.$updateClass(newValue, oldValue); |
17316 attr.$updateClass(newValue, oldValue); |
16955 } else { |
17317 } else { |
16956 attr.$set(name, newValue); |
17318 attr.$set(name, newValue); |
16957 } |
17319 } |
16958 }); |
17320 }); |
16978 removeCount = elementsToRemove.length, |
17340 removeCount = elementsToRemove.length, |
16979 parent = firstElementToRemove.parentNode, |
17341 parent = firstElementToRemove.parentNode, |
16980 i, ii; |
17342 i, ii; |
16981 |
17343 |
16982 if ($rootElement) { |
17344 if ($rootElement) { |
16983 for(i = 0, ii = $rootElement.length; i < ii; i++) { |
17345 for (i = 0, ii = $rootElement.length; i < ii; i++) { |
16984 if ($rootElement[i] == firstElementToRemove) { |
17346 if ($rootElement[i] == firstElementToRemove) { |
16985 $rootElement[i++] = newNode; |
17347 $rootElement[i++] = newNode; |
16986 for (var j = i, j2 = j + removeCount - 1, |
17348 for (var j = i, j2 = j + removeCount - 1, |
16987 jj = $rootElement.length; |
17349 jj = $rootElement.length; |
16988 j < jj; j++, j2++) { |
17350 j < jj; j++, j2++) { |
17053 |
17415 |
17054 |
17416 |
17055 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { |
17417 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { |
17056 try { |
17418 try { |
17057 linkFn(scope, $element, attrs, controllers, transcludeFn); |
17419 linkFn(scope, $element, attrs, controllers, transcludeFn); |
17058 } catch(e) { |
17420 } catch (e) { |
17059 $exceptionHandler(e, startingTag($element)); |
17421 $exceptionHandler(e, startingTag($element)); |
17060 } |
17422 } |
17061 } |
17423 } |
17062 }]; |
17424 }]; |
17063 } |
17425 } |
17064 |
17426 |
17065 var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; |
17427 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; |
17066 /** |
17428 /** |
17067 * Converts all accepted directives format into proper directive name. |
17429 * Converts all accepted directives format into proper directive name. |
17068 * All of these will become 'myDirective': |
|
17069 * my:Directive |
|
17070 * my-directive |
|
17071 * x-my-directive |
|
17072 * data-my:directive |
|
17073 * |
|
17074 * Also there is special case for Moz prefix starting with upper case letter. |
|
17075 * @param name Name to normalize |
17430 * @param name Name to normalize |
17076 */ |
17431 */ |
17077 function directiveNormalize(name) { |
17432 function directiveNormalize(name) { |
17078 return camelCase(name.replace(PREFIX_REGEXP, '')); |
17433 return camelCase(name.replace(PREFIX_REGEXP, '')); |
17079 } |
17434 } |
17126 function nodesetLinkingFn( |
17481 function nodesetLinkingFn( |
17127 /* angular.Scope */ scope, |
17482 /* angular.Scope */ scope, |
17128 /* NodeList */ nodeList, |
17483 /* NodeList */ nodeList, |
17129 /* Element */ rootElement, |
17484 /* Element */ rootElement, |
17130 /* function(Function) */ boundTranscludeFn |
17485 /* function(Function) */ boundTranscludeFn |
17131 ){} |
17486 ) {} |
17132 |
17487 |
17133 function directiveLinkingFn( |
17488 function directiveLinkingFn( |
17134 /* nodesetLinkingFn */ nodesetLinkingFn, |
17489 /* nodesetLinkingFn */ nodesetLinkingFn, |
17135 /* angular.Scope */ scope, |
17490 /* angular.Scope */ scope, |
17136 /* Node */ node, |
17491 /* Node */ node, |
17137 /* Element */ rootElement, |
17492 /* Element */ rootElement, |
17138 /* function(Function) */ boundTranscludeFn |
17493 /* function(Function) */ boundTranscludeFn |
17139 ){} |
17494 ) {} |
17140 |
17495 |
17141 function tokenDifference(str1, str2) { |
17496 function tokenDifference(str1, str2) { |
17142 var values = '', |
17497 var values = '', |
17143 tokens1 = str1.split(/\s+/), |
17498 tokens1 = str1.split(/\s+/), |
17144 tokens2 = str2.split(/\s+/); |
17499 tokens2 = str2.split(/\s+/); |
17145 |
17500 |
17146 outer: |
17501 outer: |
17147 for(var i = 0; i < tokens1.length; i++) { |
17502 for (var i = 0; i < tokens1.length; i++) { |
17148 var token = tokens1[i]; |
17503 var token = tokens1[i]; |
17149 for(var j = 0; j < tokens2.length; j++) { |
17504 for (var j = 0; j < tokens2.length; j++) { |
17150 if(token == tokens2[j]) continue outer; |
17505 if (token == tokens2[j]) continue outer; |
17151 } |
17506 } |
17152 values += (values.length > 0 ? ' ' : '') + token; |
17507 values += (values.length > 0 ? ' ' : '') + token; |
17153 } |
17508 } |
17154 return values; |
17509 return values; |
17155 } |
17510 } |
17227 * |
17582 * |
17228 * * check if a controller with given name is registered via `$controllerProvider` |
17583 * * check if a controller with given name is registered via `$controllerProvider` |
17229 * * check if evaluating the string on the current scope returns a constructor |
17584 * * check if evaluating the string on the current scope returns a constructor |
17230 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global |
17585 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global |
17231 * `window` object (not recommended) |
17586 * `window` object (not recommended) |
17587 * |
|
17588 * The string can use the `controller as property` syntax, where the controller instance is published |
|
17589 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this |
|
17590 * to work correctly. |
|
17232 * |
17591 * |
17233 * @param {Object} locals Injection locals for Controller. |
17592 * @param {Object} locals Injection locals for Controller. |
17234 * @return {Object} Instance of given controller. |
17593 * @return {Object} Instance of given controller. |
17235 * |
17594 * |
17236 * @description |
17595 * @description |
17251 later = later === true; |
17610 later = later === true; |
17252 if (ident && isString(ident)) { |
17611 if (ident && isString(ident)) { |
17253 identifier = ident; |
17612 identifier = ident; |
17254 } |
17613 } |
17255 |
17614 |
17256 if(isString(expression)) { |
17615 if (isString(expression)) { |
17257 match = expression.match(CNTRL_REG), |
17616 match = expression.match(CNTRL_REG), |
17258 constructor = match[1], |
17617 constructor = match[1], |
17259 identifier = identifier || match[3]; |
17618 identifier = identifier || match[3]; |
17260 expression = controllers.hasOwnProperty(constructor) |
17619 expression = controllers.hasOwnProperty(constructor) |
17261 ? controllers[constructor] |
17620 ? controllers[constructor] |
17273 // This allows properties to be added to the controller before the constructor is |
17632 // This allows properties to be added to the controller before the constructor is |
17274 // invoked. Primarily, this is used for isolate scope bindings in $compile. |
17633 // invoked. Primarily, this is used for isolate scope bindings in $compile. |
17275 // |
17634 // |
17276 // This feature is not intended for use by applications, and is thus not documented |
17635 // This feature is not intended for use by applications, and is thus not documented |
17277 // publicly. |
17636 // publicly. |
17278 var Constructor = function() {}; |
17637 // Object creation: http://jsperf.com/create-constructor/2 |
17279 Constructor.prototype = (isArray(expression) ? |
17638 var controllerPrototype = (isArray(expression) ? |
17280 expression[expression.length - 1] : expression).prototype; |
17639 expression[expression.length - 1] : expression).prototype; |
17281 instance = new Constructor(); |
17640 instance = Object.create(controllerPrototype); |
17282 |
17641 |
17283 if (identifier) { |
17642 if (identifier) { |
17284 addIdentifier(locals, identifier, instance, constructor || expression.name); |
17643 addIdentifier(locals, identifier, instance, constructor || expression.name); |
17285 } |
17644 } |
17286 |
17645 |
17337 $scope.windowTitle = angular.element(window.document)[0].title; |
17696 $scope.windowTitle = angular.element(window.document)[0].title; |
17338 }]); |
17697 }]); |
17339 </file> |
17698 </file> |
17340 </example> |
17699 </example> |
17341 */ |
17700 */ |
17342 function $DocumentProvider(){ |
17701 function $DocumentProvider() { |
17343 this.$get = ['$window', function(window){ |
17702 this.$get = ['$window', function(window) { |
17344 return jqLite(window.document); |
17703 return jqLite(window.document); |
17345 }]; |
17704 }]; |
17346 } |
17705 } |
17347 |
17706 |
17348 /** |
17707 /** |
17359 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. |
17718 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. |
17360 * |
17719 * |
17361 * ## Example: |
17720 * ## Example: |
17362 * |
17721 * |
17363 * ```js |
17722 * ```js |
17364 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function () { |
17723 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() { |
17365 * return function (exception, cause) { |
17724 * return function(exception, cause) { |
17366 * exception.message += ' (caused by "' + cause + '")'; |
17725 * exception.message += ' (caused by "' + cause + '")'; |
17367 * throw exception; |
17726 * throw exception; |
17368 * }; |
17727 * }; |
17369 * }); |
17728 * }); |
17370 * ``` |
17729 * ``` |
17371 * |
17730 * |
17372 * This example will override the normal action of `$exceptionHandler`, to make angular |
17731 * This example will override the normal action of `$exceptionHandler`, to make angular |
17373 * exceptions fail hard when they happen, instead of just logging to the console. |
17732 * exceptions fail hard when they happen, instead of just logging to the console. |
17733 * |
|
17734 * <hr /> |
|
17735 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` |
|
17736 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} |
|
17737 * (unless executed during a digest). |
|
17738 * |
|
17739 * If you wish, you can manually delegate exceptions, e.g. |
|
17740 * `try { ... } catch(e) { $exceptionHandler(e); }` |
|
17374 * |
17741 * |
17375 * @param {Error} exception Exception associated with the error. |
17742 * @param {Error} exception Exception associated with the error. |
17376 * @param {string=} cause optional information about the context in which |
17743 * @param {string=} cause optional information about the context in which |
17377 * the error was thrown. |
17744 * the error was thrown. |
17378 * |
17745 * |
17383 $log.error.apply($log, arguments); |
17750 $log.error.apply($log, arguments); |
17384 }; |
17751 }; |
17385 }]; |
17752 }]; |
17386 } |
17753 } |
17387 |
17754 |
17755 var APPLICATION_JSON = 'application/json'; |
|
17756 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; |
|
17757 var JSON_START = /^\[|^\{(?!\{)/; |
|
17758 var JSON_ENDS = { |
|
17759 '[': /]$/, |
|
17760 '{': /}$/ |
|
17761 }; |
|
17762 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; |
|
17763 |
|
17764 function defaultHttpResponseTransform(data, headers) { |
|
17765 if (isString(data)) { |
|
17766 // Strip json vulnerability protection prefix and trim whitespace |
|
17767 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); |
|
17768 |
|
17769 if (tempData) { |
|
17770 var contentType = headers('Content-Type'); |
|
17771 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { |
|
17772 data = fromJson(tempData); |
|
17773 } |
|
17774 } |
|
17775 } |
|
17776 |
|
17777 return data; |
|
17778 } |
|
17779 |
|
17780 function isJsonLike(str) { |
|
17781 var jsonStart = str.match(JSON_START); |
|
17782 return jsonStart && JSON_ENDS[jsonStart[0]].test(str); |
|
17783 } |
|
17784 |
|
17388 /** |
17785 /** |
17389 * Parse headers into key value object |
17786 * Parse headers into key value object |
17390 * |
17787 * |
17391 * @param {string} headers Raw headers as a string |
17788 * @param {string} headers Raw headers as a string |
17392 * @returns {Object} Parsed headers as key value object |
17789 * @returns {Object} Parsed headers as key value object |
17393 */ |
17790 */ |
17394 function parseHeaders(headers) { |
17791 function parseHeaders(headers) { |
17395 var parsed = {}, key, val, i; |
17792 var parsed = createMap(), key, val, i; |
17396 |
17793 |
17397 if (!headers) return parsed; |
17794 if (!headers) return parsed; |
17398 |
17795 |
17399 forEach(headers.split('\n'), function(line) { |
17796 forEach(headers.split('\n'), function(line) { |
17400 i = line.indexOf(':'); |
17797 i = line.indexOf(':'); |
17427 |
17824 |
17428 return function(name) { |
17825 return function(name) { |
17429 if (!headersObj) headersObj = parseHeaders(headers); |
17826 if (!headersObj) headersObj = parseHeaders(headers); |
17430 |
17827 |
17431 if (name) { |
17828 if (name) { |
17432 return headersObj[lowercase(name)] || null; |
17829 var value = headersObj[lowercase(name)]; |
17830 if (value === void 0) { |
|
17831 value = null; |
|
17832 } |
|
17833 return value; |
|
17433 } |
17834 } |
17434 |
17835 |
17435 return headersObj; |
17836 return headersObj; |
17436 }; |
17837 }; |
17437 } |
17838 } |
17441 * Chain all given functions |
17842 * Chain all given functions |
17442 * |
17843 * |
17443 * This function is used for both request and response transforming |
17844 * This function is used for both request and response transforming |
17444 * |
17845 * |
17445 * @param {*} data Data to transform. |
17846 * @param {*} data Data to transform. |
17446 * @param {function(string=)} headers Http headers getter fn. |
17847 * @param {function(string=)} headers HTTP headers getter fn. |
17848 * @param {number} status HTTP status code of the response. |
|
17447 * @param {(Function|Array.<Function>)} fns Function or an array of functions. |
17849 * @param {(Function|Array.<Function>)} fns Function or an array of functions. |
17448 * @returns {*} Transformed data. |
17850 * @returns {*} Transformed data. |
17449 */ |
17851 */ |
17450 function transformData(data, headers, fns) { |
17852 function transformData(data, headers, status, fns) { |
17451 if (isFunction(fns)) |
17853 if (isFunction(fns)) |
17452 return fns(data, headers); |
17854 return fns(data, headers, status); |
17453 |
17855 |
17454 forEach(fns, function(fn) { |
17856 forEach(fns, function(fn) { |
17455 data = fn(data, headers); |
17857 data = fn(data, headers, status); |
17456 }); |
17858 }); |
17457 |
17859 |
17458 return data; |
17860 return data; |
17459 } |
17861 } |
17460 |
17862 |
17469 * @name $httpProvider |
17871 * @name $httpProvider |
17470 * @description |
17872 * @description |
17471 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. |
17873 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. |
17472 * */ |
17874 * */ |
17473 function $HttpProvider() { |
17875 function $HttpProvider() { |
17474 var JSON_START = /^\s*(\[|\{[^\{])/, |
|
17475 JSON_END = /[\}\]]\s*$/, |
|
17476 PROTECTION_PREFIX = /^\)\]\}',?\n/, |
|
17477 APPLICATION_JSON = 'application/json', |
|
17478 CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; |
|
17479 |
|
17480 /** |
17876 /** |
17481 * @ngdoc property |
17877 * @ngdoc property |
17482 * @name $httpProvider#defaults |
17878 * @name $httpProvider#defaults |
17483 * @description |
17879 * @description |
17484 * |
17880 * |
17485 * Object containing default values for all {@link ng.$http $http} requests. |
17881 * Object containing default values for all {@link ng.$http $http} requests. |
17882 * |
|
17883 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`} |
|
17884 * that will provide the cache for all requests who set their `cache` property to `true`. |
|
17885 * If you set the `default.cache = false` then only requests that specify their own custom |
|
17886 * cache object will be cached. See {@link $http#caching $http Caching} for more information. |
|
17486 * |
17887 * |
17487 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. |
17888 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. |
17488 * Defaults value is `'XSRF-TOKEN'`. |
17889 * Defaults value is `'XSRF-TOKEN'`. |
17489 * |
17890 * |
17490 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the |
17891 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the |
17495 * setting default headers. |
17896 * setting default headers. |
17496 * - **`defaults.headers.common`** |
17897 * - **`defaults.headers.common`** |
17497 * - **`defaults.headers.post`** |
17898 * - **`defaults.headers.post`** |
17498 * - **`defaults.headers.put`** |
17899 * - **`defaults.headers.put`** |
17499 * - **`defaults.headers.patch`** |
17900 * - **`defaults.headers.patch`** |
17901 * |
|
17500 **/ |
17902 **/ |
17501 var defaults = this.defaults = { |
17903 var defaults = this.defaults = { |
17502 // transform incoming response data |
17904 // transform incoming response data |
17503 transformResponse: [function defaultHttpResponseTransform(data, headers) { |
17905 transformResponse: [defaultHttpResponseTransform], |
17504 if (isString(data)) { |
|
17505 // strip json vulnerability protection prefix |
|
17506 data = data.replace(PROTECTION_PREFIX, ''); |
|
17507 var contentType = headers('Content-Type'); |
|
17508 if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) || |
|
17509 (JSON_START.test(data) && JSON_END.test(data))) { |
|
17510 data = fromJson(data); |
|
17511 } |
|
17512 } |
|
17513 return data; |
|
17514 }], |
|
17515 |
17906 |
17516 // transform outgoing request data |
17907 // transform outgoing request data |
17517 transformRequest: [function(d) { |
17908 transformRequest: [function(d) { |
17518 return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d; |
17909 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; |
17519 }], |
17910 }], |
17520 |
17911 |
17521 // default headers |
17912 // default headers |
17522 headers: { |
17913 headers: { |
17523 common: { |
17914 common: { |
17537 * @ngdoc method |
17928 * @ngdoc method |
17538 * @name $httpProvider#useApplyAsync |
17929 * @name $httpProvider#useApplyAsync |
17539 * @description |
17930 * @description |
17540 * |
17931 * |
17541 * Configure $http service to combine processing of multiple http responses received at around |
17932 * Configure $http service to combine processing of multiple http responses received at around |
17542 * the same time via {@link ng.$rootScope#applyAsync $rootScope.$applyAsync}. This can result in |
17933 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in |
17543 * significant performance improvement for bigger applications that make many HTTP requests |
17934 * significant performance improvement for bigger applications that make many HTTP requests |
17544 * concurrently (common during application bootstrap). |
17935 * concurrently (common during application bootstrap). |
17545 * |
17936 * |
17546 * Defaults to false. If no value is specifed, returns the current configured value. |
17937 * Defaults to false. If no value is specifed, returns the current configured value. |
17547 * |
17938 * |
17559 } |
17950 } |
17560 return useApplyAsync; |
17951 return useApplyAsync; |
17561 }; |
17952 }; |
17562 |
17953 |
17563 /** |
17954 /** |
17564 * Are ordered by request, i.e. they are applied in the same order as the |
17955 * @ngdoc property |
17956 * @name $httpProvider#interceptors |
|
17957 * @description |
|
17958 * |
|
17959 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} |
|
17960 * pre-processing of request or postprocessing of responses. |
|
17961 * |
|
17962 * These service factories are ordered by request, i.e. they are applied in the same order as the |
|
17565 * array, on request, but reverse order, on response. |
17963 * array, on request, but reverse order, on response. |
17566 */ |
17964 * |
17965 * {@link ng.$http#interceptors Interceptors detailed info} |
|
17966 **/ |
|
17567 var interceptorFactories = this.interceptors = []; |
17967 var interceptorFactories = this.interceptors = []; |
17568 |
17968 |
17569 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', |
17969 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', |
17570 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { |
17970 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { |
17571 |
17971 |
17613 * The `$http` service is a function which takes a single argument — a configuration object — |
18013 * The `$http` service is a function which takes a single argument — a configuration object — |
17614 * that is used to generate an HTTP request and returns a {@link ng.$q promise} |
18014 * that is used to generate an HTTP request and returns a {@link ng.$q promise} |
17615 * with two $http specific methods: `success` and `error`. |
18015 * with two $http specific methods: `success` and `error`. |
17616 * |
18016 * |
17617 * ```js |
18017 * ```js |
17618 * $http({method: 'GET', url: '/someUrl'}). |
18018 * // Simple GET request example : |
18019 * $http.get('/someUrl'). |
|
17619 * success(function(data, status, headers, config) { |
18020 * success(function(data, status, headers, config) { |
17620 * // this callback will be called asynchronously |
18021 * // this callback will be called asynchronously |
17621 * // when the response is available |
18022 * // when the response is available |
17622 * }). |
18023 * }). |
17623 * error(function(data, status, headers, config) { |
18024 * error(function(data, status, headers, config) { |
17624 * // called asynchronously if an error occurs |
18025 * // called asynchronously if an error occurs |
17625 * // or server returns response with an error status. |
18026 * // or server returns response with an error status. |
17626 * }); |
18027 * }); |
17627 * ``` |
18028 * ``` |
18029 * |
|
18030 * ```js |
|
18031 * // Simple POST request example (passing data) : |
|
18032 * $http.post('/someUrl', {msg:'hello word!'}). |
|
18033 * success(function(data, status, headers, config) { |
|
18034 * // this callback will be called asynchronously |
|
18035 * // when the response is available |
|
18036 * }). |
|
18037 * error(function(data, status, headers, config) { |
|
18038 * // called asynchronously if an error occurs |
|
18039 * // or server returns response with an error status. |
|
18040 * }); |
|
18041 * ``` |
|
18042 * |
|
17628 * |
18043 * |
17629 * Since the returned value of calling the $http function is a `promise`, you can also use |
18044 * Since the returned value of calling the $http function is a `promise`, you can also use |
17630 * the `then` method to register callbacks, and these callbacks will receive a single argument – |
18045 * the `then` method to register callbacks, and these callbacks will receive a single argument – |
17631 * an object representing the response. See the API signature and type info below for more |
18046 * an object representing the response. See the API signature and type info below for more |
17632 * details. |
18047 * details. |
17696 * ``` |
18111 * ``` |
17697 * |
18112 * |
17698 * In addition, you can supply a `headers` property in the config object passed when |
18113 * In addition, you can supply a `headers` property in the config object passed when |
17699 * calling `$http(config)`, which overrides the defaults without changing them globally. |
18114 * calling `$http(config)`, which overrides the defaults without changing them globally. |
17700 * |
18115 * |
18116 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, |
|
18117 * Use the `headers` property, setting the desired header to `undefined`. For example: |
|
18118 * |
|
18119 * ```js |
|
18120 * var req = { |
|
18121 * method: 'POST', |
|
18122 * url: 'http://example.com', |
|
18123 * headers: { |
|
18124 * 'Content-Type': undefined |
|
18125 * }, |
|
18126 * data: { test: 'test' }, |
|
18127 * } |
|
18128 * |
|
18129 * $http(req).success(function(){...}).error(function(){...}); |
|
18130 * ``` |
|
17701 * |
18131 * |
17702 * ## Transforming Requests and Responses |
18132 * ## Transforming Requests and Responses |
17703 * |
18133 * |
17704 * Both requests and responses can be transformed using transformation functions: `transformRequest` |
18134 * Both requests and responses can be transformed using transformation functions: `transformRequest` |
17705 * and `transformResponse`. These properties can be a single function that returns |
18135 * and `transformResponse`. These properties can be a single function that returns |
17706 * the transformed value (`{function(data, headersGetter)`) or an array of such transformation functions, |
18136 * the transformed value (`{function(data, headersGetter, status)`) or an array of such transformation functions, |
17707 * which allows you to `push` or `unshift` a new transformation function into the transformation chain. |
18137 * which allows you to `push` or `unshift` a new transformation function into the transformation chain. |
17708 * |
18138 * |
17709 * ### Default Transformations |
18139 * ### Default Transformations |
17710 * |
18140 * |
17711 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and |
18141 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and |
17776 * cache, but the cache is not populated yet, only one request to the server will be made and |
18206 * cache, but the cache is not populated yet, only one request to the server will be made and |
17777 * the remaining requests will be fulfilled using the response from the first request. |
18207 * the remaining requests will be fulfilled using the response from the first request. |
17778 * |
18208 * |
17779 * You can change the default cache to a new object (built with |
18209 * You can change the default cache to a new object (built with |
17780 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the |
18210 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the |
17781 * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set |
18211 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set |
17782 * their `cache` property to `true` will now use this cache object. |
18212 * their `cache` property to `true` will now use this cache object. |
17783 * |
18213 * |
17784 * If you set the default cache to `false` then only requests that specify their own custom |
18214 * If you set the default cache to `false` then only requests that specify their own custom |
17785 * cache object will be cached. |
18215 * cache object will be cached. |
17786 * |
18216 * |
17942 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. |
18372 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. |
17943 * - **transformRequest** – |
18373 * - **transformRequest** – |
17944 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – |
18374 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – |
17945 * transform function or an array of such functions. The transform function takes the http |
18375 * transform function or an array of such functions. The transform function takes the http |
17946 * request body and headers and returns its transformed (typically serialized) version. |
18376 * request body and headers and returns its transformed (typically serialized) version. |
17947 * See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations} |
18377 * See {@link ng.$http#overriding-the-default-transformations-per-request |
18378 * Overriding the Default Transformations} |
|
17948 * - **transformResponse** – |
18379 * - **transformResponse** – |
17949 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – |
18380 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` – |
17950 * transform function or an array of such functions. The transform function takes the http |
18381 * transform function or an array of such functions. The transform function takes the http |
17951 * response body and headers and returns its transformed (typically deserialized) version. |
18382 * response body, headers and status and returns its transformed (typically deserialized) version. |
17952 * See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations} |
18383 * See {@link ng.$http#overriding-the-default-transformations-per-request |
18384 * Overriding the Default Transformations} |
|
17953 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the |
18385 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the |
17954 * GET request, otherwise if a cache instance built with |
18386 * GET request, otherwise if a cache instance built with |
17955 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for |
18387 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for |
17956 * caching. |
18388 * caching. |
17957 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} |
18389 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} |
18068 }); |
18500 }); |
18069 </file> |
18501 </file> |
18070 </example> |
18502 </example> |
18071 */ |
18503 */ |
18072 function $http(requestConfig) { |
18504 function $http(requestConfig) { |
18073 var config = { |
18505 |
18506 if (!angular.isObject(requestConfig)) { |
|
18507 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); |
|
18508 } |
|
18509 |
|
18510 var config = extend({ |
|
18074 method: 'get', |
18511 method: 'get', |
18075 transformRequest: defaults.transformRequest, |
18512 transformRequest: defaults.transformRequest, |
18076 transformResponse: defaults.transformResponse |
18513 transformResponse: defaults.transformResponse |
18077 }; |
18514 }, requestConfig); |
18078 var headers = mergeHeaders(requestConfig); |
18515 |
18079 |
18516 config.headers = mergeHeaders(requestConfig); |
18080 extend(config, requestConfig); |
|
18081 config.headers = headers; |
|
18082 config.method = uppercase(config.method); |
18517 config.method = uppercase(config.method); |
18083 |
18518 |
18084 var serverRequest = function(config) { |
18519 var serverRequest = function(config) { |
18085 headers = config.headers; |
18520 var headers = config.headers; |
18086 var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); |
18521 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); |
18087 |
18522 |
18088 // strip content-type if data is undefined |
18523 // strip content-type if data is undefined |
18089 if (isUndefined(reqData)) { |
18524 if (isUndefined(reqData)) { |
18090 forEach(headers, function(value, header) { |
18525 forEach(headers, function(value, header) { |
18091 if (lowercase(header) === 'content-type') { |
18526 if (lowercase(header) === 'content-type') { |
18097 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { |
18532 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { |
18098 config.withCredentials = defaults.withCredentials; |
18533 config.withCredentials = defaults.withCredentials; |
18099 } |
18534 } |
18100 |
18535 |
18101 // send request |
18536 // send request |
18102 return sendReq(config, reqData, headers).then(transformResponse, transformResponse); |
18537 return sendReq(config, reqData).then(transformResponse, transformResponse); |
18103 }; |
18538 }; |
18104 |
18539 |
18105 var chain = [serverRequest, undefined]; |
18540 var chain = [serverRequest, undefined]; |
18106 var promise = $q.when(config); |
18541 var promise = $q.when(config); |
18107 |
18542 |
18113 if (interceptor.response || interceptor.responseError) { |
18548 if (interceptor.response || interceptor.responseError) { |
18114 chain.push(interceptor.response, interceptor.responseError); |
18549 chain.push(interceptor.response, interceptor.responseError); |
18115 } |
18550 } |
18116 }); |
18551 }); |
18117 |
18552 |
18118 while(chain.length) { |
18553 while (chain.length) { |
18119 var thenFn = chain.shift(); |
18554 var thenFn = chain.shift(); |
18120 var rejectFn = chain.shift(); |
18555 var rejectFn = chain.shift(); |
18121 |
18556 |
18122 promise = promise.then(thenFn, rejectFn); |
18557 promise = promise.then(thenFn, rejectFn); |
18123 } |
18558 } |
18138 |
18573 |
18139 return promise; |
18574 return promise; |
18140 |
18575 |
18141 function transformResponse(response) { |
18576 function transformResponse(response) { |
18142 // make a copy since the response must be cacheable |
18577 // make a copy since the response must be cacheable |
18143 var resp = extend({}, response, { |
18578 var resp = extend({}, response); |
18144 data: transformData(response.data, response.headers, config.transformResponse) |
18579 if (!response.data) { |
18145 }); |
18580 resp.data = response.data; |
18581 } else { |
|
18582 resp.data = transformData(response.data, response.headers, response.status, config.transformResponse); |
|
18583 } |
|
18146 return (isSuccess(response.status)) |
18584 return (isSuccess(response.status)) |
18147 ? resp |
18585 ? resp |
18148 : $q.reject(resp); |
18586 : $q.reject(resp); |
18587 } |
|
18588 |
|
18589 function executeHeaderFns(headers) { |
|
18590 var headerContent, processedHeaders = {}; |
|
18591 |
|
18592 forEach(headers, function(headerFn, header) { |
|
18593 if (isFunction(headerFn)) { |
|
18594 headerContent = headerFn(); |
|
18595 if (headerContent != null) { |
|
18596 processedHeaders[header] = headerContent; |
|
18597 } |
|
18598 } else { |
|
18599 processedHeaders[header] = headerFn; |
|
18600 } |
|
18601 }); |
|
18602 |
|
18603 return processedHeaders; |
|
18149 } |
18604 } |
18150 |
18605 |
18151 function mergeHeaders(config) { |
18606 function mergeHeaders(config) { |
18152 var defHeaders = defaults.headers, |
18607 var defHeaders = defaults.headers, |
18153 reqHeaders = extend({}, config.headers), |
18608 reqHeaders = extend({}, config.headers), |
18168 |
18623 |
18169 reqHeaders[defHeaderName] = defHeaders[defHeaderName]; |
18624 reqHeaders[defHeaderName] = defHeaders[defHeaderName]; |
18170 } |
18625 } |
18171 |
18626 |
18172 // execute if header value is a function for merged headers |
18627 // execute if header value is a function for merged headers |
18173 execHeaders(reqHeaders); |
18628 return executeHeaderFns(reqHeaders); |
18174 return reqHeaders; |
|
18175 |
|
18176 function execHeaders(headers) { |
|
18177 var headerContent; |
|
18178 |
|
18179 forEach(headers, function(headerFn, header) { |
|
18180 if (isFunction(headerFn)) { |
|
18181 headerContent = headerFn(); |
|
18182 if (headerContent != null) { |
|
18183 headers[header] = headerContent; |
|
18184 } else { |
|
18185 delete headers[header]; |
|
18186 } |
|
18187 } |
|
18188 }); |
|
18189 } |
|
18190 } |
18629 } |
18191 } |
18630 } |
18192 |
18631 |
18193 $http.pendingRequests = []; |
18632 $http.pendingRequests = []; |
18194 |
18633 |
18327 * Makes the request. |
18766 * Makes the request. |
18328 * |
18767 * |
18329 * !!! ACCESSES CLOSURE VARS: |
18768 * !!! ACCESSES CLOSURE VARS: |
18330 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests |
18769 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests |
18331 */ |
18770 */ |
18332 function sendReq(config, reqData, reqHeaders) { |
18771 function sendReq(config, reqData) { |
18333 var deferred = $q.defer(), |
18772 var deferred = $q.defer(), |
18334 promise = deferred.promise, |
18773 promise = deferred.promise, |
18335 cache, |
18774 cache, |
18336 cachedResp, |
18775 cachedResp, |
18776 reqHeaders = config.headers, |
|
18337 url = buildUrl(config.url, config.params); |
18777 url = buildUrl(config.url, config.params); |
18338 |
18778 |
18339 $http.pendingRequests.push(config); |
18779 $http.pendingRequests.push(config); |
18340 promise.then(removePendingReq, removePendingReq); |
18780 promise.then(removePendingReq, removePendingReq); |
18341 |
18781 |
18350 if (cache) { |
18790 if (cache) { |
18351 cachedResp = cache.get(url); |
18791 cachedResp = cache.get(url); |
18352 if (isDefined(cachedResp)) { |
18792 if (isDefined(cachedResp)) { |
18353 if (isPromiseLike(cachedResp)) { |
18793 if (isPromiseLike(cachedResp)) { |
18354 // cached request has already been sent, but there is no response yet |
18794 // cached request has already been sent, but there is no response yet |
18355 cachedResp.then(removePendingReq, removePendingReq); |
18795 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); |
18356 return cachedResp; |
|
18357 } else { |
18796 } else { |
18358 // serving from cache |
18797 // serving from cache |
18359 if (isArray(cachedResp)) { |
18798 if (isArray(cachedResp)) { |
18360 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); |
18799 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); |
18361 } else { |
18800 } else { |
18425 (isSuccess(status) ? deferred.resolve : deferred.reject)({ |
18864 (isSuccess(status) ? deferred.resolve : deferred.reject)({ |
18426 data: response, |
18865 data: response, |
18427 status: status, |
18866 status: status, |
18428 headers: headersGetter(headers), |
18867 headers: headersGetter(headers), |
18429 config: config, |
18868 config: config, |
18430 statusText : statusText |
18869 statusText: statusText |
18431 }); |
18870 }); |
18432 } |
18871 } |
18433 |
18872 |
18873 function resolvePromiseWithResult(result) { |
|
18874 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); |
|
18875 } |
|
18434 |
18876 |
18435 function removePendingReq() { |
18877 function removePendingReq() { |
18436 var idx = $http.pendingRequests.indexOf(config); |
18878 var idx = $http.pendingRequests.indexOf(config); |
18437 if (idx !== -1) $http.pendingRequests.splice(idx, 1); |
18879 if (idx !== -1) $http.pendingRequests.splice(idx, 1); |
18438 } |
18880 } |
18446 if (value === null || isUndefined(value)) return; |
18888 if (value === null || isUndefined(value)) return; |
18447 if (!isArray(value)) value = [value]; |
18889 if (!isArray(value)) value = [value]; |
18448 |
18890 |
18449 forEach(value, function(v) { |
18891 forEach(value, function(v) { |
18450 if (isObject(v)) { |
18892 if (isObject(v)) { |
18451 if (isDate(v)){ |
18893 if (isDate(v)) { |
18452 v = v.toISOString(); |
18894 v = v.toISOString(); |
18453 } else { |
18895 } else { |
18454 v = toJson(v); |
18896 v = toJson(v); |
18455 } |
18897 } |
18456 } |
18898 } |
18457 parts.push(encodeUriQuery(key) + '=' + |
18899 parts.push(encodeUriQuery(key) + '=' + |
18458 encodeUriQuery(v)); |
18900 encodeUriQuery(v)); |
18459 }); |
18901 }); |
18460 }); |
18902 }); |
18461 if(parts.length > 0) { |
18903 if (parts.length > 0) { |
18462 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); |
18904 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); |
18463 } |
18905 } |
18464 return url; |
18906 return url; |
18465 } |
18907 } |
18466 }]; |
18908 }]; |
18543 response, |
18985 response, |
18544 xhr.getAllResponseHeaders(), |
18986 xhr.getAllResponseHeaders(), |
18545 statusText); |
18987 statusText); |
18546 }; |
18988 }; |
18547 |
18989 |
18548 var requestError = function () { |
18990 var requestError = function() { |
18549 // The response is always empty |
18991 // The response is always empty |
18550 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error |
18992 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error |
18551 completeRequest(callback, -1, null, null, ''); |
18993 completeRequest(callback, -1, null, null, ''); |
18552 }; |
18994 }; |
18553 |
18995 |
18590 xhr && xhr.abort(); |
19032 xhr && xhr.abort(); |
18591 } |
19033 } |
18592 |
19034 |
18593 function completeRequest(callback, status, response, headersString, statusText) { |
19035 function completeRequest(callback, status, response, headersString, statusText) { |
18594 // cancel timeout and subsequent timeout promise resolution |
19036 // cancel timeout and subsequent timeout promise resolution |
18595 timeoutId && $browserDefer.cancel(timeoutId); |
19037 if (timeoutId !== undefined) { |
19038 $browserDefer.cancel(timeoutId); |
|
19039 } |
|
18596 jsonpDone = xhr = null; |
19040 jsonpDone = xhr = null; |
18597 |
19041 |
18598 callback(status, response, headersString, statusText); |
19042 callback(status, response, headersString, statusText); |
18599 $browser.$$completeOutstandingRequest(noop); |
19043 $browser.$$completeOutstandingRequest(noop); |
18600 } |
19044 } |
18685 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. |
19129 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. |
18686 * |
19130 * |
18687 * @param {string=} value new value to set the starting symbol to. |
19131 * @param {string=} value new value to set the starting symbol to. |
18688 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. |
19132 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. |
18689 */ |
19133 */ |
18690 this.startSymbol = function(value){ |
19134 this.startSymbol = function(value) { |
18691 if (value) { |
19135 if (value) { |
18692 startSymbol = value; |
19136 startSymbol = value; |
18693 return this; |
19137 return this; |
18694 } else { |
19138 } else { |
18695 return startSymbol; |
19139 return startSymbol; |
18703 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. |
19147 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. |
18704 * |
19148 * |
18705 * @param {string=} value new value to set the ending symbol to. |
19149 * @param {string=} value new value to set the ending symbol to. |
18706 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. |
19150 * @returns {string|self} Returns the symbol when used as getter and self if used as setter. |
18707 */ |
19151 */ |
18708 this.endSymbol = function(value){ |
19152 this.endSymbol = function(value) { |
18709 if (value) { |
19153 if (value) { |
18710 endSymbol = value; |
19154 endSymbol = value; |
18711 return this; |
19155 return this; |
18712 } else { |
19156 } else { |
18713 return endSymbol; |
19157 return endSymbol; |
18829 textLength = text.length, |
19273 textLength = text.length, |
18830 exp, |
19274 exp, |
18831 concat = [], |
19275 concat = [], |
18832 expressionPositions = []; |
19276 expressionPositions = []; |
18833 |
19277 |
18834 while(index < textLength) { |
19278 while (index < textLength) { |
18835 if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && |
19279 if (((startIndex = text.indexOf(startSymbol, index)) != -1) && |
18836 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { |
19280 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) { |
18837 if (index !== startIndex) { |
19281 if (index !== startIndex) { |
18838 concat.push(unescapeText(text.substring(index, startIndex))); |
19282 concat.push(unescapeText(text.substring(index, startIndex))); |
18839 } |
19283 } |
18840 exp = text.substring(startIndex + startSymbolLength, endIndex); |
19284 exp = text.substring(startIndex + startSymbolLength, endIndex); |
18841 expressions.push(exp); |
19285 expressions.push(exp); |
18865 "required. See http://docs.angularjs.org/api/ng.$sce", text); |
19309 "required. See http://docs.angularjs.org/api/ng.$sce", text); |
18866 } |
19310 } |
18867 |
19311 |
18868 if (!mustHaveExpression || expressions.length) { |
19312 if (!mustHaveExpression || expressions.length) { |
18869 var compute = function(values) { |
19313 var compute = function(values) { |
18870 for(var i = 0, ii = expressions.length; i < ii; i++) { |
19314 for (var i = 0, ii = expressions.length; i < ii; i++) { |
18871 if (allOrNothing && isUndefined(values[i])) return; |
19315 if (allOrNothing && isUndefined(values[i])) return; |
18872 concat[expressionPositions[i]] = values[i]; |
19316 concat[expressionPositions[i]] = values[i]; |
18873 } |
19317 } |
18874 return concat.join(''); |
19318 return concat.join(''); |
18875 }; |
19319 }; |
18876 |
19320 |
18877 var getValue = function (value) { |
19321 var getValue = function(value) { |
18878 return trustedContext ? |
19322 return trustedContext ? |
18879 $sce.getTrusted(trustedContext, value) : |
19323 $sce.getTrusted(trustedContext, value) : |
18880 $sce.valueOf(value); |
19324 $sce.valueOf(value); |
18881 }; |
19325 }; |
18882 |
19326 |
18883 var stringify = function (value) { |
19327 var stringify = function(value) { |
18884 if (value == null) { // null || undefined |
19328 if (value == null) { // null || undefined |
18885 return ''; |
19329 return ''; |
18886 } |
19330 } |
18887 switch (typeof value) { |
19331 switch (typeof value) { |
18888 case 'string': { |
19332 case 'string': |
18889 break; |
19333 break; |
18890 } |
19334 case 'number': |
18891 case 'number': { |
|
18892 value = '' + value; |
19335 value = '' + value; |
18893 break; |
19336 break; |
18894 } |
19337 default: |
18895 default: { |
|
18896 value = toJson(value); |
19338 value = toJson(value); |
18897 } |
|
18898 } |
19339 } |
18899 |
19340 |
18900 return value; |
19341 return value; |
18901 }; |
19342 }; |
18902 |
19343 |
18909 for (; i < ii; i++) { |
19350 for (; i < ii; i++) { |
18910 values[i] = parseFns[i](context); |
19351 values[i] = parseFns[i](context); |
18911 } |
19352 } |
18912 |
19353 |
18913 return compute(values); |
19354 return compute(values); |
18914 } catch(err) { |
19355 } catch (err) { |
18915 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, |
19356 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, |
18916 err.toString()); |
19357 err.toString()); |
18917 $exceptionHandler(newErr); |
19358 $exceptionHandler(newErr); |
18918 } |
19359 } |
18919 |
19360 |
18920 }, { |
19361 }, { |
18921 // all of these properties are undocumented for now |
19362 // all of these properties are undocumented for now |
18922 exp: text, //just for compatibility with regular watchers created via $watch |
19363 exp: text, //just for compatibility with regular watchers created via $watch |
18923 expressions: expressions, |
19364 expressions: expressions, |
18924 $$watchDelegate: function (scope, listener, objectEquality) { |
19365 $$watchDelegate: function(scope, listener, objectEquality) { |
18925 var lastValue; |
19366 var lastValue; |
18926 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { |
19367 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { |
18927 var currValue = compute(values); |
19368 var currValue = compute(values); |
18928 if (isFunction(listener)) { |
19369 if (isFunction(listener)) { |
18929 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); |
19370 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); |
18939 replace(escapedEndRegexp, endSymbol); |
19380 replace(escapedEndRegexp, endSymbol); |
18940 } |
19381 } |
18941 |
19382 |
18942 function parseStringifyInterceptor(value) { |
19383 function parseStringifyInterceptor(value) { |
18943 try { |
19384 try { |
18944 return stringify(getValue(value)); |
19385 value = getValue(value); |
18945 } catch(err) { |
19386 return allOrNothing && !isDefined(value) ? value : stringify(value); |
19387 } catch (err) { |
|
18946 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, |
19388 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, |
18947 err.toString()); |
19389 err.toString()); |
18948 $exceptionHandler(newErr); |
19390 $exceptionHandler(newErr); |
18949 } |
19391 } |
18950 } |
19392 } |
19040 * var stop; |
19482 * var stop; |
19041 * $scope.fight = function() { |
19483 * $scope.fight = function() { |
19042 * // Don't start a new fight if we are already fighting |
19484 * // Don't start a new fight if we are already fighting |
19043 * if ( angular.isDefined(stop) ) return; |
19485 * if ( angular.isDefined(stop) ) return; |
19044 * |
19486 * |
19045 * stop = $interval(function() { |
19487 * stop = $interval(function() { |
19046 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { |
19488 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { |
19047 * $scope.blood_1 = $scope.blood_1 - 3; |
19489 * $scope.blood_1 = $scope.blood_1 - 3; |
19048 * $scope.blood_2 = $scope.blood_2 - 4; |
19490 * $scope.blood_2 = $scope.blood_2 - 4; |
19049 * } else { |
19491 * } else { |
19050 * $scope.stopFight(); |
19492 * $scope.stopFight(); |
19493 * } |
|
19494 * }, 100); |
|
19495 * }; |
|
19496 * |
|
19497 * $scope.stopFight = function() { |
|
19498 * if (angular.isDefined(stop)) { |
|
19499 * $interval.cancel(stop); |
|
19500 * stop = undefined; |
|
19051 * } |
19501 * } |
19052 * }, 100); |
19502 * }; |
19053 * }; |
|
19054 * |
19503 * |
19055 * $scope.stopFight = function() { |
19504 * $scope.resetFight = function() { |
19056 * if (angular.isDefined(stop)) { |
19505 * $scope.blood_1 = 100; |
19057 * $interval.cancel(stop); |
19506 * $scope.blood_2 = 120; |
19058 * stop = undefined; |
19507 * }; |
19059 * } |
|
19060 * }; |
|
19061 * |
19508 * |
19062 * $scope.resetFight = function() { |
19509 * $scope.$on('$destroy', function() { |
19063 * $scope.blood_1 = 100; |
19510 * // Make sure that the interval is destroyed too |
19064 * $scope.blood_2 = 120; |
19511 * $scope.stopFight(); |
19065 * }; |
19512 * }); |
19066 * |
19513 * }]) |
19067 * $scope.$on('$destroy', function() { |
|
19068 * // Make sure that the interval is destroyed too |
|
19069 * $scope.stopFight(); |
|
19070 * }); |
|
19071 * }]) |
|
19072 * // Register the 'myCurrentTime' directive factory method. |
19514 * // Register the 'myCurrentTime' directive factory method. |
19073 * // We inject $interval and dateFilter service since the factory method is DI. |
19515 * // We inject $interval and dateFilter service since the factory method is DI. |
19074 * .directive('myCurrentTime', ['$interval', 'dateFilter', |
19516 * .directive('myCurrentTime', ['$interval', 'dateFilter', |
19075 * function($interval, dateFilter) { |
19517 * function($interval, dateFilter) { |
19076 * // return the directive link function. (compile function not needed) |
19518 * // return the directive link function. (compile function not needed) |
19179 * $locale service provides localization rules for various Angular components. As of right now the |
19621 * $locale service provides localization rules for various Angular components. As of right now the |
19180 * only public api is: |
19622 * only public api is: |
19181 * |
19623 * |
19182 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) |
19624 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) |
19183 */ |
19625 */ |
19184 function $LocaleProvider(){ |
19626 function $LocaleProvider() { |
19185 this.$get = function() { |
19627 this.$get = function() { |
19186 return { |
19628 return { |
19187 id: 'en-us', |
19629 id: 'en-us', |
19188 |
19630 |
19189 NUMBER_FORMATS: { |
19631 NUMBER_FORMATS: { |
19222 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), |
19664 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), |
19223 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), |
19665 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), |
19224 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), |
19666 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), |
19225 AMPMS: ['AM','PM'], |
19667 AMPMS: ['AM','PM'], |
19226 medium: 'MMM d, y h:mm:ss a', |
19668 medium: 'MMM d, y h:mm:ss a', |
19227 short: 'M/d/yy h:mm a', |
19669 'short': 'M/d/yy h:mm a', |
19228 fullDate: 'EEEE, MMMM d, y', |
19670 fullDate: 'EEEE, MMMM d, y', |
19229 longDate: 'MMMM d, y', |
19671 longDate: 'MMMM d, y', |
19230 mediumDate: 'MMM d, y', |
19672 mediumDate: 'MMM d, y', |
19231 shortDate: 'M/d/yy', |
19673 shortDate: 'M/d/yy', |
19232 mediumTime: 'h:mm:ss a', |
19674 mediumTime: 'h:mm:ss a', |
19263 } |
19705 } |
19264 |
19706 |
19265 return segments.join('/'); |
19707 return segments.join('/'); |
19266 } |
19708 } |
19267 |
19709 |
19268 function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { |
19710 function parseAbsoluteUrl(absoluteUrl, locationObj) { |
19269 var parsedUrl = urlResolve(absoluteUrl, appBase); |
19711 var parsedUrl = urlResolve(absoluteUrl); |
19270 |
19712 |
19271 locationObj.$$protocol = parsedUrl.protocol; |
19713 locationObj.$$protocol = parsedUrl.protocol; |
19272 locationObj.$$host = parsedUrl.hostname; |
19714 locationObj.$$host = parsedUrl.hostname; |
19273 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; |
19715 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; |
19274 } |
19716 } |
19275 |
19717 |
19276 |
19718 |
19277 function parseAppUrl(relativeUrl, locationObj, appBase) { |
19719 function parseAppUrl(relativeUrl, locationObj) { |
19278 var prefixed = (relativeUrl.charAt(0) !== '/'); |
19720 var prefixed = (relativeUrl.charAt(0) !== '/'); |
19279 if (prefixed) { |
19721 if (prefixed) { |
19280 relativeUrl = '/' + relativeUrl; |
19722 relativeUrl = '/' + relativeUrl; |
19281 } |
19723 } |
19282 var match = urlResolve(relativeUrl, appBase); |
19724 var match = urlResolve(relativeUrl); |
19283 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? |
19725 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? |
19284 match.pathname.substring(1) : match.pathname); |
19726 match.pathname.substring(1) : match.pathname); |
19285 locationObj.$$search = parseKeyValue(match.search); |
19727 locationObj.$$search = parseKeyValue(match.search); |
19286 locationObj.$$hash = decodeURIComponent(match.hash); |
19728 locationObj.$$hash = decodeURIComponent(match.hash); |
19287 |
19729 |
19309 function stripHash(url) { |
19751 function stripHash(url) { |
19310 var index = url.indexOf('#'); |
19752 var index = url.indexOf('#'); |
19311 return index == -1 ? url : url.substr(0, index); |
19753 return index == -1 ? url : url.substr(0, index); |
19312 } |
19754 } |
19313 |
19755 |
19756 function trimEmptyHash(url) { |
|
19757 return url.replace(/(#.+)|#$/, '$1'); |
|
19758 } |
|
19759 |
|
19314 |
19760 |
19315 function stripFile(url) { |
19761 function stripFile(url) { |
19316 return url.substr(0, stripHash(url).lastIndexOf('/') + 1); |
19762 return url.substr(0, stripHash(url).lastIndexOf('/') + 1); |
19317 } |
19763 } |
19318 |
19764 |
19332 */ |
19778 */ |
19333 function LocationHtml5Url(appBase, basePrefix) { |
19779 function LocationHtml5Url(appBase, basePrefix) { |
19334 this.$$html5 = true; |
19780 this.$$html5 = true; |
19335 basePrefix = basePrefix || ''; |
19781 basePrefix = basePrefix || ''; |
19336 var appBaseNoFile = stripFile(appBase); |
19782 var appBaseNoFile = stripFile(appBase); |
19337 parseAbsoluteUrl(appBase, this, appBase); |
19783 parseAbsoluteUrl(appBase, this); |
19338 |
19784 |
19339 |
19785 |
19340 /** |
19786 /** |
19341 * Parse given html5 (regular) url string into properties |
19787 * Parse given html5 (regular) url string into properties |
19342 * @param {string} newAbsoluteUrl HTML5 url |
19788 * @param {string} url HTML5 url |
19343 * @private |
19789 * @private |
19344 */ |
19790 */ |
19345 this.$$parse = function(url) { |
19791 this.$$parse = function(url) { |
19346 var pathUrl = beginsWith(appBaseNoFile, url); |
19792 var pathUrl = beginsWith(appBaseNoFile, url); |
19347 if (!isString(pathUrl)) { |
19793 if (!isString(pathUrl)) { |
19348 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, |
19794 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, |
19349 appBaseNoFile); |
19795 appBaseNoFile); |
19350 } |
19796 } |
19351 |
19797 |
19352 parseAppUrl(pathUrl, this, appBase); |
19798 parseAppUrl(pathUrl, this); |
19353 |
19799 |
19354 if (!this.$$path) { |
19800 if (!this.$$path) { |
19355 this.$$path = '/'; |
19801 this.$$path = '/'; |
19356 } |
19802 } |
19357 |
19803 |
19378 return true; |
19824 return true; |
19379 } |
19825 } |
19380 var appUrl, prevAppUrl; |
19826 var appUrl, prevAppUrl; |
19381 var rewrittenUrl; |
19827 var rewrittenUrl; |
19382 |
19828 |
19383 if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { |
19829 if ((appUrl = beginsWith(appBase, url)) !== undefined) { |
19384 prevAppUrl = appUrl; |
19830 prevAppUrl = appUrl; |
19385 if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { |
19831 if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) { |
19386 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); |
19832 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); |
19387 } else { |
19833 } else { |
19388 rewrittenUrl = appBase + prevAppUrl; |
19834 rewrittenUrl = appBase + prevAppUrl; |
19389 } |
19835 } |
19390 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { |
19836 } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) { |
19391 rewrittenUrl = appBaseNoFile + appUrl; |
19837 rewrittenUrl = appBaseNoFile + appUrl; |
19392 } else if (appBaseNoFile == url + '/') { |
19838 } else if (appBaseNoFile == url + '/') { |
19393 rewrittenUrl = appBaseNoFile; |
19839 rewrittenUrl = appBaseNoFile; |
19394 } |
19840 } |
19395 if (rewrittenUrl) { |
19841 if (rewrittenUrl) { |
19410 * @param {string} hashPrefix hashbang prefix |
19856 * @param {string} hashPrefix hashbang prefix |
19411 */ |
19857 */ |
19412 function LocationHashbangUrl(appBase, hashPrefix) { |
19858 function LocationHashbangUrl(appBase, hashPrefix) { |
19413 var appBaseNoFile = stripFile(appBase); |
19859 var appBaseNoFile = stripFile(appBase); |
19414 |
19860 |
19415 parseAbsoluteUrl(appBase, this, appBase); |
19861 parseAbsoluteUrl(appBase, this); |
19416 |
19862 |
19417 |
19863 |
19418 /** |
19864 /** |
19419 * Parse given hashbang url into properties |
19865 * Parse given hashbang url into properties |
19420 * @param {string} url Hashbang url |
19866 * @param {string} url Hashbang url |
19421 * @private |
19867 * @private |
19422 */ |
19868 */ |
19423 this.$$parse = function(url) { |
19869 this.$$parse = function(url) { |
19424 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); |
19870 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); |
19425 var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' |
19871 var withoutHashUrl; |
19426 ? beginsWith(hashPrefix, withoutBaseUrl) |
19872 |
19427 : (this.$$html5) |
19873 if (withoutBaseUrl.charAt(0) === '#') { |
19428 ? withoutBaseUrl |
19874 |
19429 : ''; |
19875 // The rest of the url starts with a hash so we have |
19430 |
19876 // got either a hashbang path or a plain hash fragment |
19431 if (!isString(withoutHashUrl)) { |
19877 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl); |
19432 throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, |
19878 if (isUndefined(withoutHashUrl)) { |
19433 hashPrefix); |
19879 // There was no hashbang prefix so we just have a hash fragment |
19434 } |
19880 withoutHashUrl = withoutBaseUrl; |
19435 parseAppUrl(withoutHashUrl, this, appBase); |
19881 } |
19882 |
|
19883 } else { |
|
19884 // There was no hashbang path nor hash fragment: |
|
19885 // If we are in HTML5 mode we use what is left as the path; |
|
19886 // Otherwise we ignore what is left |
|
19887 withoutHashUrl = this.$$html5 ? withoutBaseUrl : ''; |
|
19888 } |
|
19889 |
|
19890 parseAppUrl(withoutHashUrl, this); |
|
19436 |
19891 |
19437 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); |
19892 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); |
19438 |
19893 |
19439 this.$$compose(); |
19894 this.$$compose(); |
19440 |
19895 |
19447 * * a.pathname === '/C:/foo' //true |
19902 * * a.pathname === '/C:/foo' //true |
19448 * |
19903 * |
19449 * Inside of Angular, we're always using pathnames that |
19904 * Inside of Angular, we're always using pathnames that |
19450 * do not include drive names for routing. |
19905 * do not include drive names for routing. |
19451 */ |
19906 */ |
19452 function removeWindowsDriveName (path, url, base) { |
19907 function removeWindowsDriveName(path, url, base) { |
19453 /* |
19908 /* |
19454 Matches paths for file protocol on windows, |
19909 Matches paths for file protocol on windows, |
19455 such as /C:/foo/bar, and captures only /foo/bar. |
19910 such as /C:/foo/bar, and captures only /foo/bar. |
19456 */ |
19911 */ |
19457 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; |
19912 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; |
19484 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; |
19939 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; |
19485 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); |
19940 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); |
19486 }; |
19941 }; |
19487 |
19942 |
19488 this.$$parseLinkUrl = function(url, relHref) { |
19943 this.$$parseLinkUrl = function(url, relHref) { |
19489 if(stripHash(appBase) == stripHash(url)) { |
19944 if (stripHash(appBase) == stripHash(url)) { |
19490 this.$$parse(url); |
19945 this.$$parse(url); |
19491 return true; |
19946 return true; |
19492 } |
19947 } |
19493 return false; |
19948 return false; |
19494 }; |
19949 }; |
19519 } |
19974 } |
19520 |
19975 |
19521 var rewrittenUrl; |
19976 var rewrittenUrl; |
19522 var appUrl; |
19977 var appUrl; |
19523 |
19978 |
19524 if ( appBase == stripHash(url) ) { |
19979 if (appBase == stripHash(url)) { |
19525 rewrittenUrl = url; |
19980 rewrittenUrl = url; |
19526 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { |
19981 } else if ((appUrl = beginsWith(appBaseNoFile, url))) { |
19527 rewrittenUrl = appBase + hashPrefix + appUrl; |
19982 rewrittenUrl = appBase + hashPrefix + appUrl; |
19528 } else if ( appBaseNoFile === url + '/') { |
19983 } else if (appBaseNoFile === url + '/') { |
19529 rewrittenUrl = appBaseNoFile; |
19984 rewrittenUrl = appBaseNoFile; |
19530 } |
19985 } |
19531 if (rewrittenUrl) { |
19986 if (rewrittenUrl) { |
19532 this.$$parse(rewrittenUrl); |
19987 this.$$parse(rewrittenUrl); |
19533 } |
19988 } |
19567 * @description |
20022 * @description |
19568 * This method is getter only. |
20023 * This method is getter only. |
19569 * |
20024 * |
19570 * Return full url representation with all segments encoded according to rules specified in |
20025 * Return full url representation with all segments encoded according to rules specified in |
19571 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). |
20026 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). |
20027 * |
|
20028 * |
|
20029 * ```js |
|
20030 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20031 * var absUrl = $location.absUrl(); |
|
20032 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" |
|
20033 * ``` |
|
19572 * |
20034 * |
19573 * @return {string} full url |
20035 * @return {string} full url |
19574 */ |
20036 */ |
19575 absUrl: locationGetter('$$absUrl'), |
20037 absUrl: locationGetter('$$absUrl'), |
19576 |
20038 |
19583 * |
20045 * |
19584 * Return url (e.g. `/path?a=b#hash`) when called without any parameter. |
20046 * Return url (e.g. `/path?a=b#hash`) when called without any parameter. |
19585 * |
20047 * |
19586 * Change path, search and hash, when called with parameter and return `$location`. |
20048 * Change path, search and hash, when called with parameter and return `$location`. |
19587 * |
20049 * |
20050 * |
|
20051 * ```js |
|
20052 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20053 * var url = $location.url(); |
|
20054 * // => "/some/path?foo=bar&baz=xoxo" |
|
20055 * ``` |
|
20056 * |
|
19588 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) |
20057 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) |
19589 * @return {string} url |
20058 * @return {string} url |
19590 */ |
20059 */ |
19591 url: function(url) { |
20060 url: function(url) { |
19592 if (isUndefined(url)) |
20061 if (isUndefined(url)) |
19593 return this.$$url; |
20062 return this.$$url; |
19594 |
20063 |
19595 var match = PATH_MATCH.exec(url); |
20064 var match = PATH_MATCH.exec(url); |
19596 if (match[1]) this.path(decodeURIComponent(match[1])); |
20065 if (match[1] || url === '') this.path(decodeURIComponent(match[1])); |
19597 if (match[2] || match[1]) this.search(match[3] || ''); |
20066 if (match[2] || match[1] || url === '') this.search(match[3] || ''); |
19598 this.hash(match[5] || ''); |
20067 this.hash(match[5] || ''); |
19599 |
20068 |
19600 return this; |
20069 return this; |
19601 }, |
20070 }, |
19602 |
20071 |
19607 * @description |
20076 * @description |
19608 * This method is getter only. |
20077 * This method is getter only. |
19609 * |
20078 * |
19610 * Return protocol of current url. |
20079 * Return protocol of current url. |
19611 * |
20080 * |
20081 * |
|
20082 * ```js |
|
20083 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20084 * var protocol = $location.protocol(); |
|
20085 * // => "http" |
|
20086 * ``` |
|
20087 * |
|
19612 * @return {string} protocol of current url |
20088 * @return {string} protocol of current url |
19613 */ |
20089 */ |
19614 protocol: locationGetter('$$protocol'), |
20090 protocol: locationGetter('$$protocol'), |
19615 |
20091 |
19616 /** |
20092 /** |
19620 * @description |
20096 * @description |
19621 * This method is getter only. |
20097 * This method is getter only. |
19622 * |
20098 * |
19623 * Return host of current url. |
20099 * Return host of current url. |
19624 * |
20100 * |
20101 * |
|
20102 * ```js |
|
20103 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20104 * var host = $location.host(); |
|
20105 * // => "example.com" |
|
20106 * ``` |
|
20107 * |
|
19625 * @return {string} host of current url. |
20108 * @return {string} host of current url. |
19626 */ |
20109 */ |
19627 host: locationGetter('$$host'), |
20110 host: locationGetter('$$host'), |
19628 |
20111 |
19629 /** |
20112 /** |
19632 * |
20115 * |
19633 * @description |
20116 * @description |
19634 * This method is getter only. |
20117 * This method is getter only. |
19635 * |
20118 * |
19636 * Return port of current url. |
20119 * Return port of current url. |
20120 * |
|
20121 * |
|
20122 * ```js |
|
20123 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20124 * var port = $location.port(); |
|
20125 * // => 80 |
|
20126 * ``` |
|
19637 * |
20127 * |
19638 * @return {Number} port |
20128 * @return {Number} port |
19639 */ |
20129 */ |
19640 port: locationGetter('$$port'), |
20130 port: locationGetter('$$port'), |
19641 |
20131 |
19650 * |
20140 * |
19651 * Change path when called with parameter and return `$location`. |
20141 * Change path when called with parameter and return `$location`. |
19652 * |
20142 * |
19653 * Note: Path should always begin with forward slash (/), this method will add the forward slash |
20143 * Note: Path should always begin with forward slash (/), this method will add the forward slash |
19654 * if it is missing. |
20144 * if it is missing. |
20145 * |
|
20146 * |
|
20147 * ```js |
|
20148 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
|
20149 * var path = $location.path(); |
|
20150 * // => "/some/path" |
|
20151 * ``` |
|
19655 * |
20152 * |
19656 * @param {(string|number)=} path New path |
20153 * @param {(string|number)=} path New path |
19657 * @return {string} path |
20154 * @return {string} path |
19658 */ |
20155 */ |
19659 path: locationGetterSetter('$$path', function(path) { |
20156 path: locationGetterSetter('$$path', function(path) { |
19676 * ```js |
20173 * ```js |
19677 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
20174 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo |
19678 * var searchObject = $location.search(); |
20175 * var searchObject = $location.search(); |
19679 * // => {foo: 'bar', baz: 'xoxo'} |
20176 * // => {foo: 'bar', baz: 'xoxo'} |
19680 * |
20177 * |
19681 * |
|
19682 * // set foo to 'yipee' |
20178 * // set foo to 'yipee' |
19683 * $location.search('foo', 'yipee'); |
20179 * $location.search('foo', 'yipee'); |
19684 * // => $location |
20180 * // $location.search() => {foo: 'yipee', baz: 'xoxo'} |
19685 * ``` |
20181 * ``` |
19686 * |
20182 * |
19687 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or |
20183 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or |
19688 * hash object. |
20184 * hash object. |
19689 * |
20185 * |
19714 case 1: |
20210 case 1: |
19715 if (isString(search) || isNumber(search)) { |
20211 if (isString(search) || isNumber(search)) { |
19716 search = search.toString(); |
20212 search = search.toString(); |
19717 this.$$search = parseKeyValue(search); |
20213 this.$$search = parseKeyValue(search); |
19718 } else if (isObject(search)) { |
20214 } else if (isObject(search)) { |
20215 search = copy(search, {}); |
|
19719 // remove object undefined or null properties |
20216 // remove object undefined or null properties |
19720 forEach(search, function(value, key) { |
20217 forEach(search, function(value, key) { |
19721 if (value == null) delete search[key]; |
20218 if (value == null) delete search[key]; |
19722 }); |
20219 }); |
19723 |
20220 |
19747 * This method is getter / setter. |
20244 * This method is getter / setter. |
19748 * |
20245 * |
19749 * Return hash fragment when called without any parameter. |
20246 * Return hash fragment when called without any parameter. |
19750 * |
20247 * |
19751 * Change hash fragment when called with parameter and return `$location`. |
20248 * Change hash fragment when called with parameter and return `$location`. |
20249 * |
|
20250 * |
|
20251 * ```js |
|
20252 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue |
|
20253 * var hash = $location.hash(); |
|
20254 * // => "hashValue" |
|
20255 * ``` |
|
19752 * |
20256 * |
19753 * @param {(string|number)=} hash New hash fragment |
20257 * @param {(string|number)=} hash New hash fragment |
19754 * @return {string} hash |
20258 * @return {string} hash |
19755 */ |
20259 */ |
19756 hash: locationGetterSetter('$$hash', function(hash) { |
20260 hash: locationGetterSetter('$$hash', function(hash) { |
19769 this.$$replace = true; |
20273 this.$$replace = true; |
19770 return this; |
20274 return this; |
19771 } |
20275 } |
19772 }; |
20276 }; |
19773 |
20277 |
19774 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function (Location) { |
20278 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { |
19775 Location.prototype = Object.create(locationPrototype); |
20279 Location.prototype = Object.create(locationPrototype); |
19776 |
20280 |
19777 /** |
20281 /** |
19778 * @ngdoc method |
20282 * @ngdoc method |
19779 * @name $location#state |
20283 * @name $location#state |
19861 * @ngdoc provider |
20365 * @ngdoc provider |
19862 * @name $locationProvider |
20366 * @name $locationProvider |
19863 * @description |
20367 * @description |
19864 * Use the `$locationProvider` to configure how the application deep linking paths are stored. |
20368 * Use the `$locationProvider` to configure how the application deep linking paths are stored. |
19865 */ |
20369 */ |
19866 function $LocationProvider(){ |
20370 function $LocationProvider() { |
19867 var hashPrefix = '', |
20371 var hashPrefix = '', |
19868 html5Mode = { |
20372 html5Mode = { |
19869 enabled: false, |
20373 enabled: false, |
19870 requireBase: true, |
20374 requireBase: true, |
19871 rewriteLinks: true |
20375 rewriteLinks: true |
19899 * support `pushState`. |
20403 * support `pushState`. |
19900 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies |
20404 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies |
19901 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are |
20405 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are |
19902 * true, and a base tag is not present, an error will be thrown when `$location` is injected. |
20406 * true, and a base tag is not present, an error will be thrown when `$location` is injected. |
19903 * See the {@link guide/$location $location guide for more information} |
20407 * See the {@link guide/$location $location guide for more information} |
19904 * - **rewriteLinks** - `{boolean}` - (default: `false`) When html5Mode is enabled, disables |
20408 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, |
19905 * url rewriting for relative linksTurns off url rewriting for relative links. |
20409 * enables/disables url rewriting for relative links. |
19906 * |
20410 * |
19907 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter |
20411 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter |
19908 */ |
20412 */ |
19909 this.html5Mode = function(mode) { |
20413 this.html5Mode = function(mode) { |
19910 if (isBoolean(mode)) { |
20414 if (isBoolean(mode)) { |
19911 html5Mode.enabled = mode; |
20415 html5Mode.enabled = mode; |
19912 return this; |
20416 return this; |
19913 } else if (isObject(mode)) { |
20417 } else if (isObject(mode)) { |
19914 |
20418 |
19915 if (isBoolean(mode.enabled)) { |
20419 if (isBoolean(mode.enabled)) { |
19916 html5Mode.enabled = mode.enabled; |
20420 html5Mode.enabled = mode.enabled; |
19917 } |
20421 } |
19918 |
20422 |
19919 if (isBoolean(mode.requireBase)) { |
20423 if (isBoolean(mode.requireBase)) { |
19920 html5Mode.requireBase = mode.requireBase; |
20424 html5Mode.requireBase = mode.requireBase; |
19921 } |
20425 } |
19922 |
20426 |
19923 if (isBoolean(mode.rewriteLinks)) { |
20427 if (isBoolean(mode.rewriteLinks)) { |
19924 html5Mode.rewriteLinks = mode.rewriteLinks; |
20428 html5Mode.rewriteLinks = mode.rewriteLinks; |
19925 } |
20429 } |
19926 |
20430 |
19927 return this; |
20431 return this; |
19928 } else { |
20432 } else { |
19929 return html5Mode; |
20433 return html5Mode; |
19938 * Broadcasted before a URL will change. |
20442 * Broadcasted before a URL will change. |
19939 * |
20443 * |
19940 * This change can be prevented by calling |
20444 * This change can be prevented by calling |
19941 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more |
20445 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more |
19942 * details about event object. Upon successful change |
20446 * details about event object. Upon successful change |
19943 * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired. |
20447 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. |
19944 * |
20448 * |
19945 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when |
20449 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when |
19946 * the browser supports the HTML5 History API. |
20450 * the browser supports the HTML5 History API. |
19947 * |
20451 * |
19948 * @param {Object} angularEvent Synthetic event object. |
20452 * @param {Object} angularEvent Synthetic event object. |
19967 * @param {string=} oldUrl URL that was before it was changed. |
20471 * @param {string=} oldUrl URL that was before it was changed. |
19968 * @param {string=} newState New history state object |
20472 * @param {string=} newState New history state object |
19969 * @param {string=} oldState History state object that was before it was changed. |
20473 * @param {string=} oldState History state object that was before it was changed. |
19970 */ |
20474 */ |
19971 |
20475 |
19972 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', |
20476 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', |
19973 function( $rootScope, $browser, $sniffer, $rootElement) { |
20477 function($rootScope, $browser, $sniffer, $rootElement, $window) { |
19974 var $location, |
20478 var $location, |
19975 LocationMode, |
20479 LocationMode, |
19976 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' |
20480 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' |
19977 initialUrl = $browser.url(), |
20481 initialUrl = $browser.url(), |
19978 appBase; |
20482 appBase; |
20050 event.preventDefault(); |
20554 event.preventDefault(); |
20051 // update location manually |
20555 // update location manually |
20052 if ($location.absUrl() != $browser.url()) { |
20556 if ($location.absUrl() != $browser.url()) { |
20053 $rootScope.$apply(); |
20557 $rootScope.$apply(); |
20054 // hack to work around FF6 bug 684208 when scenario runner clicks on links |
20558 // hack to work around FF6 bug 684208 when scenario runner clicks on links |
20055 window.angular['ff-684208-preventDefault'] = true; |
20559 $window.angular['ff-684208-preventDefault'] = true; |
20056 } |
20560 } |
20057 } |
20561 } |
20058 } |
20562 } |
20059 }); |
20563 }); |
20060 |
20564 |
20069 // update $location when $browser url changes |
20573 // update $location when $browser url changes |
20070 $browser.onUrlChange(function(newUrl, newState) { |
20574 $browser.onUrlChange(function(newUrl, newState) { |
20071 $rootScope.$evalAsync(function() { |
20575 $rootScope.$evalAsync(function() { |
20072 var oldUrl = $location.absUrl(); |
20576 var oldUrl = $location.absUrl(); |
20073 var oldState = $location.$$state; |
20577 var oldState = $location.$$state; |
20578 var defaultPrevented; |
|
20074 |
20579 |
20075 $location.$$parse(newUrl); |
20580 $location.$$parse(newUrl); |
20076 $location.$$state = newState; |
20581 $location.$$state = newState; |
20077 if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, |
20582 |
20078 newState, oldState).defaultPrevented) { |
20583 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, |
20584 newState, oldState).defaultPrevented; |
|
20585 |
|
20586 // if the location was changed by a `$locationChangeStart` handler then stop |
|
20587 // processing this location change |
|
20588 if ($location.absUrl() !== newUrl) return; |
|
20589 |
|
20590 if (defaultPrevented) { |
|
20079 $location.$$parse(oldUrl); |
20591 $location.$$parse(oldUrl); |
20080 $location.$$state = oldState; |
20592 $location.$$state = oldState; |
20081 setBrowserUrlWithFallback(oldUrl, false, oldState); |
20593 setBrowserUrlWithFallback(oldUrl, false, oldState); |
20082 } else { |
20594 } else { |
20083 initializing = false; |
20595 initializing = false; |
20087 if (!$rootScope.$$phase) $rootScope.$digest(); |
20599 if (!$rootScope.$$phase) $rootScope.$digest(); |
20088 }); |
20600 }); |
20089 |
20601 |
20090 // update browser |
20602 // update browser |
20091 $rootScope.$watch(function $locationWatch() { |
20603 $rootScope.$watch(function $locationWatch() { |
20092 var oldUrl = $browser.url(); |
20604 var oldUrl = trimEmptyHash($browser.url()); |
20605 var newUrl = trimEmptyHash($location.absUrl()); |
|
20093 var oldState = $browser.state(); |
20606 var oldState = $browser.state(); |
20094 var currentReplace = $location.$$replace; |
20607 var currentReplace = $location.$$replace; |
20095 |
20608 var urlOrStateChanged = oldUrl !== newUrl || |
20096 if (initializing || oldUrl !== $location.absUrl() || |
20609 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); |
20097 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state)) { |
20610 |
20611 if (initializing || urlOrStateChanged) { |
|
20098 initializing = false; |
20612 initializing = false; |
20099 |
20613 |
20100 $rootScope.$evalAsync(function() { |
20614 $rootScope.$evalAsync(function() { |
20101 if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl, |
20615 var newUrl = $location.absUrl(); |
20102 $location.$$state, oldState).defaultPrevented) { |
20616 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, |
20617 $location.$$state, oldState).defaultPrevented; |
|
20618 |
|
20619 // if the location was changed by a `$locationChangeStart` handler then stop |
|
20620 // processing this location change |
|
20621 if ($location.absUrl() !== newUrl) return; |
|
20622 |
|
20623 if (defaultPrevented) { |
|
20103 $location.$$parse(oldUrl); |
20624 $location.$$parse(oldUrl); |
20104 $location.$$state = oldState; |
20625 $location.$$state = oldState; |
20105 } else { |
20626 } else { |
20106 setBrowserUrlWithFallback($location.absUrl(), currentReplace, |
20627 if (urlOrStateChanged) { |
20107 oldState === $location.$$state ? null : $location.$$state); |
20628 setBrowserUrlWithFallback(newUrl, currentReplace, |
20629 oldState === $location.$$state ? null : $location.$$state); |
|
20630 } |
|
20108 afterLocationChange(oldUrl, oldState); |
20631 afterLocationChange(oldUrl, oldState); |
20109 } |
20632 } |
20110 }); |
20633 }); |
20111 } |
20634 } |
20112 |
20635 |
20166 * @ngdoc provider |
20689 * @ngdoc provider |
20167 * @name $logProvider |
20690 * @name $logProvider |
20168 * @description |
20691 * @description |
20169 * Use the `$logProvider` to configure how the application logs messages |
20692 * Use the `$logProvider` to configure how the application logs messages |
20170 */ |
20693 */ |
20171 function $LogProvider(){ |
20694 function $LogProvider() { |
20172 var debug = true, |
20695 var debug = true, |
20173 self = this; |
20696 self = this; |
20174 |
20697 |
20175 /** |
20698 /** |
20176 * @ngdoc method |
20699 * @ngdoc method |
20186 } else { |
20709 } else { |
20187 return debug; |
20710 return debug; |
20188 } |
20711 } |
20189 }; |
20712 }; |
20190 |
20713 |
20191 this.$get = ['$window', function($window){ |
20714 this.$get = ['$window', function($window) { |
20192 return { |
20715 return { |
20193 /** |
20716 /** |
20194 * @ngdoc method |
20717 * @ngdoc method |
20195 * @name $log#log |
20718 * @name $log#log |
20196 * |
20719 * |
20231 * @name $log#debug |
20754 * @name $log#debug |
20232 * |
20755 * |
20233 * @description |
20756 * @description |
20234 * Write a debug message |
20757 * Write a debug message |
20235 */ |
20758 */ |
20236 debug: (function () { |
20759 debug: (function() { |
20237 var fn = consoleLog('debug'); |
20760 var fn = consoleLog('debug'); |
20238 |
20761 |
20239 return function() { |
20762 return function() { |
20240 if (debug) { |
20763 if (debug) { |
20241 fn.apply(self, arguments); |
20764 fn.apply(self, arguments); |
20290 var $parseMinErr = minErr('$parse'); |
20813 var $parseMinErr = minErr('$parse'); |
20291 |
20814 |
20292 // Sandboxing Angular Expressions |
20815 // Sandboxing Angular Expressions |
20293 // ------------------------------ |
20816 // ------------------------------ |
20294 // Angular expressions are generally considered safe because these expressions only have direct |
20817 // Angular expressions are generally considered safe because these expressions only have direct |
20295 // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by |
20818 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by |
20296 // obtaining a reference to native JS functions such as the Function constructor. |
20819 // obtaining a reference to native JS functions such as the Function constructor. |
20297 // |
20820 // |
20298 // As an example, consider the following Angular expression: |
20821 // As an example, consider the following Angular expression: |
20299 // |
20822 // |
20300 // {}.toString.constructor('alert("evil JS code")') |
20823 // {}.toString.constructor('alert("evil JS code")') |
20301 // |
20824 // |
20302 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits |
20825 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits |
20303 // against the expression language, but not to prevent exploits that were enabled by exposing |
20826 // against the expression language, but not to prevent exploits that were enabled by exposing |
20304 // sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good |
20827 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good |
20305 // practice and therefore we are not even trying to protect against interaction with an object |
20828 // practice and therefore we are not even trying to protect against interaction with an object |
20306 // explicitly exposed in this way. |
20829 // explicitly exposed in this way. |
20307 // |
20830 // |
20308 // In general, it is not possible to access a Window object from an angular expression unless a |
20831 // In general, it is not possible to access a Window object from an angular expression unless a |
20309 // window or some DOM object that has a reference to window is published onto a Scope. |
20832 // window or some DOM object that has a reference to window is published onto a Scope. |
20310 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to |
20833 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to |
20311 // native objects. |
20834 // native objects. |
20835 // |
|
20836 // See https://docs.angularjs.org/guide/security |
|
20312 |
20837 |
20313 |
20838 |
20314 function ensureSafeMemberName(name, fullExpression) { |
20839 function ensureSafeMemberName(name, fullExpression) { |
20315 if (name === "__defineGetter__" || name === "__defineSetter__" |
20840 if (name === "__defineGetter__" || name === "__defineSetter__" |
20316 || name === "__lookupGetter__" || name === "__lookupSetter__" |
20841 || name === "__lookupGetter__" || name === "__lookupSetter__" |
20317 || name === "__proto__") { |
20842 || name === "__proto__") { |
20318 throw $parseMinErr('isecfld', |
20843 throw $parseMinErr('isecfld', |
20319 'Attempting to access a disallowed field in Angular expressions! ' |
20844 'Attempting to access a disallowed field in Angular expressions! ' |
20320 +'Expression: {0}', fullExpression); |
20845 + 'Expression: {0}', fullExpression); |
20321 } |
20846 } |
20322 return name; |
20847 return name; |
20323 } |
20848 } |
20324 |
20849 |
20325 function ensureSafeObject(obj, fullExpression) { |
20850 function ensureSafeObject(obj, fullExpression) { |
20384 CONSTANTS['this'].sharedGetter = true; |
20909 CONSTANTS['this'].sharedGetter = true; |
20385 |
20910 |
20386 |
20911 |
20387 //Operators - will be wrapped by binaryFn/unaryFn/assignment/filter |
20912 //Operators - will be wrapped by binaryFn/unaryFn/assignment/filter |
20388 var OPERATORS = extend(createMap(), { |
20913 var OPERATORS = extend(createMap(), { |
20389 /* jshint bitwise : false */ |
20914 '+':function(self, locals, a, b) { |
20390 '+':function(self, locals, a,b){ |
|
20391 a=a(self, locals); b=b(self, locals); |
20915 a=a(self, locals); b=b(self, locals); |
20392 if (isDefined(a)) { |
20916 if (isDefined(a)) { |
20393 if (isDefined(b)) { |
20917 if (isDefined(b)) { |
20394 return a + b; |
20918 return a + b; |
20395 } |
20919 } |
20396 return a; |
20920 return a; |
20397 } |
20921 } |
20398 return isDefined(b)?b:undefined;}, |
20922 return isDefined(b) ? b : undefined;}, |
20399 '-':function(self, locals, a,b){ |
20923 '-':function(self, locals, a, b) { |
20400 a=a(self, locals); b=b(self, locals); |
20924 a=a(self, locals); b=b(self, locals); |
20401 return (isDefined(a)?a:0)-(isDefined(b)?b:0); |
20925 return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0); |
20402 }, |
20926 }, |
20403 '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, |
20927 '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);}, |
20404 '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, |
20928 '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);}, |
20405 '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, |
20929 '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);}, |
20406 '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, |
20930 '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);}, |
20407 '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, |
20931 '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);}, |
20408 '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, |
20932 '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);}, |
20409 '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, |
20933 '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);}, |
20410 '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, |
20934 '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);}, |
20411 '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);}, |
20935 '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);}, |
20412 '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, |
20936 '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);}, |
20413 '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, |
20937 '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);}, |
20414 '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, |
20938 '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);}, |
20415 '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, |
20939 '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);}, |
20416 '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, |
20940 '!':function(self, locals, a) {return !a(self, locals);}, |
20417 '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, |
|
20418 '!':function(self, locals, a){return !a(self, locals);}, |
|
20419 |
20941 |
20420 //Tokenized as operators but parsed as assignment/filters |
20942 //Tokenized as operators but parsed as assignment/filters |
20421 '=':true, |
20943 '=':true, |
20422 '|':true |
20944 '|':true |
20423 }); |
20945 }); |
20424 /* jshint bitwise: true */ |
|
20425 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; |
20946 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; |
20426 |
20947 |
20427 |
20948 |
20428 ///////////////////////////////////////// |
20949 ///////////////////////////////////////// |
20429 |
20950 |
20430 |
20951 |
20431 /** |
20952 /** |
20432 * @constructor |
20953 * @constructor |
20433 */ |
20954 */ |
20434 var Lexer = function (options) { |
20955 var Lexer = function(options) { |
20435 this.options = options; |
20956 this.options = options; |
20436 }; |
20957 }; |
20437 |
20958 |
20438 Lexer.prototype = { |
20959 Lexer.prototype = { |
20439 constructor: Lexer, |
20960 constructor: Lexer, |
20440 |
20961 |
20441 lex: function (text) { |
20962 lex: function(text) { |
20442 this.text = text; |
20963 this.text = text; |
20443 this.index = 0; |
20964 this.index = 0; |
20444 this.ch = undefined; |
|
20445 this.tokens = []; |
20965 this.tokens = []; |
20446 |
20966 |
20447 while (this.index < this.text.length) { |
20967 while (this.index < this.text.length) { |
20448 this.ch = this.text.charAt(this.index); |
20968 var ch = this.text.charAt(this.index); |
20449 if (this.is('"\'')) { |
20969 if (ch === '"' || ch === "'") { |
20450 this.readString(this.ch); |
20970 this.readString(ch); |
20451 } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { |
20971 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { |
20452 this.readNumber(); |
20972 this.readNumber(); |
20453 } else if (this.isIdent(this.ch)) { |
20973 } else if (this.isIdent(ch)) { |
20454 this.readIdent(); |
20974 this.readIdent(); |
20455 } else if (this.is('(){}[].,;:?')) { |
20975 } else if (this.is(ch, '(){}[].,;:?')) { |
20456 this.tokens.push({ |
20976 this.tokens.push({index: this.index, text: ch}); |
20457 index: this.index, |
|
20458 text: this.ch |
|
20459 }); |
|
20460 this.index++; |
20977 this.index++; |
20461 } else if (this.isWhitespace(this.ch)) { |
20978 } else if (this.isWhitespace(ch)) { |
20462 this.index++; |
20979 this.index++; |
20463 } else { |
20980 } else { |
20464 var ch2 = this.ch + this.peek(); |
20981 var ch2 = ch + this.peek(); |
20465 var ch3 = ch2 + this.peek(2); |
20982 var ch3 = ch2 + this.peek(2); |
20466 var fn = OPERATORS[this.ch]; |
20983 var op1 = OPERATORS[ch]; |
20467 var fn2 = OPERATORS[ch2]; |
20984 var op2 = OPERATORS[ch2]; |
20468 var fn3 = OPERATORS[ch3]; |
20985 var op3 = OPERATORS[ch3]; |
20469 if (fn3) { |
20986 if (op1 || op2 || op3) { |
20470 this.tokens.push({index: this.index, text: ch3, fn: fn3}); |
20987 var token = op3 ? ch3 : (op2 ? ch2 : ch); |
20471 this.index += 3; |
20988 this.tokens.push({index: this.index, text: token, operator: true}); |
20472 } else if (fn2) { |
20989 this.index += token.length; |
20473 this.tokens.push({index: this.index, text: ch2, fn: fn2}); |
|
20474 this.index += 2; |
|
20475 } else if (fn) { |
|
20476 this.tokens.push({ |
|
20477 index: this.index, |
|
20478 text: this.ch, |
|
20479 fn: fn |
|
20480 }); |
|
20481 this.index += 1; |
|
20482 } else { |
20990 } else { |
20483 this.throwError('Unexpected next character ', this.index, this.index + 1); |
20991 this.throwError('Unexpected next character ', this.index, this.index + 1); |
20484 } |
20992 } |
20485 } |
20993 } |
20486 } |
20994 } |
20487 return this.tokens; |
20995 return this.tokens; |
20488 }, |
20996 }, |
20489 |
20997 |
20490 is: function(chars) { |
20998 is: function(ch, chars) { |
20491 return chars.indexOf(this.ch) !== -1; |
20999 return chars.indexOf(ch) !== -1; |
20492 }, |
21000 }, |
20493 |
21001 |
20494 peek: function(i) { |
21002 peek: function(i) { |
20495 var num = i || 1; |
21003 var num = i || 1; |
20496 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; |
21004 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; |
20497 }, |
21005 }, |
20498 |
21006 |
20499 isNumber: function(ch) { |
21007 isNumber: function(ch) { |
20500 return ('0' <= ch && ch <= '9'); |
21008 return ('0' <= ch && ch <= '9') && typeof ch === "string"; |
20501 }, |
21009 }, |
20502 |
21010 |
20503 isWhitespace: function(ch) { |
21011 isWhitespace: function(ch) { |
20504 // IE treats non-breaking space as \u00A0 |
21012 // IE treats non-breaking space as \u00A0 |
20505 return (ch === ' ' || ch === '\r' || ch === '\t' || |
21013 return (ch === ' ' || ch === '\r' || ch === '\t' || |
20548 break; |
21056 break; |
20549 } |
21057 } |
20550 } |
21058 } |
20551 this.index++; |
21059 this.index++; |
20552 } |
21060 } |
20553 number = 1 * number; |
|
20554 this.tokens.push({ |
21061 this.tokens.push({ |
20555 index: start, |
21062 index: start, |
20556 text: number, |
21063 text: number, |
20557 constant: true, |
21064 constant: true, |
20558 fn: function() { return number; } |
21065 value: Number(number) |
20559 }); |
21066 }); |
20560 }, |
21067 }, |
20561 |
21068 |
20562 readIdent: function() { |
21069 readIdent: function() { |
20563 var expression = this.text; |
|
20564 |
|
20565 var ident = ''; |
|
20566 var start = this.index; |
21070 var start = this.index; |
20567 |
|
20568 var lastDot, peekIndex, methodName, ch; |
|
20569 |
|
20570 while (this.index < this.text.length) { |
21071 while (this.index < this.text.length) { |
20571 ch = this.text.charAt(this.index); |
21072 var ch = this.text.charAt(this.index); |
20572 if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { |
21073 if (!(this.isIdent(ch) || this.isNumber(ch))) { |
20573 if (ch === '.') lastDot = this.index; |
|
20574 ident += ch; |
|
20575 } else { |
|
20576 break; |
21074 break; |
20577 } |
21075 } |
20578 this.index++; |
21076 this.index++; |
20579 } |
21077 } |
20580 |
|
20581 //check if the identifier ends with . and if so move back one char |
|
20582 if (lastDot && ident[ident.length - 1] === '.') { |
|
20583 this.index--; |
|
20584 ident = ident.slice(0, -1); |
|
20585 lastDot = ident.lastIndexOf('.'); |
|
20586 if (lastDot === -1) { |
|
20587 lastDot = undefined; |
|
20588 } |
|
20589 } |
|
20590 |
|
20591 //check if this is not a method invocation and if it is back out to last dot |
|
20592 if (lastDot) { |
|
20593 peekIndex = this.index; |
|
20594 while (peekIndex < this.text.length) { |
|
20595 ch = this.text.charAt(peekIndex); |
|
20596 if (ch === '(') { |
|
20597 methodName = ident.substr(lastDot - start + 1); |
|
20598 ident = ident.substr(0, lastDot - start); |
|
20599 this.index = peekIndex; |
|
20600 break; |
|
20601 } |
|
20602 if (this.isWhitespace(ch)) { |
|
20603 peekIndex++; |
|
20604 } else { |
|
20605 break; |
|
20606 } |
|
20607 } |
|
20608 } |
|
20609 |
|
20610 this.tokens.push({ |
21078 this.tokens.push({ |
20611 index: start, |
21079 index: start, |
20612 text: ident, |
21080 text: this.text.slice(start, this.index), |
20613 fn: CONSTANTS[ident] || getterFn(ident, this.options, expression) |
21081 identifier: true |
20614 }); |
21082 }); |
20615 |
|
20616 if (methodName) { |
|
20617 this.tokens.push({ |
|
20618 index: lastDot, |
|
20619 text: '.' |
|
20620 }); |
|
20621 this.tokens.push({ |
|
20622 index: lastDot + 1, |
|
20623 text: methodName |
|
20624 }); |
|
20625 } |
|
20626 }, |
21083 }, |
20627 |
21084 |
20628 readString: function(quote) { |
21085 readString: function(quote) { |
20629 var start = this.index; |
21086 var start = this.index; |
20630 this.index++; |
21087 this.index++; |
20651 } else if (ch === quote) { |
21108 } else if (ch === quote) { |
20652 this.index++; |
21109 this.index++; |
20653 this.tokens.push({ |
21110 this.tokens.push({ |
20654 index: start, |
21111 index: start, |
20655 text: rawString, |
21112 text: rawString, |
20656 string: string, |
|
20657 constant: true, |
21113 constant: true, |
20658 fn: function() { return string; } |
21114 value: string |
20659 }); |
21115 }); |
20660 return; |
21116 return; |
20661 } else { |
21117 } else { |
20662 string += ch; |
21118 string += ch; |
20663 } |
21119 } |
20673 } |
21129 } |
20674 |
21130 |
20675 /** |
21131 /** |
20676 * @constructor |
21132 * @constructor |
20677 */ |
21133 */ |
20678 var Parser = function (lexer, $filter, options) { |
21134 var Parser = function(lexer, $filter, options) { |
20679 this.lexer = lexer; |
21135 this.lexer = lexer; |
20680 this.$filter = $filter; |
21136 this.$filter = $filter; |
20681 this.options = options; |
21137 this.options = options; |
20682 }; |
21138 }; |
20683 |
21139 |
20684 Parser.ZERO = extend(function () { |
21140 Parser.ZERO = extend(function() { |
20685 return 0; |
21141 return 0; |
20686 }, { |
21142 }, { |
20687 sharedGetter: true, |
21143 sharedGetter: true, |
20688 constant: true |
21144 constant: true |
20689 }); |
21145 }); |
20690 |
21146 |
20691 Parser.prototype = { |
21147 Parser.prototype = { |
20692 constructor: Parser, |
21148 constructor: Parser, |
20693 |
21149 |
20694 parse: function (text) { |
21150 parse: function(text) { |
20695 this.text = text; |
21151 this.text = text; |
20696 this.tokens = this.lexer.lex(text); |
21152 this.tokens = this.lexer.lex(text); |
20697 |
21153 |
20698 var value = this.statements(); |
21154 var value = this.statements(); |
20699 |
21155 |
20705 value.constant = !!value.constant; |
21161 value.constant = !!value.constant; |
20706 |
21162 |
20707 return value; |
21163 return value; |
20708 }, |
21164 }, |
20709 |
21165 |
20710 primary: function () { |
21166 primary: function() { |
20711 var primary; |
21167 var primary; |
20712 if (this.expect('(')) { |
21168 if (this.expect('(')) { |
20713 primary = this.filterChain(); |
21169 primary = this.filterChain(); |
20714 this.consume(')'); |
21170 this.consume(')'); |
20715 } else if (this.expect('[')) { |
21171 } else if (this.expect('[')) { |
20716 primary = this.arrayDeclaration(); |
21172 primary = this.arrayDeclaration(); |
20717 } else if (this.expect('{')) { |
21173 } else if (this.expect('{')) { |
20718 primary = this.object(); |
21174 primary = this.object(); |
21175 } else if (this.peek().identifier && this.peek().text in CONSTANTS) { |
|
21176 primary = CONSTANTS[this.consume().text]; |
|
21177 } else if (this.peek().identifier) { |
|
21178 primary = this.identifier(); |
|
21179 } else if (this.peek().constant) { |
|
21180 primary = this.constant(); |
|
20719 } else { |
21181 } else { |
20720 var token = this.expect(); |
21182 this.throwError('not a primary expression', this.peek()); |
20721 primary = token.fn; |
|
20722 if (!primary) { |
|
20723 this.throwError('not a primary expression', token); |
|
20724 } |
|
20725 if (token.constant) { |
|
20726 primary.constant = true; |
|
20727 primary.literal = true; |
|
20728 } |
|
20729 } |
21183 } |
20730 |
21184 |
20731 var next, context; |
21185 var next, context; |
20732 while ((next = this.expect('(', '[', '.'))) { |
21186 while ((next = this.expect('(', '[', '.'))) { |
20733 if (next.text === '(') { |
21187 if (next.text === '(') { |
20757 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); |
21211 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); |
20758 return this.tokens[0]; |
21212 return this.tokens[0]; |
20759 }, |
21213 }, |
20760 |
21214 |
20761 peek: function(e1, e2, e3, e4) { |
21215 peek: function(e1, e2, e3, e4) { |
20762 if (this.tokens.length > 0) { |
21216 return this.peekAhead(0, e1, e2, e3, e4); |
20763 var token = this.tokens[0]; |
21217 }, |
21218 peekAhead: function(i, e1, e2, e3, e4) { |
|
21219 if (this.tokens.length > i) { |
|
21220 var token = this.tokens[i]; |
|
20764 var t = token.text; |
21221 var t = token.text; |
20765 if (t === e1 || t === e2 || t === e3 || t === e4 || |
21222 if (t === e1 || t === e2 || t === e3 || t === e4 || |
20766 (!e1 && !e2 && !e3 && !e4)) { |
21223 (!e1 && !e2 && !e3 && !e4)) { |
20767 return token; |
21224 return token; |
20768 } |
21225 } |
20769 } |
21226 } |
20770 return false; |
21227 return false; |
20771 }, |
21228 }, |
20772 |
21229 |
20773 expect: function(e1, e2, e3, e4){ |
21230 expect: function(e1, e2, e3, e4) { |
20774 var token = this.peek(e1, e2, e3, e4); |
21231 var token = this.peek(e1, e2, e3, e4); |
20775 if (token) { |
21232 if (token) { |
20776 this.tokens.shift(); |
21233 this.tokens.shift(); |
20777 return token; |
21234 return token; |
20778 } |
21235 } |
20779 return false; |
21236 return false; |
20780 }, |
21237 }, |
20781 |
21238 |
20782 consume: function(e1){ |
21239 consume: function(e1) { |
20783 if (!this.expect(e1)) { |
21240 if (this.tokens.length === 0) { |
21241 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); |
|
21242 } |
|
21243 |
|
21244 var token = this.expect(e1); |
|
21245 if (!token) { |
|
20784 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); |
21246 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); |
20785 } |
21247 } |
21248 return token; |
|
20786 }, |
21249 }, |
20787 |
21250 |
20788 unaryFn: function(fn, right) { |
21251 unaryFn: function(op, right) { |
21252 var fn = OPERATORS[op]; |
|
20789 return extend(function $parseUnaryFn(self, locals) { |
21253 return extend(function $parseUnaryFn(self, locals) { |
20790 return fn(self, locals, right); |
21254 return fn(self, locals, right); |
20791 }, { |
21255 }, { |
20792 constant:right.constant, |
21256 constant:right.constant, |
20793 inputs: [right] |
21257 inputs: [right] |
20794 }); |
21258 }); |
20795 }, |
21259 }, |
20796 |
21260 |
20797 binaryFn: function(left, fn, right, isBranching) { |
21261 binaryFn: function(left, op, right, isBranching) { |
21262 var fn = OPERATORS[op]; |
|
20798 return extend(function $parseBinaryFn(self, locals) { |
21263 return extend(function $parseBinaryFn(self, locals) { |
20799 return fn(self, locals, left, right); |
21264 return fn(self, locals, left, right); |
20800 }, { |
21265 }, { |
20801 constant: left.constant && right.constant, |
21266 constant: left.constant && right.constant, |
20802 inputs: !isBranching && [left, right] |
21267 inputs: !isBranching && [left, right] |
21268 }); |
|
21269 }, |
|
21270 |
|
21271 identifier: function() { |
|
21272 var id = this.consume().text; |
|
21273 |
|
21274 //Continue reading each `.identifier` unless it is a method invocation |
|
21275 while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) { |
|
21276 id += this.consume().text + this.consume().text; |
|
21277 } |
|
21278 |
|
21279 return getterFn(id, this.options, this.text); |
|
21280 }, |
|
21281 |
|
21282 constant: function() { |
|
21283 var value = this.consume().value; |
|
21284 |
|
21285 return extend(function $parseConstant() { |
|
21286 return value; |
|
21287 }, { |
|
21288 constant: true, |
|
21289 literal: true |
|
20803 }); |
21290 }); |
20804 }, |
21291 }, |
20805 |
21292 |
20806 statements: function() { |
21293 statements: function() { |
20807 var statements = []; |
21294 var statements = []; |
20832 } |
21319 } |
20833 return left; |
21320 return left; |
20834 }, |
21321 }, |
20835 |
21322 |
20836 filter: function(inputFn) { |
21323 filter: function(inputFn) { |
20837 var token = this.expect(); |
21324 var fn = this.$filter(this.consume().text); |
20838 var fn = this.$filter(token.text); |
|
20839 var argsFn; |
21325 var argsFn; |
20840 var args; |
21326 var args; |
20841 |
21327 |
20842 if (this.peek(':')) { |
21328 if (this.peek(':')) { |
20843 argsFn = []; |
21329 argsFn = []; |
20896 var left = this.logicalOR(); |
21382 var left = this.logicalOR(); |
20897 var middle; |
21383 var middle; |
20898 var token; |
21384 var token; |
20899 if ((token = this.expect('?'))) { |
21385 if ((token = this.expect('?'))) { |
20900 middle = this.assignment(); |
21386 middle = this.assignment(); |
20901 if ((token = this.expect(':'))) { |
21387 if (this.consume(':')) { |
20902 var right = this.assignment(); |
21388 var right = this.assignment(); |
20903 |
21389 |
20904 return extend(function $parseTernary(self, locals){ |
21390 return extend(function $parseTernary(self, locals) { |
20905 return left(self, locals) ? middle(self, locals) : right(self, locals); |
21391 return left(self, locals) ? middle(self, locals) : right(self, locals); |
20906 }, { |
21392 }, { |
20907 constant: left.constant && middle.constant && right.constant |
21393 constant: left.constant && middle.constant && right.constant |
20908 }); |
21394 }); |
20909 |
|
20910 } else { |
|
20911 this.throwError('expected :', token); |
|
20912 } |
21395 } |
20913 } |
21396 } |
20914 |
21397 |
20915 return left; |
21398 return left; |
20916 }, |
21399 }, |
20917 |
21400 |
20918 logicalOR: function() { |
21401 logicalOR: function() { |
20919 var left = this.logicalAND(); |
21402 var left = this.logicalAND(); |
20920 var token; |
21403 var token; |
20921 while ((token = this.expect('||'))) { |
21404 while ((token = this.expect('||'))) { |
20922 left = this.binaryFn(left, token.fn, this.logicalAND(), true); |
21405 left = this.binaryFn(left, token.text, this.logicalAND(), true); |
20923 } |
21406 } |
20924 return left; |
21407 return left; |
20925 }, |
21408 }, |
20926 |
21409 |
20927 logicalAND: function() { |
21410 logicalAND: function() { |
20928 var left = this.equality(); |
21411 var left = this.equality(); |
20929 var token; |
21412 var token; |
20930 if ((token = this.expect('&&'))) { |
21413 while ((token = this.expect('&&'))) { |
20931 left = this.binaryFn(left, token.fn, this.logicalAND(), true); |
21414 left = this.binaryFn(left, token.text, this.equality(), true); |
20932 } |
21415 } |
20933 return left; |
21416 return left; |
20934 }, |
21417 }, |
20935 |
21418 |
20936 equality: function() { |
21419 equality: function() { |
20937 var left = this.relational(); |
21420 var left = this.relational(); |
20938 var token; |
21421 var token; |
20939 if ((token = this.expect('==','!=','===','!=='))) { |
21422 while ((token = this.expect('==','!=','===','!=='))) { |
20940 left = this.binaryFn(left, token.fn, this.equality()); |
21423 left = this.binaryFn(left, token.text, this.relational()); |
20941 } |
21424 } |
20942 return left; |
21425 return left; |
20943 }, |
21426 }, |
20944 |
21427 |
20945 relational: function() { |
21428 relational: function() { |
20946 var left = this.additive(); |
21429 var left = this.additive(); |
20947 var token; |
21430 var token; |
20948 if ((token = this.expect('<', '>', '<=', '>='))) { |
21431 while ((token = this.expect('<', '>', '<=', '>='))) { |
20949 left = this.binaryFn(left, token.fn, this.relational()); |
21432 left = this.binaryFn(left, token.text, this.additive()); |
20950 } |
21433 } |
20951 return left; |
21434 return left; |
20952 }, |
21435 }, |
20953 |
21436 |
20954 additive: function() { |
21437 additive: function() { |
20955 var left = this.multiplicative(); |
21438 var left = this.multiplicative(); |
20956 var token; |
21439 var token; |
20957 while ((token = this.expect('+','-'))) { |
21440 while ((token = this.expect('+','-'))) { |
20958 left = this.binaryFn(left, token.fn, this.multiplicative()); |
21441 left = this.binaryFn(left, token.text, this.multiplicative()); |
20959 } |
21442 } |
20960 return left; |
21443 return left; |
20961 }, |
21444 }, |
20962 |
21445 |
20963 multiplicative: function() { |
21446 multiplicative: function() { |
20964 var left = this.unary(); |
21447 var left = this.unary(); |
20965 var token; |
21448 var token; |
20966 while ((token = this.expect('*','/','%'))) { |
21449 while ((token = this.expect('*','/','%'))) { |
20967 left = this.binaryFn(left, token.fn, this.unary()); |
21450 left = this.binaryFn(left, token.text, this.unary()); |
20968 } |
21451 } |
20969 return left; |
21452 return left; |
20970 }, |
21453 }, |
20971 |
21454 |
20972 unary: function() { |
21455 unary: function() { |
20973 var token; |
21456 var token; |
20974 if (this.expect('+')) { |
21457 if (this.expect('+')) { |
20975 return this.primary(); |
21458 return this.primary(); |
20976 } else if ((token = this.expect('-'))) { |
21459 } else if ((token = this.expect('-'))) { |
20977 return this.binaryFn(Parser.ZERO, token.fn, this.unary()); |
21460 return this.binaryFn(Parser.ZERO, token.text, this.unary()); |
20978 } else if ((token = this.expect('!'))) { |
21461 } else if ((token = this.expect('!'))) { |
20979 return this.unaryFn(token.fn, this.unary()); |
21462 return this.unaryFn(token.text, this.unary()); |
20980 } else { |
21463 } else { |
20981 return this.primary(); |
21464 return this.primary(); |
20982 } |
21465 } |
20983 }, |
21466 }, |
20984 |
21467 |
20985 fieldAccess: function(object) { |
21468 fieldAccess: function(object) { |
20986 var expression = this.text; |
21469 var getter = this.identifier(); |
20987 var field = this.expect().text; |
|
20988 var getter = getterFn(field, this.options, expression); |
|
20989 |
21470 |
20990 return extend(function $parseFieldAccess(scope, locals, self) { |
21471 return extend(function $parseFieldAccess(scope, locals, self) { |
20991 return getter(self || object(scope, locals)); |
21472 var o = self || object(scope, locals); |
21473 return (o == null) ? undefined : getter(o); |
|
20992 }, { |
21474 }, { |
20993 assign: function(scope, value, locals) { |
21475 assign: function(scope, value, locals) { |
20994 var o = object(scope, locals); |
21476 var o = object(scope, locals); |
20995 if (!o) object.assign(scope, o = {}); |
21477 if (!o) object.assign(scope, o = {}); |
20996 return setter(o, field, value, expression); |
21478 return getter.assign(o, value); |
20997 } |
21479 } |
20998 }); |
21480 }); |
20999 }, |
21481 }, |
21000 |
21482 |
21001 objectIndex: function(obj) { |
21483 objectIndex: function(obj) { |
21036 var expressionText = this.text; |
21518 var expressionText = this.text; |
21037 // we can safely reuse the array across invocations |
21519 // we can safely reuse the array across invocations |
21038 var args = argsFn.length ? [] : null; |
21520 var args = argsFn.length ? [] : null; |
21039 |
21521 |
21040 return function $parseFunctionCall(scope, locals) { |
21522 return function $parseFunctionCall(scope, locals) { |
21041 var context = contextGetter ? contextGetter(scope, locals) : scope; |
21523 var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope; |
21042 var fn = fnGetter(scope, locals, context) || noop; |
21524 var fn = fnGetter(scope, locals, context) || noop; |
21043 |
21525 |
21044 if (args) { |
21526 if (args) { |
21045 var i = argsFn.length; |
21527 var i = argsFn.length; |
21046 while (i--) { |
21528 while (i--) { |
21049 } |
21531 } |
21050 |
21532 |
21051 ensureSafeObject(context, expressionText); |
21533 ensureSafeObject(context, expressionText); |
21052 ensureSafeFunction(fn, expressionText); |
21534 ensureSafeFunction(fn, expressionText); |
21053 |
21535 |
21054 // IE stupidity! (IE doesn't have apply for some native functions) |
21536 // IE doesn't have apply for some native functions |
21055 var v = fn.apply |
21537 var v = fn.apply |
21056 ? fn.apply(context, args) |
21538 ? fn.apply(context, args) |
21057 : fn(args[0], args[1], args[2], args[3], args[4]); |
21539 : fn(args[0], args[1], args[2], args[3], args[4]); |
21058 |
21540 |
21059 return ensureSafeObject(v, expressionText); |
21541 return ensureSafeObject(v, expressionText); |
21060 }; |
21542 }; |
21061 }, |
21543 }, |
21062 |
21544 |
21063 // This is used with json array declaration |
21545 // This is used with json array declaration |
21064 arrayDeclaration: function () { |
21546 arrayDeclaration: function() { |
21065 var elementFns = []; |
21547 var elementFns = []; |
21066 if (this.peekToken().text !== ']') { |
21548 if (this.peekToken().text !== ']') { |
21067 do { |
21549 do { |
21068 if (this.peek(']')) { |
21550 if (this.peek(']')) { |
21069 // Support trailing commas per ES5.1. |
21551 // Support trailing commas per ES5.1. |
21070 break; |
21552 break; |
21071 } |
21553 } |
21072 var elementFn = this.expression(); |
21554 elementFns.push(this.expression()); |
21073 elementFns.push(elementFn); |
|
21074 } while (this.expect(',')); |
21555 } while (this.expect(',')); |
21075 } |
21556 } |
21076 this.consume(']'); |
21557 this.consume(']'); |
21077 |
21558 |
21078 return extend(function $parseArrayLiteral(self, locals) { |
21559 return extend(function $parseArrayLiteral(self, locals) { |
21086 constant: elementFns.every(isConstant), |
21567 constant: elementFns.every(isConstant), |
21087 inputs: elementFns |
21568 inputs: elementFns |
21088 }); |
21569 }); |
21089 }, |
21570 }, |
21090 |
21571 |
21091 object: function () { |
21572 object: function() { |
21092 var keys = [], valueFns = []; |
21573 var keys = [], valueFns = []; |
21093 if (this.peekToken().text !== '}') { |
21574 if (this.peekToken().text !== '}') { |
21094 do { |
21575 do { |
21095 if (this.peek('}')) { |
21576 if (this.peek('}')) { |
21096 // Support trailing commas per ES5.1. |
21577 // Support trailing commas per ES5.1. |
21097 break; |
21578 break; |
21098 } |
21579 } |
21099 var token = this.expect(); |
21580 var token = this.consume(); |
21100 keys.push(token.string || token.text); |
21581 if (token.constant) { |
21582 keys.push(token.value); |
|
21583 } else if (token.identifier) { |
|
21584 keys.push(token.text); |
|
21585 } else { |
|
21586 this.throwError("invalid key", token); |
|
21587 } |
|
21101 this.consume(':'); |
21588 this.consume(':'); |
21102 var value = this.expression(); |
21589 valueFns.push(this.expression()); |
21103 valueFns.push(value); |
|
21104 } while (this.expect(',')); |
21590 } while (this.expect(',')); |
21105 } |
21591 } |
21106 this.consume('}'); |
21592 this.consume('}'); |
21107 |
21593 |
21108 return extend(function $parseObjectLiteral(self, locals) { |
21594 return extend(function $parseObjectLiteral(self, locals) { |
21141 ensureSafeObject(obj[key], fullExp); |
21627 ensureSafeObject(obj[key], fullExp); |
21142 obj[key] = setValue; |
21628 obj[key] = setValue; |
21143 return setValue; |
21629 return setValue; |
21144 } |
21630 } |
21145 |
21631 |
21146 var getterFnCache = createMap(); |
21632 var getterFnCacheDefault = createMap(); |
21633 var getterFnCacheExpensive = createMap(); |
|
21634 |
|
21635 function isPossiblyDangerousMemberName(name) { |
|
21636 return name == 'constructor'; |
|
21637 } |
|
21147 |
21638 |
21148 /** |
21639 /** |
21149 * Implementation of the "Black Hole" variant from: |
21640 * Implementation of the "Black Hole" variant from: |
21150 * - http://jsperf.com/angularjs-parse-getter/4 |
21641 * - http://jsperf.com/angularjs-parse-getter/4 |
21151 * - http://jsperf.com/path-evaluation-simplified/7 |
21642 * - http://jsperf.com/path-evaluation-simplified/7 |
21152 */ |
21643 */ |
21153 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) { |
21644 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) { |
21154 ensureSafeMemberName(key0, fullExp); |
21645 ensureSafeMemberName(key0, fullExp); |
21155 ensureSafeMemberName(key1, fullExp); |
21646 ensureSafeMemberName(key1, fullExp); |
21156 ensureSafeMemberName(key2, fullExp); |
21647 ensureSafeMemberName(key2, fullExp); |
21157 ensureSafeMemberName(key3, fullExp); |
21648 ensureSafeMemberName(key3, fullExp); |
21158 ensureSafeMemberName(key4, fullExp); |
21649 ensureSafeMemberName(key4, fullExp); |
21650 var eso = function(o) { |
|
21651 return ensureSafeObject(o, fullExp); |
|
21652 }; |
|
21653 var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity; |
|
21654 var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity; |
|
21655 var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity; |
|
21656 var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity; |
|
21657 var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity; |
|
21159 |
21658 |
21160 return function cspSafeGetter(scope, locals) { |
21659 return function cspSafeGetter(scope, locals) { |
21161 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; |
21660 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; |
21162 |
21661 |
21163 if (pathVal == null) return pathVal; |
21662 if (pathVal == null) return pathVal; |
21164 pathVal = pathVal[key0]; |
21663 pathVal = eso0(pathVal[key0]); |
21165 |
21664 |
21166 if (!key1) return pathVal; |
21665 if (!key1) return pathVal; |
21167 if (pathVal == null) return undefined; |
21666 if (pathVal == null) return undefined; |
21168 pathVal = pathVal[key1]; |
21667 pathVal = eso1(pathVal[key1]); |
21169 |
21668 |
21170 if (!key2) return pathVal; |
21669 if (!key2) return pathVal; |
21171 if (pathVal == null) return undefined; |
21670 if (pathVal == null) return undefined; |
21172 pathVal = pathVal[key2]; |
21671 pathVal = eso2(pathVal[key2]); |
21173 |
21672 |
21174 if (!key3) return pathVal; |
21673 if (!key3) return pathVal; |
21175 if (pathVal == null) return undefined; |
21674 if (pathVal == null) return undefined; |
21176 pathVal = pathVal[key3]; |
21675 pathVal = eso3(pathVal[key3]); |
21177 |
21676 |
21178 if (!key4) return pathVal; |
21677 if (!key4) return pathVal; |
21179 if (pathVal == null) return undefined; |
21678 if (pathVal == null) return undefined; |
21180 pathVal = pathVal[key4]; |
21679 pathVal = eso4(pathVal[key4]); |
21181 |
21680 |
21182 return pathVal; |
21681 return pathVal; |
21183 }; |
21682 }; |
21184 } |
21683 } |
21185 |
21684 |
21685 function getterFnWithEnsureSafeObject(fn, fullExpression) { |
|
21686 return function(s, l) { |
|
21687 return fn(s, l, ensureSafeObject, fullExpression); |
|
21688 }; |
|
21689 } |
|
21690 |
|
21186 function getterFn(path, options, fullExp) { |
21691 function getterFn(path, options, fullExp) { |
21692 var expensiveChecks = options.expensiveChecks; |
|
21693 var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault); |
|
21187 var fn = getterFnCache[path]; |
21694 var fn = getterFnCache[path]; |
21188 |
|
21189 if (fn) return fn; |
21695 if (fn) return fn; |
21696 |
|
21190 |
21697 |
21191 var pathKeys = path.split('.'), |
21698 var pathKeys = path.split('.'), |
21192 pathKeysLength = pathKeys.length; |
21699 pathKeysLength = pathKeys.length; |
21193 |
21700 |
21194 // http://jsperf.com/angularjs-parse-getter/6 |
21701 // http://jsperf.com/angularjs-parse-getter/6 |
21195 if (options.csp) { |
21702 if (options.csp) { |
21196 if (pathKeysLength < 6) { |
21703 if (pathKeysLength < 6) { |
21197 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp); |
21704 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks); |
21198 } else { |
21705 } else { |
21199 fn = function cspSafeGetter(scope, locals) { |
21706 fn = function cspSafeGetter(scope, locals) { |
21200 var i = 0, val; |
21707 var i = 0, val; |
21201 do { |
21708 do { |
21202 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], |
21709 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], |
21203 pathKeys[i++], fullExp)(scope, locals); |
21710 pathKeys[i++], fullExp, expensiveChecks)(scope, locals); |
21204 |
21711 |
21205 locals = undefined; // clear after first iteration |
21712 locals = undefined; // clear after first iteration |
21206 scope = val; |
21713 scope = val; |
21207 } while (i < pathKeysLength); |
21714 } while (i < pathKeysLength); |
21208 return val; |
21715 return val; |
21209 }; |
21716 }; |
21210 } |
21717 } |
21211 } else { |
21718 } else { |
21212 var code = ''; |
21719 var code = ''; |
21720 if (expensiveChecks) { |
|
21721 code += 's = eso(s, fe);\nl = eso(l, fe);\n'; |
|
21722 } |
|
21723 var needsEnsureSafeObject = expensiveChecks; |
|
21213 forEach(pathKeys, function(key, index) { |
21724 forEach(pathKeys, function(key, index) { |
21214 ensureSafeMemberName(key, fullExp); |
21725 ensureSafeMemberName(key, fullExp); |
21215 code += 'if(s == null) return undefined;\n' + |
21726 var lookupJs = (index |
21216 's='+ (index |
|
21217 // we simply dereference 's' on any .dot notation |
21727 // we simply dereference 's' on any .dot notation |
21218 ? 's' |
21728 ? 's' |
21219 // but if we are first then we check locals first, and if so read it first |
21729 // but if we are first then we check locals first, and if so read it first |
21220 : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key + ';\n'; |
21730 : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key; |
21731 if (expensiveChecks || isPossiblyDangerousMemberName(key)) { |
|
21732 lookupJs = 'eso(' + lookupJs + ', fe)'; |
|
21733 needsEnsureSafeObject = true; |
|
21734 } |
|
21735 code += 'if(s == null) return undefined;\n' + |
|
21736 's=' + lookupJs + ';\n'; |
|
21221 }); |
21737 }); |
21222 code += 'return s;'; |
21738 code += 'return s;'; |
21223 |
21739 |
21224 /* jshint -W054 */ |
21740 /* jshint -W054 */ |
21225 var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals |
21741 var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject |
21226 /* jshint +W054 */ |
21742 /* jshint +W054 */ |
21227 evaledFnGetter.toString = valueFn(code); |
21743 evaledFnGetter.toString = valueFn(code); |
21228 |
21744 if (needsEnsureSafeObject) { |
21745 evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp); |
|
21746 } |
|
21229 fn = evaledFnGetter; |
21747 fn = evaledFnGetter; |
21230 } |
21748 } |
21231 |
21749 |
21232 fn.sharedGetter = true; |
21750 fn.sharedGetter = true; |
21233 fn.assign = function(self, value) { |
21751 fn.assign = function(self, value) { |
21235 }; |
21753 }; |
21236 getterFnCache[path] = fn; |
21754 getterFnCache[path] = fn; |
21237 return fn; |
21755 return fn; |
21238 } |
21756 } |
21239 |
21757 |
21758 var objectValueOf = Object.prototype.valueOf; |
|
21759 |
|
21760 function getValueOf(value) { |
|
21761 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); |
|
21762 } |
|
21763 |
|
21240 /////////////////////////////////// |
21764 /////////////////////////////////// |
21241 |
21765 |
21242 /** |
21766 /** |
21243 * @ngdoc service |
21767 * @ngdoc service |
21244 * @name $parse |
21768 * @name $parse |
21287 * @description |
21811 * @description |
21288 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} |
21812 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} |
21289 * service. |
21813 * service. |
21290 */ |
21814 */ |
21291 function $ParseProvider() { |
21815 function $ParseProvider() { |
21292 var cache = createMap(); |
21816 var cacheDefault = createMap(); |
21293 |
21817 var cacheExpensive = createMap(); |
21294 var $parseOptions = { |
21818 |
21295 csp: false |
|
21296 }; |
|
21297 |
21819 |
21298 |
21820 |
21299 this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { |
21821 this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { |
21300 $parseOptions.csp = $sniffer.csp; |
21822 var $parseOptions = { |
21823 csp: $sniffer.csp, |
|
21824 expensiveChecks: false |
|
21825 }, |
|
21826 $parseOptionsExpensive = { |
|
21827 csp: $sniffer.csp, |
|
21828 expensiveChecks: true |
|
21829 }; |
|
21301 |
21830 |
21302 function wrapSharedExpression(exp) { |
21831 function wrapSharedExpression(exp) { |
21303 var wrapped = exp; |
21832 var wrapped = exp; |
21304 |
21833 |
21305 if (exp.sharedGetter) { |
21834 if (exp.sharedGetter) { |
21312 } |
21841 } |
21313 |
21842 |
21314 return wrapped; |
21843 return wrapped; |
21315 } |
21844 } |
21316 |
21845 |
21317 return function $parse(exp, interceptorFn) { |
21846 return function $parse(exp, interceptorFn, expensiveChecks) { |
21318 var parsedExpression, oneTime, cacheKey; |
21847 var parsedExpression, oneTime, cacheKey; |
21319 |
21848 |
21320 switch (typeof exp) { |
21849 switch (typeof exp) { |
21321 case 'string': |
21850 case 'string': |
21322 cacheKey = exp = exp.trim(); |
21851 cacheKey = exp = exp.trim(); |
21323 |
21852 |
21853 var cache = (expensiveChecks ? cacheExpensive : cacheDefault); |
|
21324 parsedExpression = cache[cacheKey]; |
21854 parsedExpression = cache[cacheKey]; |
21325 |
21855 |
21326 if (!parsedExpression) { |
21856 if (!parsedExpression) { |
21327 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { |
21857 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { |
21328 oneTime = true; |
21858 oneTime = true; |
21329 exp = exp.substring(2); |
21859 exp = exp.substring(2); |
21330 } |
21860 } |
21331 |
21861 |
21332 var lexer = new Lexer($parseOptions); |
21862 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; |
21333 var parser = new Parser(lexer, $filter, $parseOptions); |
21863 var lexer = new Lexer(parseOptions); |
21864 var parser = new Parser(lexer, $filter, parseOptions); |
|
21334 parsedExpression = parser.parse(exp); |
21865 parsedExpression = parser.parse(exp); |
21335 |
21866 |
21336 if (parsedExpression.constant) { |
21867 if (parsedExpression.constant) { |
21337 parsedExpression.$$watchDelegate = constantWatchDelegate; |
21868 parsedExpression.$$watchDelegate = constantWatchDelegate; |
21338 } else if (oneTime) { |
21869 } else if (oneTime) { |
21381 if (typeof newValue === 'object') { |
21912 if (typeof newValue === 'object') { |
21382 |
21913 |
21383 // attempt to convert the value to a primitive type |
21914 // attempt to convert the value to a primitive type |
21384 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can |
21915 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can |
21385 // be cheaply dirty-checked |
21916 // be cheaply dirty-checked |
21386 newValue = newValue.valueOf(); |
21917 newValue = getValueOf(newValue); |
21387 |
21918 |
21388 if (typeof newValue === 'object') { |
21919 if (typeof newValue === 'object') { |
21389 // objects/arrays are not supported - deep-watching them would be too expensive |
21920 // objects/arrays are not supported - deep-watching them would be too expensive |
21390 return false; |
21921 return false; |
21391 } |
21922 } |
21408 inputExpressions = inputExpressions[0]; |
21939 inputExpressions = inputExpressions[0]; |
21409 return scope.$watch(function expressionInputWatch(scope) { |
21940 return scope.$watch(function expressionInputWatch(scope) { |
21410 var newInputValue = inputExpressions(scope); |
21941 var newInputValue = inputExpressions(scope); |
21411 if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) { |
21942 if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) { |
21412 lastResult = parsedExpression(scope); |
21943 lastResult = parsedExpression(scope); |
21413 oldInputValue = newInputValue && newInputValue.valueOf(); |
21944 oldInputValue = newInputValue && getValueOf(newInputValue); |
21414 } |
21945 } |
21415 return lastResult; |
21946 return lastResult; |
21416 }, listener, objectEquality); |
21947 }, listener, objectEquality); |
21417 } |
21948 } |
21418 |
21949 |
21425 var changed = false; |
21956 var changed = false; |
21426 |
21957 |
21427 for (var i = 0, ii = inputExpressions.length; i < ii; i++) { |
21958 for (var i = 0, ii = inputExpressions.length; i < ii; i++) { |
21428 var newInputValue = inputExpressions[i](scope); |
21959 var newInputValue = inputExpressions[i](scope); |
21429 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { |
21960 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { |
21430 oldInputValueOfValues[i] = newInputValue && newInputValue.valueOf(); |
21961 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); |
21431 } |
21962 } |
21432 } |
21963 } |
21433 |
21964 |
21434 if (changed) { |
21965 if (changed) { |
21435 lastResult = parsedExpression(scope); |
21966 lastResult = parsedExpression(scope); |
21447 lastValue = value; |
21978 lastValue = value; |
21448 if (isFunction(listener)) { |
21979 if (isFunction(listener)) { |
21449 listener.apply(this, arguments); |
21980 listener.apply(this, arguments); |
21450 } |
21981 } |
21451 if (isDefined(value)) { |
21982 if (isDefined(value)) { |
21452 scope.$$postDigest(function () { |
21983 scope.$$postDigest(function() { |
21453 if (isDefined(lastValue)) { |
21984 if (isDefined(lastValue)) { |
21454 unwatch(); |
21985 unwatch(); |
21455 } |
21986 } |
21456 }); |
21987 }); |
21457 } |
21988 } |
21458 }, objectEquality); |
21989 }, objectEquality); |
21459 } |
21990 } |
21460 |
21991 |
21461 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { |
21992 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { |
21462 var unwatch; |
21993 var unwatch, lastValue; |
21463 return unwatch = scope.$watch(function oneTimeWatch(scope) { |
21994 return unwatch = scope.$watch(function oneTimeWatch(scope) { |
21464 return parsedExpression(scope); |
21995 return parsedExpression(scope); |
21465 }, function oneTimeListener(value, old, scope) { |
21996 }, function oneTimeListener(value, old, scope) { |
21997 lastValue = value; |
|
21466 if (isFunction(listener)) { |
21998 if (isFunction(listener)) { |
21467 listener.call(this, value, old, scope); |
21999 listener.call(this, value, old, scope); |
21468 } |
22000 } |
21469 if (isAllDefined(value)) { |
22001 if (isAllDefined(value)) { |
21470 scope.$$postDigest(function () { |
22002 scope.$$postDigest(function() { |
21471 if(isAllDefined(value)) unwatch(); |
22003 if (isAllDefined(lastValue)) unwatch(); |
21472 }); |
22004 }); |
21473 } |
22005 } |
21474 }, objectEquality); |
22006 }, objectEquality); |
21475 |
22007 |
21476 function isAllDefined(value) { |
22008 function isAllDefined(value) { |
21477 var allDefined = true; |
22009 var allDefined = true; |
21478 forEach(value, function (val) { |
22010 forEach(value, function(val) { |
21479 if (!isDefined(val)) allDefined = false; |
22011 if (!isDefined(val)) allDefined = false; |
21480 }); |
22012 }); |
21481 return allDefined; |
22013 return allDefined; |
21482 } |
22014 } |
21483 } |
22015 } |
21494 }, objectEquality); |
22026 }, objectEquality); |
21495 } |
22027 } |
21496 |
22028 |
21497 function addInterceptor(parsedExpression, interceptorFn) { |
22029 function addInterceptor(parsedExpression, interceptorFn) { |
21498 if (!interceptorFn) return parsedExpression; |
22030 if (!interceptorFn) return parsedExpression; |
21499 |
22031 var watchDelegate = parsedExpression.$$watchDelegate; |
21500 var fn = function interceptedExpression(scope, locals) { |
22032 |
22033 var regularWatch = |
|
22034 watchDelegate !== oneTimeLiteralWatchDelegate && |
|
22035 watchDelegate !== oneTimeWatchDelegate; |
|
22036 |
|
22037 var fn = regularWatch ? function regularInterceptedExpression(scope, locals) { |
|
22038 var value = parsedExpression(scope, locals); |
|
22039 return interceptorFn(value, scope, locals); |
|
22040 } : function oneTimeInterceptedExpression(scope, locals) { |
|
21501 var value = parsedExpression(scope, locals); |
22041 var value = parsedExpression(scope, locals); |
21502 var result = interceptorFn(value, scope, locals); |
22042 var result = interceptorFn(value, scope, locals); |
21503 // we only return the interceptor's result if the |
22043 // we only return the interceptor's result if the |
21504 // initial value is defined (for bind-once) |
22044 // initial value is defined (for bind-once) |
21505 return isDefined(value) ? result : value; |
22045 return isDefined(value) ? result : value; |
21525 * @ngdoc service |
22065 * @ngdoc service |
21526 * @name $q |
22066 * @name $q |
21527 * @requires $rootScope |
22067 * @requires $rootScope |
21528 * |
22068 * |
21529 * @description |
22069 * @description |
21530 * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). |
22070 * A service that helps you run functions asynchronously, and use their return values (or exceptions) |
22071 * when they are done processing. |
|
22072 * |
|
22073 * This is an implementation of promises/deferred objects inspired by |
|
22074 * [Kris Kowal's Q](https://github.com/kriskowal/q). |
|
21531 * |
22075 * |
21532 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred |
22076 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred |
21533 * implementations, and the other which resembles ES6 promises to some degree. |
22077 * implementations, and the other which resembles ES6 promises to some degree. |
21534 * |
22078 * |
21535 * # $q constructor |
22079 * # $q constructor |
21542 * available yet. |
22086 * available yet. |
21543 * |
22087 * |
21544 * It can be used like so: |
22088 * It can be used like so: |
21545 * |
22089 * |
21546 * ```js |
22090 * ```js |
21547 * return $q(function(resolve, reject) { |
22091 * // for the purpose of this example let's assume that variables `$q` and `okToGreet` |
21548 * // perform some asynchronous operation, resolve or reject the promise when appropriate. |
22092 * // are available in the current lexical scope (they could have been injected or passed in). |
21549 * setInterval(function() { |
22093 * |
21550 * if (pollStatus > 0) { |
22094 * function asyncGreet(name) { |
21551 * resolve(polledValue); |
22095 * // perform some asynchronous operation, resolve or reject the promise when appropriate. |
21552 * } else if (pollStatus < 0) { |
22096 * return $q(function(resolve, reject) { |
21553 * reject(polledValue); |
22097 * setTimeout(function() { |
21554 * } else { |
22098 * if (okToGreet(name)) { |
21555 * pollStatus = pollAgain(function(value) { |
22099 * resolve('Hello, ' + name + '!'); |
21556 * polledValue = value; |
22100 * } else { |
21557 * }); |
22101 * reject('Greeting ' + name + ' is not allowed.'); |
21558 * } |
22102 * } |
21559 * }, 10000); |
22103 * }, 1000); |
21560 * }). |
22104 * }); |
21561 * then(function(value) { |
22105 * } |
21562 * // handle success |
22106 * |
22107 * var promise = asyncGreet('Robin Hood'); |
|
22108 * promise.then(function(greeting) { |
|
22109 * alert('Success: ' + greeting); |
|
21563 * }, function(reason) { |
22110 * }, function(reason) { |
21564 * // handle failure |
22111 * alert('Failed: ' + reason); |
21565 * }); |
22112 * }); |
21566 * ``` |
22113 * ``` |
21567 * |
22114 * |
21568 * Note: progress/notify callbacks are not currently supported via the ES6-style interface. |
22115 * Note: progress/notify callbacks are not currently supported via the ES6-style interface. |
21569 * |
22116 * |
21575 * |
22122 * |
21576 * From the perspective of dealing with error handling, deferred and promise APIs are to |
22123 * From the perspective of dealing with error handling, deferred and promise APIs are to |
21577 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. |
22124 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. |
21578 * |
22125 * |
21579 * ```js |
22126 * ```js |
21580 * // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet` |
22127 * // for the purpose of this example let's assume that variables `$q` and `okToGreet` |
21581 * // are available in the current lexical scope (they could have been injected or passed in). |
22128 * // are available in the current lexical scope (they could have been injected or passed in). |
21582 * |
22129 * |
21583 * function asyncGreet(name) { |
22130 * function asyncGreet(name) { |
21584 * var deferred = $q.defer(); |
22131 * var deferred = $q.defer(); |
21585 * |
22132 * |
21658 * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback |
22205 * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback |
21659 * method. |
22206 * method. |
21660 * |
22207 * |
21661 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` |
22208 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` |
21662 * |
22209 * |
21663 * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, |
22210 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, |
21664 * but to do so without modifying the final value. This is useful to release resources or do some |
22211 * but to do so without modifying the final value. This is useful to release resources or do some |
21665 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full |
22212 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full |
21666 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for |
22213 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for |
21667 * more information. |
22214 * more information. |
21668 * |
|
21669 * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as |
|
21670 * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to |
|
21671 * make your code IE8 and Android 2.x compatible. |
|
21672 * |
22215 * |
21673 * # Chaining promises |
22216 * # Chaining promises |
21674 * |
22217 * |
21675 * Because calling the `then` method of a promise returns a new derived promise, it is easily |
22218 * Because calling the `then` method of a promise returns a new derived promise, it is easily |
21676 * possible to create a chain of promises: |
22219 * possible to create a chain of promises: |
21834 } else if (state.status === 1) { |
22377 } else if (state.status === 1) { |
21835 promise.resolve(state.value); |
22378 promise.resolve(state.value); |
21836 } else { |
22379 } else { |
21837 promise.reject(state.value); |
22380 promise.reject(state.value); |
21838 } |
22381 } |
21839 } catch(e) { |
22382 } catch (e) { |
21840 promise.reject(e); |
22383 promise.reject(e); |
21841 exceptionHandler(e); |
22384 exceptionHandler(e); |
21842 } |
22385 } |
21843 } |
22386 } |
21844 } |
22387 } |
21884 } else { |
22427 } else { |
21885 this.promise.$$state.value = val; |
22428 this.promise.$$state.value = val; |
21886 this.promise.$$state.status = 1; |
22429 this.promise.$$state.status = 1; |
21887 scheduleProcessQueue(this.promise.$$state); |
22430 scheduleProcessQueue(this.promise.$$state); |
21888 } |
22431 } |
21889 } catch(e) { |
22432 } catch (e) { |
21890 fns[1](e); |
22433 fns[1](e); |
21891 exceptionHandler(e); |
22434 exceptionHandler(e); |
21892 } |
22435 } |
21893 }, |
22436 }, |
21894 |
22437 |
21912 for (var i = 0, ii = callbacks.length; i < ii; i++) { |
22455 for (var i = 0, ii = callbacks.length; i < ii; i++) { |
21913 result = callbacks[i][0]; |
22456 result = callbacks[i][0]; |
21914 callback = callbacks[i][3]; |
22457 callback = callbacks[i][3]; |
21915 try { |
22458 try { |
21916 result.notify(isFunction(callback) ? callback(progress) : progress); |
22459 result.notify(isFunction(callback) ? callback(progress) : progress); |
21917 } catch(e) { |
22460 } catch (e) { |
21918 exceptionHandler(e); |
22461 exceptionHandler(e); |
21919 } |
22462 } |
21920 } |
22463 } |
21921 }); |
22464 }); |
21922 } |
22465 } |
21977 |
22520 |
21978 var handleCallback = function handleCallback(value, isResolved, callback) { |
22521 var handleCallback = function handleCallback(value, isResolved, callback) { |
21979 var callbackOutput = null; |
22522 var callbackOutput = null; |
21980 try { |
22523 try { |
21981 if (isFunction(callback)) callbackOutput = callback(); |
22524 if (isFunction(callback)) callbackOutput = callback(); |
21982 } catch(e) { |
22525 } catch (e) { |
21983 return makePromise(e, false); |
22526 return makePromise(e, false); |
21984 } |
22527 } |
21985 if (isPromiseLike(callbackOutput)) { |
22528 if (isPromiseLike(callbackOutput)) { |
21986 return callbackOutput.then(function() { |
22529 return callbackOutput.then(function() { |
21987 return makePromise(value, isResolved); |
22530 return makePromise(value, isResolved); |
22085 $Q.all = all; |
22628 $Q.all = all; |
22086 |
22629 |
22087 return $Q; |
22630 return $Q; |
22088 } |
22631 } |
22089 |
22632 |
22090 function $$RAFProvider(){ //rAF |
22633 function $$RAFProvider() { //rAF |
22091 this.$get = ['$window', '$timeout', function($window, $timeout) { |
22634 this.$get = ['$window', '$timeout', function($window, $timeout) { |
22092 var requestAnimationFrame = $window.requestAnimationFrame || |
22635 var requestAnimationFrame = $window.requestAnimationFrame || |
22093 $window.webkitRequestAnimationFrame || |
22636 $window.webkitRequestAnimationFrame; |
22094 $window.mozRequestAnimationFrame; |
|
22095 |
22637 |
22096 var cancelAnimationFrame = $window.cancelAnimationFrame || |
22638 var cancelAnimationFrame = $window.cancelAnimationFrame || |
22097 $window.webkitCancelAnimationFrame || |
22639 $window.webkitCancelAnimationFrame || |
22098 $window.mozCancelAnimationFrame || |
|
22099 $window.webkitCancelRequestAnimationFrame; |
22640 $window.webkitCancelRequestAnimationFrame; |
22100 |
22641 |
22101 var rafSupported = !!requestAnimationFrame; |
22642 var rafSupported = !!requestAnimationFrame; |
22102 var raf = rafSupported |
22643 var raf = rafSupported |
22103 ? function(fn) { |
22644 ? function(fn) { |
22184 * All other scopes are descendant scopes of the root scope. Scopes provide separation |
22725 * All other scopes are descendant scopes of the root scope. Scopes provide separation |
22185 * between the model and the view, via a mechanism for watching the model for changes. |
22726 * between the model and the view, via a mechanism for watching the model for changes. |
22186 * They also provide an event emission/broadcast and subscription facility. See the |
22727 * They also provide an event emission/broadcast and subscription facility. See the |
22187 * {@link guide/scope developer guide on scopes}. |
22728 * {@link guide/scope developer guide on scopes}. |
22188 */ |
22729 */ |
22189 function $RootScopeProvider(){ |
22730 function $RootScopeProvider() { |
22190 var TTL = 10; |
22731 var TTL = 10; |
22191 var $rootScopeMinErr = minErr('$rootScope'); |
22732 var $rootScopeMinErr = minErr('$rootScope'); |
22192 var lastDirtyWatch = null; |
22733 var lastDirtyWatch = null; |
22193 var applyAsyncId = null; |
22734 var applyAsyncId = null; |
22194 |
22735 |
22198 } |
22739 } |
22199 return TTL; |
22740 return TTL; |
22200 }; |
22741 }; |
22201 |
22742 |
22202 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', |
22743 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', |
22203 function( $injector, $exceptionHandler, $parse, $browser) { |
22744 function($injector, $exceptionHandler, $parse, $browser) { |
22204 |
22745 |
22205 /** |
22746 /** |
22206 * @ngdoc type |
22747 * @ngdoc type |
22207 * @name $rootScope.Scope |
22748 * @name $rootScope.Scope |
22208 * |
22749 * |
22222 * ```js |
22763 * ```js |
22223 var parent = $rootScope; |
22764 var parent = $rootScope; |
22224 var child = parent.$new(); |
22765 var child = parent.$new(); |
22225 |
22766 |
22226 parent.salutation = "Hello"; |
22767 parent.salutation = "Hello"; |
22227 child.name = "World"; |
|
22228 expect(child.salutation).toEqual('Hello'); |
22768 expect(child.salutation).toEqual('Hello'); |
22229 |
22769 |
22230 child.salutation = "Welcome"; |
22770 child.salutation = "Welcome"; |
22231 expect(child.salutation).toEqual('Welcome'); |
22771 expect(child.salutation).toEqual('Welcome'); |
22232 expect(parent.salutation).toEqual('Hello'); |
22772 expect(parent.salutation).toEqual('Hello'); |
22233 * ``` |
22773 * ``` |
22774 * |
|
22775 * When interacting with `Scope` in tests, additional helper methods are available on the |
|
22776 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional |
|
22777 * details. |
|
22234 * |
22778 * |
22235 * |
22779 * |
22236 * @param {Object.<string, function()>=} providers Map of service factory which need to be |
22780 * @param {Object.<string, function()>=} providers Map of service factory which need to be |
22237 * provided for the current scope. Defaults to {@link ng}. |
22781 * provided for the current scope. Defaults to {@link ng}. |
22238 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should |
22782 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should |
22541 var firstRun = true; |
23085 var firstRun = true; |
22542 |
23086 |
22543 if (!watchExpressions.length) { |
23087 if (!watchExpressions.length) { |
22544 // No expressions means we call the listener ASAP |
23088 // No expressions means we call the listener ASAP |
22545 var shouldCall = true; |
23089 var shouldCall = true; |
22546 self.$evalAsync(function () { |
23090 self.$evalAsync(function() { |
22547 if (shouldCall) listener(newValues, newValues, self); |
23091 if (shouldCall) listener(newValues, newValues, self); |
22548 }); |
23092 }); |
22549 return function deregisterWatchGroup() { |
23093 return function deregisterWatchGroup() { |
22550 shouldCall = false; |
23094 shouldCall = false; |
22551 }; |
23095 }; |
22558 oldValues[0] = oldValue; |
23102 oldValues[0] = oldValue; |
22559 listener(newValues, (value === oldValue) ? newValues : oldValues, scope); |
23103 listener(newValues, (value === oldValue) ? newValues : oldValues, scope); |
22560 }); |
23104 }); |
22561 } |
23105 } |
22562 |
23106 |
22563 forEach(watchExpressions, function (expr, i) { |
23107 forEach(watchExpressions, function(expr, i) { |
22564 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { |
23108 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { |
22565 newValues[i] = value; |
23109 newValues[i] = value; |
22566 oldValues[i] = oldValue; |
23110 oldValues[i] = oldValue; |
22567 if (!changeReactionScheduled) { |
23111 if (!changeReactionScheduled) { |
22568 changeReactionScheduled = true; |
23112 changeReactionScheduled = true; |
22668 |
23212 |
22669 function $watchCollectionInterceptor(_value) { |
23213 function $watchCollectionInterceptor(_value) { |
22670 newValue = _value; |
23214 newValue = _value; |
22671 var newLength, key, bothNaN, newItem, oldItem; |
23215 var newLength, key, bothNaN, newItem, oldItem; |
22672 |
23216 |
23217 // If the new value is undefined, then return undefined as the watch may be a one-time watch |
|
23218 if (isUndefined(newValue)) return; |
|
23219 |
|
22673 if (!isObject(newValue)) { // if primitive |
23220 if (!isObject(newValue)) { // if primitive |
22674 if (oldValue !== newValue) { |
23221 if (oldValue !== newValue) { |
22675 oldValue = newValue; |
23222 oldValue = newValue; |
22676 changeDetected++; |
23223 changeDetected++; |
22677 } |
23224 } |
22730 } |
23277 } |
22731 } |
23278 } |
22732 if (oldLength > newLength) { |
23279 if (oldLength > newLength) { |
22733 // we used to have more keys, need to find them and destroy them. |
23280 // we used to have more keys, need to find them and destroy them. |
22734 changeDetected++; |
23281 changeDetected++; |
22735 for(key in oldValue) { |
23282 for (key in oldValue) { |
22736 if (!newValue.hasOwnProperty(key)) { |
23283 if (!newValue.hasOwnProperty(key)) { |
22737 oldLength--; |
23284 oldLength--; |
22738 delete oldValue[key]; |
23285 delete oldValue[key]; |
22739 } |
23286 } |
22740 } |
23287 } |
22850 |
23397 |
22851 do { // "while dirty" loop |
23398 do { // "while dirty" loop |
22852 dirty = false; |
23399 dirty = false; |
22853 current = target; |
23400 current = target; |
22854 |
23401 |
22855 while(asyncQueue.length) { |
23402 while (asyncQueue.length) { |
22856 try { |
23403 try { |
22857 asyncTask = asyncQueue.shift(); |
23404 asyncTask = asyncQueue.shift(); |
22858 asyncTask.scope.$eval(asyncTask.expression); |
23405 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); |
22859 } catch (e) { |
23406 } catch (e) { |
22860 $exceptionHandler(e); |
23407 $exceptionHandler(e); |
22861 } |
23408 } |
22862 lastDirtyWatch = null; |
23409 lastDirtyWatch = null; |
22863 } |
23410 } |
22883 watch.last = watch.eq ? copy(value, null) : value; |
23430 watch.last = watch.eq ? copy(value, null) : value; |
22884 watch.fn(value, ((last === initWatchVal) ? value : last), current); |
23431 watch.fn(value, ((last === initWatchVal) ? value : last), current); |
22885 if (ttl < 5) { |
23432 if (ttl < 5) { |
22886 logIdx = 4 - ttl; |
23433 logIdx = 4 - ttl; |
22887 if (!watchLog[logIdx]) watchLog[logIdx] = []; |
23434 if (!watchLog[logIdx]) watchLog[logIdx] = []; |
22888 logMsg = (isFunction(watch.exp)) |
23435 watchLog[logIdx].push({ |
22889 ? 'fn: ' + (watch.exp.name || watch.exp.toString()) |
23436 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, |
22890 : watch.exp; |
23437 newVal: value, |
22891 logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); |
23438 oldVal: last |
22892 watchLog[logIdx].push(logMsg); |
23439 }); |
22893 } |
23440 } |
22894 } else if (watch === lastDirtyWatch) { |
23441 } else if (watch === lastDirtyWatch) { |
22895 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers |
23442 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers |
22896 // have already been tested. |
23443 // have already been tested. |
22897 dirty = false; |
23444 dirty = false; |
22907 // Insanity Warning: scope depth-first traversal |
23454 // Insanity Warning: scope depth-first traversal |
22908 // yes, this code is a bit crazy, but it works and we have tests to prove it! |
23455 // yes, this code is a bit crazy, but it works and we have tests to prove it! |
22909 // this piece should be kept in sync with the traversal in $broadcast |
23456 // this piece should be kept in sync with the traversal in $broadcast |
22910 if (!(next = (current.$$childHead || |
23457 if (!(next = (current.$$childHead || |
22911 (current !== target && current.$$nextSibling)))) { |
23458 (current !== target && current.$$nextSibling)))) { |
22912 while(current !== target && !(next = current.$$nextSibling)) { |
23459 while (current !== target && !(next = current.$$nextSibling)) { |
22913 current = current.$parent; |
23460 current = current.$parent; |
22914 } |
23461 } |
22915 } |
23462 } |
22916 } while ((current = next)); |
23463 } while ((current = next)); |
22917 |
23464 |
22918 // `break traverseScopesLoop;` takes us to here |
23465 // `break traverseScopesLoop;` takes us to here |
22919 |
23466 |
22920 if((dirty || asyncQueue.length) && !(ttl--)) { |
23467 if ((dirty || asyncQueue.length) && !(ttl--)) { |
22921 clearPhase(); |
23468 clearPhase(); |
22922 throw $rootScopeMinErr('infdig', |
23469 throw $rootScopeMinErr('infdig', |
22923 '{0} $digest() iterations reached. Aborting!\n' + |
23470 '{0} $digest() iterations reached. Aborting!\n' + |
22924 'Watchers fired in the last 5 iterations: {1}', |
23471 'Watchers fired in the last 5 iterations: {1}', |
22925 TTL, toJson(watchLog)); |
23472 TTL, watchLog); |
22926 } |
23473 } |
22927 |
23474 |
22928 } while (dirty || asyncQueue.length); |
23475 } while (dirty || asyncQueue.length); |
22929 |
23476 |
22930 clearPhase(); |
23477 clearPhase(); |
22931 |
23478 |
22932 while(postDigestQueue.length) { |
23479 while (postDigestQueue.length) { |
22933 try { |
23480 try { |
22934 postDigestQueue.shift()(); |
23481 postDigestQueue.shift()(); |
22935 } catch (e) { |
23482 } catch (e) { |
22936 $exceptionHandler(e); |
23483 $exceptionHandler(e); |
22937 } |
23484 } |
23068 * @param {(string|function())=} expression An angular expression to be executed. |
23615 * @param {(string|function())=} expression An angular expression to be executed. |
23069 * |
23616 * |
23070 * - `string`: execute using the rules as defined in {@link guide/expression expression}. |
23617 * - `string`: execute using the rules as defined in {@link guide/expression expression}. |
23071 * - `function(scope)`: execute the function with the current `scope` parameter. |
23618 * - `function(scope)`: execute the function with the current `scope` parameter. |
23072 * |
23619 * |
23620 * @param {(object)=} locals Local variables object, useful for overriding values in scope. |
|
23073 */ |
23621 */ |
23074 $evalAsync: function(expr) { |
23622 $evalAsync: function(expr, locals) { |
23075 // if we are outside of an $digest loop and this is the first time we are scheduling async |
23623 // if we are outside of an $digest loop and this is the first time we are scheduling async |
23076 // task also schedule async auto-flush |
23624 // task also schedule async auto-flush |
23077 if (!$rootScope.$$phase && !asyncQueue.length) { |
23625 if (!$rootScope.$$phase && !asyncQueue.length) { |
23078 $browser.defer(function() { |
23626 $browser.defer(function() { |
23079 if (asyncQueue.length) { |
23627 if (asyncQueue.length) { |
23080 $rootScope.$digest(); |
23628 $rootScope.$digest(); |
23081 } |
23629 } |
23082 }); |
23630 }); |
23083 } |
23631 } |
23084 |
23632 |
23085 asyncQueue.push({scope: this, expression: expr}); |
23633 asyncQueue.push({scope: this, expression: expr, locals: locals}); |
23086 }, |
23634 }, |
23087 |
23635 |
23088 $$postDigest : function(fn) { |
23636 $$postDigest: function(fn) { |
23089 postDigestQueue.push(fn); |
23637 postDigestQueue.push(fn); |
23090 }, |
23638 }, |
23091 |
23639 |
23092 /** |
23640 /** |
23093 * @ngdoc method |
23641 * @ngdoc method |
23220 current.$$listenerCount[name]++; |
23768 current.$$listenerCount[name]++; |
23221 } while ((current = current.$parent)); |
23769 } while ((current = current.$parent)); |
23222 |
23770 |
23223 var self = this; |
23771 var self = this; |
23224 return function() { |
23772 return function() { |
23225 namedListeners[namedListeners.indexOf(listener)] = null; |
23773 var indexOfListener = namedListeners.indexOf(listener); |
23226 decrementListenerCount(self, 1, name); |
23774 if (indexOfListener !== -1) { |
23775 namedListeners[indexOfListener] = null; |
|
23776 decrementListenerCount(self, 1, name); |
|
23777 } |
|
23227 }; |
23778 }; |
23228 }, |
23779 }, |
23229 |
23780 |
23230 |
23781 |
23231 /** |
23782 /** |
23268 i, length; |
23819 i, length; |
23269 |
23820 |
23270 do { |
23821 do { |
23271 namedListeners = scope.$$listeners[name] || empty; |
23822 namedListeners = scope.$$listeners[name] || empty; |
23272 event.currentScope = scope; |
23823 event.currentScope = scope; |
23273 for (i=0, length=namedListeners.length; i<length; i++) { |
23824 for (i = 0, length = namedListeners.length; i < length; i++) { |
23274 |
23825 |
23275 // if listeners were deregistered, defragment the array |
23826 // if listeners were deregistered, defragment the array |
23276 if (!namedListeners[i]) { |
23827 if (!namedListeners[i]) { |
23277 namedListeners.splice(i, 1); |
23828 namedListeners.splice(i, 1); |
23278 i--; |
23829 i--; |
23342 |
23893 |
23343 //down while you can, then up and next sibling or up and next sibling until back at root |
23894 //down while you can, then up and next sibling or up and next sibling until back at root |
23344 while ((current = next)) { |
23895 while ((current = next)) { |
23345 event.currentScope = current; |
23896 event.currentScope = current; |
23346 listeners = current.$$listeners[name] || []; |
23897 listeners = current.$$listeners[name] || []; |
23347 for (i=0, length = listeners.length; i<length; i++) { |
23898 for (i = 0, length = listeners.length; i < length; i++) { |
23348 // if listeners were deregistered, defragment the array |
23899 // if listeners were deregistered, defragment the array |
23349 if (!listeners[i]) { |
23900 if (!listeners[i]) { |
23350 listeners.splice(i, 1); |
23901 listeners.splice(i, 1); |
23351 i--; |
23902 i--; |
23352 length--; |
23903 length--; |
23353 continue; |
23904 continue; |
23354 } |
23905 } |
23355 |
23906 |
23356 try { |
23907 try { |
23357 listeners[i].apply(null, listenerArgs); |
23908 listeners[i].apply(null, listenerArgs); |
23358 } catch(e) { |
23909 } catch (e) { |
23359 $exceptionHandler(e); |
23910 $exceptionHandler(e); |
23360 } |
23911 } |
23361 } |
23912 } |
23362 |
23913 |
23363 // Insanity Warning: scope depth-first traversal |
23914 // Insanity Warning: scope depth-first traversal |
23364 // yes, this code is a bit crazy, but it works and we have tests to prove it! |
23915 // yes, this code is a bit crazy, but it works and we have tests to prove it! |
23365 // this piece should be kept in sync with the traversal in $digest |
23916 // this piece should be kept in sync with the traversal in $digest |
23366 // (though it differs due to having the extra check for $$listenerCount) |
23917 // (though it differs due to having the extra check for $$listenerCount) |
23367 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || |
23918 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || |
23368 (current !== target && current.$$nextSibling)))) { |
23919 (current !== target && current.$$nextSibling)))) { |
23369 while(current !== target && !(next = current.$$nextSibling)) { |
23920 while (current !== target && !(next = current.$$nextSibling)) { |
23370 current = current.$parent; |
23921 current = current.$parent; |
23371 } |
23922 } |
23372 } |
23923 } |
23373 } |
23924 } |
23374 |
23925 |
23418 |
23969 |
23419 function flushApplyAsync() { |
23970 function flushApplyAsync() { |
23420 while (applyAsyncQueue.length) { |
23971 while (applyAsyncQueue.length) { |
23421 try { |
23972 try { |
23422 applyAsyncQueue.shift()(); |
23973 applyAsyncQueue.shift()(); |
23423 } catch(e) { |
23974 } catch (e) { |
23424 $exceptionHandler(e); |
23975 $exceptionHandler(e); |
23425 } |
23976 } |
23426 } |
23977 } |
23427 applyAsyncId = null; |
23978 applyAsyncId = null; |
23428 } |
23979 } |
23498 return function sanitizeUri(uri, isImage) { |
24049 return function sanitizeUri(uri, isImage) { |
23499 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; |
24050 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; |
23500 var normalizedVal; |
24051 var normalizedVal; |
23501 normalizedVal = urlResolve(uri).href; |
24052 normalizedVal = urlResolve(uri).href; |
23502 if (normalizedVal !== '' && !normalizedVal.match(regex)) { |
24053 if (normalizedVal !== '' && !normalizedVal.match(regex)) { |
23503 return 'unsafe:'+normalizedVal; |
24054 return 'unsafe:' + normalizedVal; |
23504 } |
24055 } |
23505 return uri; |
24056 return uri; |
23506 }; |
24057 }; |
23507 }; |
24058 }; |
23508 } |
24059 } |
23518 RESOURCE_URL: 'resourceUrl', |
24069 RESOURCE_URL: 'resourceUrl', |
23519 JS: 'js' |
24070 JS: 'js' |
23520 }; |
24071 }; |
23521 |
24072 |
23522 // Helper functions follow. |
24073 // Helper functions follow. |
23523 |
|
23524 // Copied from: |
|
23525 // http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962 |
|
23526 // Prereq: s is a string. |
|
23527 function escapeForRegexp(s) { |
|
23528 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). |
|
23529 replace(/\x08/g, '\\x08'); |
|
23530 } |
|
23531 |
|
23532 |
24074 |
23533 function adjustMatcher(matcher) { |
24075 function adjustMatcher(matcher) { |
23534 if (matcher === 'self') { |
24076 if (matcher === 'self') { |
23535 return matcher; |
24077 return matcher; |
23536 } else if (isString(matcher)) { |
24078 } else if (isString(matcher)) { |
23663 * same origin resource requests. |
24205 * same origin resource requests. |
23664 * |
24206 * |
23665 * @description |
24207 * @description |
23666 * Sets/Gets the whitelist of trusted resource URLs. |
24208 * Sets/Gets the whitelist of trusted resource URLs. |
23667 */ |
24209 */ |
23668 this.resourceUrlWhitelist = function (value) { |
24210 this.resourceUrlWhitelist = function(value) { |
23669 if (arguments.length) { |
24211 if (arguments.length) { |
23670 resourceUrlWhitelist = adjustMatchers(value); |
24212 resourceUrlWhitelist = adjustMatchers(value); |
23671 } |
24213 } |
23672 return resourceUrlWhitelist; |
24214 return resourceUrlWhitelist; |
23673 }; |
24215 }; |
23697 * |
24239 * |
23698 * @description |
24240 * @description |
23699 * Sets/Gets the blacklist of trusted resource URLs. |
24241 * Sets/Gets the blacklist of trusted resource URLs. |
23700 */ |
24242 */ |
23701 |
24243 |
23702 this.resourceUrlBlacklist = function (value) { |
24244 this.resourceUrlBlacklist = function(value) { |
23703 if (arguments.length) { |
24245 if (arguments.length) { |
23704 resourceUrlBlacklist = adjustMatchers(value); |
24246 resourceUrlBlacklist = adjustMatchers(value); |
23705 } |
24247 } |
23706 return resourceUrlBlacklist; |
24248 return resourceUrlBlacklist; |
23707 }; |
24249 }; |
23915 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer |
24457 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer |
23916 * to these contexts as privileged or SCE contexts. |
24458 * to these contexts as privileged or SCE contexts. |
23917 * |
24459 * |
23918 * As of version 1.2, Angular ships with SCE enabled by default. |
24460 * As of version 1.2, Angular ships with SCE enabled by default. |
23919 * |
24461 * |
23920 * Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows |
24462 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow |
23921 * one to execute arbitrary javascript by the use of the expression() syntax. Refer |
24463 * one to execute arbitrary javascript by the use of the expression() syntax. Refer |
23922 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them. |
24464 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them. |
23923 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` |
24465 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` |
23924 * to the top of your HTML document. |
24466 * to the top of your HTML document. |
23925 * |
24467 * |
23962 * |
24504 * |
23963 * ## How does it work? |
24505 * ## How does it work? |
23964 * |
24506 * |
23965 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted |
24507 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted |
23966 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link |
24508 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link |
23967 * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the |
24509 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the |
23968 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. |
24510 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. |
23969 * |
24511 * |
23970 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link |
24512 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link |
23971 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly |
24513 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly |
23972 * simplified): |
24514 * simplified): |
24178 * @return {boolean} true if SCE is enabled, false otherwise. |
24720 * @return {boolean} true if SCE is enabled, false otherwise. |
24179 * |
24721 * |
24180 * @description |
24722 * @description |
24181 * Enables/disables SCE and returns the current value. |
24723 * Enables/disables SCE and returns the current value. |
24182 */ |
24724 */ |
24183 this.enabled = function (value) { |
24725 this.enabled = function(value) { |
24184 if (arguments.length) { |
24726 if (arguments.length) { |
24185 enabled = !!value; |
24727 enabled = !!value; |
24186 } |
24728 } |
24187 return enabled; |
24729 return enabled; |
24188 }; |
24730 }; |
24232 * Inheritance happens to capture this in a natural way. In some future, we |
24774 * Inheritance happens to capture this in a natural way. In some future, we |
24233 * may not use inheritance anymore. That is OK because no code outside of |
24775 * may not use inheritance anymore. That is OK because no code outside of |
24234 * sce.js and sceSpecs.js would need to be aware of this detail. |
24776 * sce.js and sceSpecs.js would need to be aware of this detail. |
24235 */ |
24777 */ |
24236 |
24778 |
24237 this.$get = ['$parse', '$sniffer', '$sceDelegate', function( |
24779 this.$get = ['$parse', '$sceDelegate', function( |
24238 $parse, $sniffer, $sceDelegate) { |
24780 $parse, $sceDelegate) { |
24239 // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows |
24781 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow |
24240 // the "expression(javascript expression)" syntax which is insecure. |
24782 // the "expression(javascript expression)" syntax which is insecure. |
24241 if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) { |
24783 if (enabled && msie < 8) { |
24242 throw $sceMinErr('iequirks', |
24784 throw $sceMinErr('iequirks', |
24243 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' + |
24785 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + |
24244 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + |
24786 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + |
24245 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); |
24787 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); |
24246 } |
24788 } |
24247 |
24789 |
24248 var sce = shallowCopy(SCE_CONTEXTS); |
24790 var sce = shallowCopy(SCE_CONTEXTS); |
24256 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. |
24798 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. |
24257 * |
24799 * |
24258 * @description |
24800 * @description |
24259 * Returns a boolean indicating if SCE is enabled. |
24801 * Returns a boolean indicating if SCE is enabled. |
24260 */ |
24802 */ |
24261 sce.isEnabled = function () { |
24803 sce.isEnabled = function() { |
24262 return enabled; |
24804 return enabled; |
24263 }; |
24805 }; |
24264 sce.trustAs = $sceDelegate.trustAs; |
24806 sce.trustAs = $sceDelegate.trustAs; |
24265 sce.getTrusted = $sceDelegate.getTrusted; |
24807 sce.getTrusted = $sceDelegate.getTrusted; |
24266 sce.valueOf = $sceDelegate.valueOf; |
24808 sce.valueOf = $sceDelegate.valueOf; |
24292 sce.parseAs = function sceParseAs(type, expr) { |
24834 sce.parseAs = function sceParseAs(type, expr) { |
24293 var parsed = $parse(expr); |
24835 var parsed = $parse(expr); |
24294 if (parsed.literal && parsed.constant) { |
24836 if (parsed.literal && parsed.constant) { |
24295 return parsed; |
24837 return parsed; |
24296 } else { |
24838 } else { |
24297 return $parse(expr, function (value) { |
24839 return $parse(expr, function(value) { |
24298 return sce.getTrusted(type, value); |
24840 return sce.getTrusted(type, value); |
24299 }); |
24841 }); |
24300 } |
24842 } |
24301 }; |
24843 }; |
24302 |
24844 |
24461 * @ngdoc method |
25003 * @ngdoc method |
24462 * @name $sce#parseAsHtml |
25004 * @name $sce#parseAsHtml |
24463 * |
25005 * |
24464 * @description |
25006 * @description |
24465 * Shorthand method. `$sce.parseAsHtml(expression string)` → |
25007 * Shorthand method. `$sce.parseAsHtml(expression string)` → |
24466 * {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`} |
25008 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} |
24467 * |
25009 * |
24468 * @param {string} expression String expression to compile. |
25010 * @param {string} expression String expression to compile. |
24469 * @returns {function(context, locals)} a function which represents the compiled expression: |
25011 * @returns {function(context, locals)} a function which represents the compiled expression: |
24470 * |
25012 * |
24471 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
25013 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
24478 * @ngdoc method |
25020 * @ngdoc method |
24479 * @name $sce#parseAsCss |
25021 * @name $sce#parseAsCss |
24480 * |
25022 * |
24481 * @description |
25023 * @description |
24482 * Shorthand method. `$sce.parseAsCss(value)` → |
25024 * Shorthand method. `$sce.parseAsCss(value)` → |
24483 * {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`} |
25025 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} |
24484 * |
25026 * |
24485 * @param {string} expression String expression to compile. |
25027 * @param {string} expression String expression to compile. |
24486 * @returns {function(context, locals)} a function which represents the compiled expression: |
25028 * @returns {function(context, locals)} a function which represents the compiled expression: |
24487 * |
25029 * |
24488 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
25030 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
24495 * @ngdoc method |
25037 * @ngdoc method |
24496 * @name $sce#parseAsUrl |
25038 * @name $sce#parseAsUrl |
24497 * |
25039 * |
24498 * @description |
25040 * @description |
24499 * Shorthand method. `$sce.parseAsUrl(value)` → |
25041 * Shorthand method. `$sce.parseAsUrl(value)` → |
24500 * {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`} |
25042 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} |
24501 * |
25043 * |
24502 * @param {string} expression String expression to compile. |
25044 * @param {string} expression String expression to compile. |
24503 * @returns {function(context, locals)} a function which represents the compiled expression: |
25045 * @returns {function(context, locals)} a function which represents the compiled expression: |
24504 * |
25046 * |
24505 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
25047 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
24512 * @ngdoc method |
25054 * @ngdoc method |
24513 * @name $sce#parseAsResourceUrl |
25055 * @name $sce#parseAsResourceUrl |
24514 * |
25056 * |
24515 * @description |
25057 * @description |
24516 * Shorthand method. `$sce.parseAsResourceUrl(value)` → |
25058 * Shorthand method. `$sce.parseAsResourceUrl(value)` → |
24517 * {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`} |
25059 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} |
24518 * |
25060 * |
24519 * @param {string} expression String expression to compile. |
25061 * @param {string} expression String expression to compile. |
24520 * @returns {function(context, locals)} a function which represents the compiled expression: |
25062 * @returns {function(context, locals)} a function which represents the compiled expression: |
24521 * |
25063 * |
24522 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
25064 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
24529 * @ngdoc method |
25071 * @ngdoc method |
24530 * @name $sce#parseAsJs |
25072 * @name $sce#parseAsJs |
24531 * |
25073 * |
24532 * @description |
25074 * @description |
24533 * Shorthand method. `$sce.parseAsJs(value)` → |
25075 * Shorthand method. `$sce.parseAsJs(value)` → |
24534 * {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`} |
25076 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} |
24535 * |
25077 * |
24536 * @param {string} expression String expression to compile. |
25078 * @param {string} expression String expression to compile. |
24537 * @returns {function(context, locals)} a function which represents the compiled expression: |
25079 * @returns {function(context, locals)} a function which represents the compiled expression: |
24538 * |
25080 * |
24539 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
25081 * * `context` – `{object}` – an object against which any expressions embedded in the strings |
24545 // Shorthand delegations. |
25087 // Shorthand delegations. |
24546 var parse = sce.parseAs, |
25088 var parse = sce.parseAs, |
24547 getTrusted = sce.getTrusted, |
25089 getTrusted = sce.getTrusted, |
24548 trustAs = sce.trustAs; |
25090 trustAs = sce.trustAs; |
24549 |
25091 |
24550 forEach(SCE_CONTEXTS, function (enumValue, name) { |
25092 forEach(SCE_CONTEXTS, function(enumValue, name) { |
24551 var lName = lowercase(name); |
25093 var lName = lowercase(name); |
24552 sce[camelCase("parse_as_" + lName)] = function (expr) { |
25094 sce[camelCase("parse_as_" + lName)] = function(expr) { |
24553 return parse(enumValue, expr); |
25095 return parse(enumValue, expr); |
24554 }; |
25096 }; |
24555 sce[camelCase("get_trusted_" + lName)] = function (value) { |
25097 sce[camelCase("get_trusted_" + lName)] = function(value) { |
24556 return getTrusted(enumValue, value); |
25098 return getTrusted(enumValue, value); |
24557 }; |
25099 }; |
24558 sce[camelCase("trust_as_" + lName)] = function (value) { |
25100 sce[camelCase("trust_as_" + lName)] = function(value) { |
24559 return trustAs(enumValue, value); |
25101 return trustAs(enumValue, value); |
24560 }; |
25102 }; |
24561 }); |
25103 }); |
24562 |
25104 |
24563 return sce; |
25105 return sce; |
24583 var eventSupport = {}, |
25125 var eventSupport = {}, |
24584 android = |
25126 android = |
24585 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), |
25127 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), |
24586 boxee = /Boxee/i.test(($window.navigator || {}).userAgent), |
25128 boxee = /Boxee/i.test(($window.navigator || {}).userAgent), |
24587 document = $document[0] || {}, |
25129 document = $document[0] || {}, |
24588 documentMode = document.documentMode, |
|
24589 vendorPrefix, |
25130 vendorPrefix, |
24590 vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, |
25131 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/, |
24591 bodyStyle = document.body && document.body.style, |
25132 bodyStyle = document.body && document.body.style, |
24592 transitions = false, |
25133 transitions = false, |
24593 animations = false, |
25134 animations = false, |
24594 match; |
25135 match; |
24595 |
25136 |
24596 if (bodyStyle) { |
25137 if (bodyStyle) { |
24597 for(var prop in bodyStyle) { |
25138 for (var prop in bodyStyle) { |
24598 if(match = vendorRegex.exec(prop)) { |
25139 if (match = vendorRegex.exec(prop)) { |
24599 vendorPrefix = match[0]; |
25140 vendorPrefix = match[0]; |
24600 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); |
25141 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); |
24601 break; |
25142 break; |
24602 } |
25143 } |
24603 } |
25144 } |
24604 |
25145 |
24605 if(!vendorPrefix) { |
25146 if (!vendorPrefix) { |
24606 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; |
25147 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; |
24607 } |
25148 } |
24608 |
25149 |
24609 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); |
25150 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); |
24610 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); |
25151 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); |
24611 |
25152 |
24612 if (android && (!transitions||!animations)) { |
25153 if (android && (!transitions || !animations)) { |
24613 transitions = isString(document.body.style.webkitTransition); |
25154 transitions = isString(document.body.style.webkitTransition); |
24614 animations = isString(document.body.style.webkitAnimation); |
25155 animations = isString(document.body.style.webkitAnimation); |
24615 } |
25156 } |
24616 } |
25157 } |
24617 |
25158 |
24630 // jshint +W018 |
25171 // jshint +W018 |
24631 hasEvent: function(event) { |
25172 hasEvent: function(event) { |
24632 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have |
25173 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have |
24633 // it. In particular the event is not fired when backspace or delete key are pressed or |
25174 // it. In particular the event is not fired when backspace or delete key are pressed or |
24634 // when cut operation is performed. |
25175 // when cut operation is performed. |
24635 if (event == 'input' && msie == 9) return false; |
25176 // IE10+ implements 'input' event but it erroneously fires under various situations, |
25177 // e.g. when placeholder changes, or a form is focused. |
|
25178 if (event === 'input' && msie <= 11) return false; |
|
24636 |
25179 |
24637 if (isUndefined(eventSupport[event])) { |
25180 if (isUndefined(eventSupport[event])) { |
24638 var divElm = document.createElement('div'); |
25181 var divElm = document.createElement('div'); |
24639 eventSupport[event] = 'on' + event in divElm; |
25182 eventSupport[event] = 'on' + event in divElm; |
24640 } |
25183 } |
24641 |
25184 |
24642 return eventSupport[event]; |
25185 return eventSupport[event]; |
24643 }, |
25186 }, |
24644 csp: csp(), |
25187 csp: csp(), |
24645 vendorPrefix: vendorPrefix, |
25188 vendorPrefix: vendorPrefix, |
24646 transitions : transitions, |
25189 transitions: transitions, |
24647 animations : animations, |
25190 animations: animations, |
24648 android: android, |
25191 android: android |
24649 msie : msie, |
|
24650 msieDocumentMode: documentMode |
|
24651 }; |
25192 }; |
24652 }]; |
25193 }]; |
24653 } |
25194 } |
24654 |
25195 |
24655 var $compileMinErr = minErr('$compile'); |
25196 var $compileMinErr = minErr('$compile'); |
24659 * @name $templateRequest |
25200 * @name $templateRequest |
24660 * |
25201 * |
24661 * @description |
25202 * @description |
24662 * The `$templateRequest` service downloads the provided template using `$http` and, upon success, |
25203 * The `$templateRequest` service downloads the provided template using `$http` and, upon success, |
24663 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data |
25204 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data |
24664 * of the HTTP request is empty then a `$compile` error will be thrown (the exception can be thwarted |
25205 * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted |
24665 * by setting the 2nd parameter of the function to true). |
25206 * by setting the 2nd parameter of the function to true). |
24666 * |
25207 * |
24667 * @param {string} tpl The HTTP request template URL |
25208 * @param {string} tpl The HTTP request template URL |
24668 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty |
25209 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty |
24669 * |
25210 * |
24675 this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) { |
25216 this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) { |
24676 function handleRequestFn(tpl, ignoreRequestError) { |
25217 function handleRequestFn(tpl, ignoreRequestError) { |
24677 var self = handleRequestFn; |
25218 var self = handleRequestFn; |
24678 self.totalPendingRequests++; |
25219 self.totalPendingRequests++; |
24679 |
25220 |
24680 return $http.get(tpl, { cache : $templateCache }) |
25221 var transformResponse = $http.defaults && $http.defaults.transformResponse; |
25222 |
|
25223 if (isArray(transformResponse)) { |
|
25224 transformResponse = transformResponse.filter(function(transformer) { |
|
25225 return transformer !== defaultHttpResponseTransform; |
|
25226 }); |
|
25227 } else if (transformResponse === defaultHttpResponseTransform) { |
|
25228 transformResponse = null; |
|
25229 } |
|
25230 |
|
25231 var httpOptions = { |
|
25232 cache: $templateCache, |
|
25233 transformResponse: transformResponse |
|
25234 }; |
|
25235 |
|
25236 return $http.get(tpl, httpOptions) |
|
24681 .then(function(response) { |
25237 .then(function(response) { |
24682 var html = response.data; |
|
24683 if(!html || html.length === 0) { |
|
24684 return handleError(); |
|
24685 } |
|
24686 |
|
24687 self.totalPendingRequests--; |
25238 self.totalPendingRequests--; |
24688 $templateCache.put(tpl, html); |
25239 return response.data; |
24689 return html; |
|
24690 }, handleError); |
25240 }, handleError); |
24691 |
25241 |
24692 function handleError() { |
25242 function handleError(resp) { |
24693 self.totalPendingRequests--; |
25243 self.totalPendingRequests--; |
24694 if (!ignoreRequestError) { |
25244 if (!ignoreRequestError) { |
24695 throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl); |
25245 throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl); |
24696 } |
25246 } |
24697 return $q.reject(); |
25247 return $q.reject(resp); |
24698 } |
25248 } |
24699 } |
25249 } |
24700 |
25250 |
24701 handleRequestFn.totalPendingRequests = 0; |
25251 handleRequestFn.totalPendingRequests = 0; |
24702 |
25252 |
24735 forEach(bindings, function(binding) { |
25285 forEach(bindings, function(binding) { |
24736 var dataBinding = angular.element(binding).data('$binding'); |
25286 var dataBinding = angular.element(binding).data('$binding'); |
24737 if (dataBinding) { |
25287 if (dataBinding) { |
24738 forEach(dataBinding, function(bindingName) { |
25288 forEach(dataBinding, function(bindingName) { |
24739 if (opt_exactMatch) { |
25289 if (opt_exactMatch) { |
24740 var matcher = new RegExp('(^|\\s)' + expression + '(\\s|\\||$)'); |
25290 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); |
24741 if (matcher.test(bindingName)) { |
25291 if (matcher.test(bindingName)) { |
24742 matches.push(binding); |
25292 matches.push(binding); |
24743 } |
25293 } |
24744 } else { |
25294 } else { |
24745 if (bindingName.indexOf(expression) != -1) { |
25295 if (bindingName.indexOf(expression) != -1) { |
24857 timeoutId; |
25407 timeoutId; |
24858 |
25408 |
24859 timeoutId = $browser.defer(function() { |
25409 timeoutId = $browser.defer(function() { |
24860 try { |
25410 try { |
24861 deferred.resolve(fn()); |
25411 deferred.resolve(fn()); |
24862 } catch(e) { |
25412 } catch (e) { |
24863 deferred.reject(e); |
25413 deferred.reject(e); |
24864 $exceptionHandler(e); |
25414 $exceptionHandler(e); |
24865 } |
25415 } |
24866 finally { |
25416 finally { |
24867 delete deferreds[promise.$$timeoutId]; |
25417 delete deferreds[promise.$$timeoutId]; |
24908 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it |
25458 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it |
24909 // doesn't know about mocked locations and resolves URLs to the real document - which is |
25459 // doesn't know about mocked locations and resolves URLs to the real document - which is |
24910 // exactly the behavior needed here. There is little value is mocking these out for this |
25460 // exactly the behavior needed here. There is little value is mocking these out for this |
24911 // service. |
25461 // service. |
24912 var urlParsingNode = document.createElement("a"); |
25462 var urlParsingNode = document.createElement("a"); |
24913 var originUrl = urlResolve(window.location.href, true); |
25463 var originUrl = urlResolve(window.location.href); |
24914 |
25464 |
24915 |
25465 |
24916 /** |
25466 /** |
24917 * |
25467 * |
24918 * Implementation Notes for non-IE browsers |
25468 * Implementation Notes for non-IE browsers |
24963 * | hostname | The hostname |
25513 * | hostname | The hostname |
24964 * | port | The port, without ":" |
25514 * | port | The port, without ":" |
24965 * | pathname | The pathname, beginning with "/" |
25515 * | pathname | The pathname, beginning with "/" |
24966 * |
25516 * |
24967 */ |
25517 */ |
24968 function urlResolve(url, base) { |
25518 function urlResolve(url) { |
24969 var href = url; |
25519 var href = url; |
24970 |
25520 |
24971 if (msie) { |
25521 if (msie) { |
24972 // Normalize before parse. Refer Implementation Notes on why this is |
25522 // Normalize before parse. Refer Implementation Notes on why this is |
24973 // done in two steps on IE. |
25523 // done in two steps on IE. |
25023 * @example |
25573 * @example |
25024 <example module="windowExample"> |
25574 <example module="windowExample"> |
25025 <file name="index.html"> |
25575 <file name="index.html"> |
25026 <script> |
25576 <script> |
25027 angular.module('windowExample', []) |
25577 angular.module('windowExample', []) |
25028 .controller('ExampleController', ['$scope', '$window', function ($scope, $window) { |
25578 .controller('ExampleController', ['$scope', '$window', function($scope, $window) { |
25029 $scope.greeting = 'Hello, World!'; |
25579 $scope.greeting = 'Hello, World!'; |
25030 $scope.doGreeting = function(greeting) { |
25580 $scope.doGreeting = function(greeting) { |
25031 $window.alert(greeting); |
25581 $window.alert(greeting); |
25032 }; |
25582 }; |
25033 }]); |
25583 }]); |
25044 // element(':button').click(); |
25594 // element(':button').click(); |
25045 }); |
25595 }); |
25046 </file> |
25596 </file> |
25047 </example> |
25597 </example> |
25048 */ |
25598 */ |
25049 function $WindowProvider(){ |
25599 function $WindowProvider() { |
25050 this.$get = valueFn(window); |
25600 this.$get = valueFn(window); |
25051 } |
25601 } |
25052 |
25602 |
25053 /* global currencyFilter: true, |
25603 /* global currencyFilter: true, |
25054 dateFilter: true, |
25604 dateFilter: true, |
25153 * the keys are the filter names and the values are the filter factories. |
25703 * the keys are the filter names and the values are the filter factories. |
25154 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map |
25704 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map |
25155 * of the registered filter instances. |
25705 * of the registered filter instances. |
25156 */ |
25706 */ |
25157 function register(name, factory) { |
25707 function register(name, factory) { |
25158 if(isObject(name)) { |
25708 if (isObject(name)) { |
25159 var filters = {}; |
25709 var filters = {}; |
25160 forEach(name, function(filter, key) { |
25710 forEach(name, function(filter, key) { |
25161 filters[key] = register(key, filter); |
25711 filters[key] = register(key, filter); |
25162 }); |
25712 }); |
25163 return filters; |
25713 return filters; |
25210 * @param {string|Object|function()} expression The predicate to be used for selecting items from |
25760 * @param {string|Object|function()} expression The predicate to be used for selecting items from |
25211 * `array`. |
25761 * `array`. |
25212 * |
25762 * |
25213 * Can be one of: |
25763 * Can be one of: |
25214 * |
25764 * |
25215 * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against |
25765 * - `string`: The string is used for matching against the contents of the `array`. All strings or |
25216 * the contents of the `array`. All strings or objects with string properties in `array` that contain this string |
25766 * objects with string properties in `array` that match this string will be returned. This also |
25217 * will be returned. The predicate can be negated by prefixing the string with `!`. |
25767 * applies to nested object properties. |
25768 * The predicate can be negated by prefixing the string with `!`. |
|
25218 * |
25769 * |
25219 * - `Object`: A pattern object can be used to filter specific properties on objects contained |
25770 * - `Object`: A pattern object can be used to filter specific properties on objects contained |
25220 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items |
25771 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items |
25221 * which have property `name` containing "M" and property `phone` containing "1". A special |
25772 * which have property `name` containing "M" and property `phone` containing "1". A special |
25222 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any |
25773 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any |
25223 * property of the object. That's equivalent to the simple substring match with a `string` |
25774 * property of the object or its nested object properties. That's equivalent to the simple |
25224 * as described above. The predicate can be negated by prefixing the string with `!`. |
25775 * substring match with a `string` as described above. The predicate can be negated by prefixing |
25225 * For Example `{name: "!M"}` predicate will return an array of items which have property `name` |
25776 * the string with `!`. |
25777 * For example `{name: "!M"}` predicate will return an array of items which have property `name` |
|
25226 * not containing "M". |
25778 * not containing "M". |
25779 * |
|
25780 * Note that a named property will match properties on the same level only, while the special |
|
25781 * `$` property will match properties on the same level or deeper. E.g. an array item like |
|
25782 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but |
|
25783 * **will** be matched by `{$: 'John'}`. |
|
25227 * |
25784 * |
25228 * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The |
25785 * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The |
25229 * function is called for each element of `array`. The final result is an array of those |
25786 * function is called for each element of `array`. The final result is an array of those |
25230 * elements that the predicate returned true for. |
25787 * elements that the predicate returned true for. |
25231 * |
25788 * |
25235 * |
25792 * |
25236 * Can be one of: |
25793 * Can be one of: |
25237 * |
25794 * |
25238 * - `function(actual, expected)`: |
25795 * - `function(actual, expected)`: |
25239 * The function will be given the object value and the predicate value to compare and |
25796 * The function will be given the object value and the predicate value to compare and |
25240 * should return true if the item should be included in filtered result. |
25797 * should return true if both values should be considered equal. |
25241 * |
25798 * |
25242 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. |
25799 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. |
25243 * this is essentially strict comparison of expected and actual. |
25800 * This is essentially strict comparison of expected and actual. |
25244 * |
25801 * |
25245 * - `false|undefined`: A short hand for a function which will look for a substring match in case |
25802 * - `false|undefined`: A short hand for a function which will look for a substring match in case |
25246 * insensitive way. |
25803 * insensitive way. |
25247 * |
25804 * |
25248 * @example |
25805 * @example |
25315 */ |
25872 */ |
25316 function filterFilter() { |
25873 function filterFilter() { |
25317 return function(array, expression, comparator) { |
25874 return function(array, expression, comparator) { |
25318 if (!isArray(array)) return array; |
25875 if (!isArray(array)) return array; |
25319 |
25876 |
25320 var comparatorType = typeof(comparator), |
25877 var predicateFn; |
25321 predicates = []; |
25878 var matchAgainstAnyProp; |
25322 |
25879 |
25323 predicates.check = function(value, index) { |
|
25324 for (var j = 0; j < predicates.length; j++) { |
|
25325 if(!predicates[j](value, index)) { |
|
25326 return false; |
|
25327 } |
|
25328 } |
|
25329 return true; |
|
25330 }; |
|
25331 |
|
25332 if (comparatorType !== 'function') { |
|
25333 if (comparatorType === 'boolean' && comparator) { |
|
25334 comparator = function(obj, text) { |
|
25335 return angular.equals(obj, text); |
|
25336 }; |
|
25337 } else { |
|
25338 comparator = function(obj, text) { |
|
25339 if (obj && text && typeof obj === 'object' && typeof text === 'object') { |
|
25340 for (var objKey in obj) { |
|
25341 if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) && |
|
25342 comparator(obj[objKey], text[objKey])) { |
|
25343 return true; |
|
25344 } |
|
25345 } |
|
25346 return false; |
|
25347 } |
|
25348 text = (''+text).toLowerCase(); |
|
25349 return (''+obj).toLowerCase().indexOf(text) > -1; |
|
25350 }; |
|
25351 } |
|
25352 } |
|
25353 |
|
25354 var search = function(obj, text){ |
|
25355 if (typeof text == 'string' && text.charAt(0) === '!') { |
|
25356 return !search(obj, text.substr(1)); |
|
25357 } |
|
25358 switch (typeof obj) { |
|
25359 case "boolean": |
|
25360 case "number": |
|
25361 case "string": |
|
25362 return comparator(obj, text); |
|
25363 case "object": |
|
25364 switch (typeof text) { |
|
25365 case "object": |
|
25366 return comparator(obj, text); |
|
25367 default: |
|
25368 for ( var objKey in obj) { |
|
25369 if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { |
|
25370 return true; |
|
25371 } |
|
25372 } |
|
25373 break; |
|
25374 } |
|
25375 return false; |
|
25376 case "array": |
|
25377 for ( var i = 0; i < obj.length; i++) { |
|
25378 if (search(obj[i], text)) { |
|
25379 return true; |
|
25380 } |
|
25381 } |
|
25382 return false; |
|
25383 default: |
|
25384 return false; |
|
25385 } |
|
25386 }; |
|
25387 switch (typeof expression) { |
25880 switch (typeof expression) { |
25388 case "boolean": |
25881 case 'function': |
25389 case "number": |
25882 predicateFn = expression; |
25390 case "string": |
|
25391 // Set up expression object and fall through |
|
25392 expression = {$:expression}; |
|
25393 // jshint -W086 |
|
25394 case "object": |
|
25395 // jshint +W086 |
|
25396 for (var key in expression) { |
|
25397 (function(path) { |
|
25398 if (typeof expression[path] === 'undefined') return; |
|
25399 predicates.push(function(value) { |
|
25400 return search(path == '$' ? value : (value && value[path]), expression[path]); |
|
25401 }); |
|
25402 })(key); |
|
25403 } |
|
25404 break; |
25883 break; |
25405 case 'function': |
25884 case 'boolean': |
25406 predicates.push(expression); |
25885 case 'number': |
25886 case 'string': |
|
25887 matchAgainstAnyProp = true; |
|
25888 //jshint -W086 |
|
25889 case 'object': |
|
25890 //jshint +W086 |
|
25891 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); |
|
25407 break; |
25892 break; |
25408 default: |
25893 default: |
25409 return array; |
25894 return array; |
25410 } |
25895 } |
25411 var filtered = []; |
25896 |
25412 for ( var j = 0; j < array.length; j++) { |
25897 return array.filter(predicateFn); |
25413 var value = array[j]; |
25898 }; |
25414 if (predicates.check(value, j)) { |
25899 } |
25415 filtered.push(value); |
25900 |
25901 // Helper functions for `filterFilter` |
|
25902 function createPredicateFn(expression, comparator, matchAgainstAnyProp) { |
|
25903 var shouldMatchPrimitives = isObject(expression) && ('$' in expression); |
|
25904 var predicateFn; |
|
25905 |
|
25906 if (comparator === true) { |
|
25907 comparator = equals; |
|
25908 } else if (!isFunction(comparator)) { |
|
25909 comparator = function(actual, expected) { |
|
25910 if (isObject(actual) || isObject(expected)) { |
|
25911 // Prevent an object to be considered equal to a string like `'[object'` |
|
25912 return false; |
|
25416 } |
25913 } |
25417 } |
25914 |
25418 return filtered; |
25915 actual = lowercase('' + actual); |
25916 expected = lowercase('' + expected); |
|
25917 return actual.indexOf(expected) !== -1; |
|
25918 }; |
|
25919 } |
|
25920 |
|
25921 predicateFn = function(item) { |
|
25922 if (shouldMatchPrimitives && !isObject(item)) { |
|
25923 return deepCompare(item, expression.$, comparator, false); |
|
25924 } |
|
25925 return deepCompare(item, expression, comparator, matchAgainstAnyProp); |
|
25419 }; |
25926 }; |
25927 |
|
25928 return predicateFn; |
|
25929 } |
|
25930 |
|
25931 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { |
|
25932 var actualType = typeof actual; |
|
25933 var expectedType = typeof expected; |
|
25934 |
|
25935 if ((expectedType === 'string') && (expected.charAt(0) === '!')) { |
|
25936 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); |
|
25937 } else if (actualType === 'array') { |
|
25938 // In case `actual` is an array, consider it a match |
|
25939 // if ANY of it's items matches `expected` |
|
25940 return actual.some(function(item) { |
|
25941 return deepCompare(item, expected, comparator, matchAgainstAnyProp); |
|
25942 }); |
|
25943 } |
|
25944 |
|
25945 switch (actualType) { |
|
25946 case 'object': |
|
25947 var key; |
|
25948 if (matchAgainstAnyProp) { |
|
25949 for (key in actual) { |
|
25950 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { |
|
25951 return true; |
|
25952 } |
|
25953 } |
|
25954 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); |
|
25955 } else if (expectedType === 'object') { |
|
25956 for (key in expected) { |
|
25957 var expectedVal = expected[key]; |
|
25958 if (isFunction(expectedVal)) { |
|
25959 continue; |
|
25960 } |
|
25961 |
|
25962 var matchAnyProperty = key === '$'; |
|
25963 var actualVal = matchAnyProperty ? actual : actual[key]; |
|
25964 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { |
|
25965 return false; |
|
25966 } |
|
25967 } |
|
25968 return true; |
|
25969 } else { |
|
25970 return comparator(actual, expected); |
|
25971 } |
|
25972 break; |
|
25973 case 'function': |
|
25974 return false; |
|
25975 default: |
|
25976 return comparator(actual, expected); |
|
25977 } |
|
25420 } |
25978 } |
25421 |
25979 |
25422 /** |
25980 /** |
25423 * @ngdoc filter |
25981 * @ngdoc filter |
25424 * @name currency |
25982 * @name currency |
25428 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default |
25986 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default |
25429 * symbol for current locale is used. |
25987 * symbol for current locale is used. |
25430 * |
25988 * |
25431 * @param {number} amount Input to filter. |
25989 * @param {number} amount Input to filter. |
25432 * @param {string=} symbol Currency symbol or identifier to be displayed. |
25990 * @param {string=} symbol Currency symbol or identifier to be displayed. |
25991 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale |
|
25433 * @returns {string} Formatted number. |
25992 * @returns {string} Formatted number. |
25434 * |
25993 * |
25435 * |
25994 * |
25436 * @example |
25995 * @example |
25437 <example module="currencyExample"> |
25996 <example module="currencyExample"> |
25443 }]); |
26002 }]); |
25444 </script> |
26003 </script> |
25445 <div ng-controller="ExampleController"> |
26004 <div ng-controller="ExampleController"> |
25446 <input type="number" ng-model="amount"> <br> |
26005 <input type="number" ng-model="amount"> <br> |
25447 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> |
26006 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> |
25448 custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span> |
26007 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span> |
26008 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span> |
|
25449 </div> |
26009 </div> |
25450 </file> |
26010 </file> |
25451 <file name="protractor.js" type="protractor"> |
26011 <file name="protractor.js" type="protractor"> |
25452 it('should init with 1234.56', function() { |
26012 it('should init with 1234.56', function() { |
25453 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); |
26013 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); |
25454 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56'); |
26014 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); |
26015 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); |
|
25455 }); |
26016 }); |
25456 it('should update', function() { |
26017 it('should update', function() { |
25457 if (browser.params.browser == 'safari') { |
26018 if (browser.params.browser == 'safari') { |
25458 // Safari does not understand the minus key. See |
26019 // Safari does not understand the minus key. See |
25459 // https://github.com/angular/protractor/issues/481 |
26020 // https://github.com/angular/protractor/issues/481 |
25460 return; |
26021 return; |
25461 } |
26022 } |
25462 element(by.model('amount')).clear(); |
26023 element(by.model('amount')).clear(); |
25463 element(by.model('amount')).sendKeys('-1234'); |
26024 element(by.model('amount')).sendKeys('-1234'); |
25464 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); |
26025 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); |
25465 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)'); |
26026 expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)'); |
26027 expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)'); |
|
25466 }); |
26028 }); |
25467 </file> |
26029 </file> |
25468 </example> |
26030 </example> |
25469 */ |
26031 */ |
25470 currencyFilter.$inject = ['$locale']; |
26032 currencyFilter.$inject = ['$locale']; |
25471 function currencyFilter($locale) { |
26033 function currencyFilter($locale) { |
25472 var formats = $locale.NUMBER_FORMATS; |
26034 var formats = $locale.NUMBER_FORMATS; |
25473 return function(amount, currencySymbol){ |
26035 return function(amount, currencySymbol, fractionSize) { |
25474 if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; |
26036 if (isUndefined(currencySymbol)) { |
26037 currencySymbol = formats.CURRENCY_SYM; |
|
26038 } |
|
26039 |
|
26040 if (isUndefined(fractionSize)) { |
|
26041 fractionSize = formats.PATTERNS[1].maxFrac; |
|
26042 } |
|
25475 |
26043 |
25476 // if null or undefined pass it through |
26044 // if null or undefined pass it through |
25477 return (amount == null) |
26045 return (amount == null) |
25478 ? amount |
26046 ? amount |
25479 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). |
26047 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). |
25480 replace(/\u00A4/g, currencySymbol); |
26048 replace(/\u00A4/g, currencySymbol); |
25481 }; |
26049 }; |
25482 } |
26050 } |
25483 |
26051 |
25484 /** |
26052 /** |
25557 |
26125 |
25558 var hasExponent = false; |
26126 var hasExponent = false; |
25559 if (numStr.indexOf('e') !== -1) { |
26127 if (numStr.indexOf('e') !== -1) { |
25560 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); |
26128 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); |
25561 if (match && match[2] == '-' && match[3] > fractionSize + 1) { |
26129 if (match && match[2] == '-' && match[3] > fractionSize + 1) { |
25562 numStr = '0'; |
|
25563 number = 0; |
26130 number = 0; |
25564 } else { |
26131 } else { |
25565 formatedText = numStr; |
26132 formatedText = numStr; |
25566 hasExponent = true; |
26133 hasExponent = true; |
25567 } |
26134 } |
25578 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics |
26145 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics |
25579 // inspired by: |
26146 // inspired by: |
25580 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round |
26147 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round |
25581 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); |
26148 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); |
25582 |
26149 |
25583 if (number === 0) { |
|
25584 isNegative = false; |
|
25585 } |
|
25586 |
|
25587 var fraction = ('' + number).split(DECIMAL_SEP); |
26150 var fraction = ('' + number).split(DECIMAL_SEP); |
25588 var whole = fraction[0]; |
26151 var whole = fraction[0]; |
25589 fraction = fraction[1] || ''; |
26152 fraction = fraction[1] || ''; |
25590 |
26153 |
25591 var i, pos = 0, |
26154 var i, pos = 0, |
25593 group = pattern.gSize; |
26156 group = pattern.gSize; |
25594 |
26157 |
25595 if (whole.length >= (lgroup + group)) { |
26158 if (whole.length >= (lgroup + group)) { |
25596 pos = whole.length - lgroup; |
26159 pos = whole.length - lgroup; |
25597 for (i = 0; i < pos; i++) { |
26160 for (i = 0; i < pos; i++) { |
25598 if ((pos - i)%group === 0 && i !== 0) { |
26161 if ((pos - i) % group === 0 && i !== 0) { |
25599 formatedText += groupSep; |
26162 formatedText += groupSep; |
25600 } |
26163 } |
25601 formatedText += whole.charAt(i); |
26164 formatedText += whole.charAt(i); |
25602 } |
26165 } |
25603 } |
26166 } |
25604 |
26167 |
25605 for (i = pos; i < whole.length; i++) { |
26168 for (i = pos; i < whole.length; i++) { |
25606 if ((whole.length - i)%lgroup === 0 && i !== 0) { |
26169 if ((whole.length - i) % lgroup === 0 && i !== 0) { |
25607 formatedText += groupSep; |
26170 formatedText += groupSep; |
25608 } |
26171 } |
25609 formatedText += whole.charAt(i); |
26172 formatedText += whole.charAt(i); |
25610 } |
26173 } |
25611 |
26174 |
25612 // format fraction part. |
26175 // format fraction part. |
25613 while(fraction.length < fractionSize) { |
26176 while (fraction.length < fractionSize) { |
25614 fraction += '0'; |
26177 fraction += '0'; |
25615 } |
26178 } |
25616 |
26179 |
25617 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); |
26180 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); |
25618 } else { |
26181 } else { |
25619 |
26182 if (fractionSize > 0 && number < 1) { |
25620 if (fractionSize > 0 && number > -1 && number < 1) { |
|
25621 formatedText = number.toFixed(fractionSize); |
26183 formatedText = number.toFixed(fractionSize); |
26184 number = parseFloat(formatedText); |
|
25622 } |
26185 } |
25623 } |
26186 } |
25624 |
26187 |
25625 parts.push(isNegative ? pattern.negPre : pattern.posPre); |
26188 if (number === 0) { |
25626 parts.push(formatedText); |
26189 isNegative = false; |
25627 parts.push(isNegative ? pattern.negSuf : pattern.posSuf); |
26190 } |
26191 |
|
26192 parts.push(isNegative ? pattern.negPre : pattern.posPre, |
|
26193 formatedText, |
|
26194 isNegative ? pattern.negSuf : pattern.posSuf); |
|
25628 return parts.join(''); |
26195 return parts.join(''); |
25629 } |
26196 } |
25630 |
26197 |
25631 function padNumber(num, digits, trim) { |
26198 function padNumber(num, digits, trim) { |
25632 var neg = ''; |
26199 var neg = ''; |
25633 if (num < 0) { |
26200 if (num < 0) { |
25634 neg = '-'; |
26201 neg = '-'; |
25635 num = -num; |
26202 num = -num; |
25636 } |
26203 } |
25637 num = '' + num; |
26204 num = '' + num; |
25638 while(num.length < digits) num = '0' + num; |
26205 while (num.length < digits) num = '0' + num; |
25639 if (trim) |
26206 if (trim) |
25640 num = num.substr(num.length - digits); |
26207 num = num.substr(num.length - digits); |
25641 return neg + num; |
26208 return neg + num; |
25642 } |
26209 } |
25643 |
26210 |
25646 offset = offset || 0; |
26213 offset = offset || 0; |
25647 return function(date) { |
26214 return function(date) { |
25648 var value = date['get' + name](); |
26215 var value = date['get' + name](); |
25649 if (offset > 0 || value > -offset) |
26216 if (offset > 0 || value > -offset) |
25650 value += offset; |
26217 value += offset; |
25651 if (value === 0 && offset == -12 ) value = 12; |
26218 if (value === 0 && offset == -12) value = 12; |
25652 return padNumber(value, size, trim); |
26219 return padNumber(value, size, trim); |
25653 }; |
26220 }; |
25654 } |
26221 } |
25655 |
26222 |
25656 function dateStrGetter(name, shortForm) { |
26223 function dateStrGetter(name, shortForm) { |
25764 * * `'ss'`: Second in minute, padded (00-59) |
26331 * * `'ss'`: Second in minute, padded (00-59) |
25765 * * `'s'`: Second in minute (0-59) |
26332 * * `'s'`: Second in minute (0-59) |
25766 * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) |
26333 * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) |
25767 * * `'a'`: AM/PM marker |
26334 * * `'a'`: AM/PM marker |
25768 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) |
26335 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) |
25769 * * `'ww'`: ISO-8601 week of year (00-53) |
26336 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year |
25770 * * `'w'`: ISO-8601 week of year (0-53) |
26337 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year |
25771 * |
26338 * |
25772 * `format` string can also be one of the following predefined |
26339 * `format` string can also be one of the following predefined |
25773 * {@link guide/i18n localizable formats}: |
26340 * {@link guide/i18n localizable formats}: |
25774 * |
26341 * |
25775 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale |
26342 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale |
25841 if (match[9]) { |
26408 if (match[9]) { |
25842 tzHour = int(match[9] + match[10]); |
26409 tzHour = int(match[9] + match[10]); |
25843 tzMin = int(match[9] + match[11]); |
26410 tzMin = int(match[9] + match[11]); |
25844 } |
26411 } |
25845 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); |
26412 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); |
25846 var h = int(match[4]||0) - tzHour; |
26413 var h = int(match[4] || 0) - tzHour; |
25847 var m = int(match[5]||0) - tzMin; |
26414 var m = int(match[5] || 0) - tzMin; |
25848 var s = int(match[6]||0); |
26415 var s = int(match[6] || 0); |
25849 var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); |
26416 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); |
25850 timeSetter.call(date, h, m, s, ms); |
26417 timeSetter.call(date, h, m, s, ms); |
25851 return date; |
26418 return date; |
25852 } |
26419 } |
25853 return string; |
26420 return string; |
25854 } |
26421 } |
25871 |
26438 |
25872 if (!isDate(date)) { |
26439 if (!isDate(date)) { |
25873 return date; |
26440 return date; |
25874 } |
26441 } |
25875 |
26442 |
25876 while(format) { |
26443 while (format) { |
25877 match = DATE_FORMATS_SPLIT.exec(format); |
26444 match = DATE_FORMATS_SPLIT.exec(format); |
25878 if (match) { |
26445 if (match) { |
25879 parts = concat(parts, match, 1); |
26446 parts = concat(parts, match, 1); |
25880 format = parts.pop(); |
26447 format = parts.pop(); |
25881 } else { |
26448 } else { |
25886 |
26453 |
25887 if (timezone && timezone === 'UTC') { |
26454 if (timezone && timezone === 'UTC') { |
25888 date = new Date(date.getTime()); |
26455 date = new Date(date.getTime()); |
25889 date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); |
26456 date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); |
25890 } |
26457 } |
25891 forEach(parts, function(value){ |
26458 forEach(parts, function(value) { |
25892 fn = DATE_FORMATS[value]; |
26459 fn = DATE_FORMATS[value]; |
25893 text += fn ? fn(date, $locale.DATETIME_FORMATS) |
26460 text += fn ? fn(date, $locale.DATETIME_FORMATS) |
25894 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); |
26461 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); |
25895 }); |
26462 }); |
25896 |
26463 |
25909 * |
26476 * |
25910 * This filter is mostly useful for debugging. When using the double curly {{value}} notation |
26477 * This filter is mostly useful for debugging. When using the double curly {{value}} notation |
25911 * the binding is automatically converted to JSON. |
26478 * the binding is automatically converted to JSON. |
25912 * |
26479 * |
25913 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. |
26480 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. |
26481 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. |
|
25914 * @returns {string} JSON string. |
26482 * @returns {string} JSON string. |
25915 * |
26483 * |
25916 * |
26484 * |
25917 * @example |
26485 * @example |
25918 <example> |
26486 <example> |
25919 <file name="index.html"> |
26487 <file name="index.html"> |
25920 <pre>{{ {'name':'value'} | json }}</pre> |
26488 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre> |
26489 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre> |
|
25921 </file> |
26490 </file> |
25922 <file name="protractor.js" type="protractor"> |
26491 <file name="protractor.js" type="protractor"> |
25923 it('should jsonify filtered objects', function() { |
26492 it('should jsonify filtered objects', function() { |
25924 expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/); |
26493 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); |
26494 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); |
|
25925 }); |
26495 }); |
25926 </file> |
26496 </file> |
25927 </example> |
26497 </example> |
25928 * |
26498 * |
25929 */ |
26499 */ |
25930 function jsonFilter() { |
26500 function jsonFilter() { |
25931 return function(object) { |
26501 return function(object, spacing) { |
25932 return toJson(object, true); |
26502 if (isUndefined(spacing)) { |
26503 spacing = 2; |
|
26504 } |
|
26505 return toJson(object, spacing); |
|
25933 }; |
26506 }; |
25934 } |
26507 } |
25935 |
26508 |
25936 |
26509 |
25937 /** |
26510 /** |
25991 <div ng-controller="ExampleController"> |
26564 <div ng-controller="ExampleController"> |
25992 Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit"> |
26565 Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit"> |
25993 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> |
26566 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> |
25994 Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit"> |
26567 Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit"> |
25995 <p>Output letters: {{ letters | limitTo:letterLimit }}</p> |
26568 <p>Output letters: {{ letters | limitTo:letterLimit }}</p> |
25996 Limit {{longNumber}} to: <input type="integer" ng-model="longNumberLimit"> |
26569 Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit"> |
25997 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p> |
26570 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p> |
25998 </div> |
26571 </div> |
25999 </file> |
26572 </file> |
26000 <file name="protractor.js" type="protractor"> |
26573 <file name="protractor.js" type="protractor"> |
26001 var numLimitInput = element(by.model('numLimit')); |
26574 var numLimitInput = element(by.model('numLimit')); |
26039 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); |
26612 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); |
26040 }); |
26613 }); |
26041 </file> |
26614 </file> |
26042 </example> |
26615 </example> |
26043 */ |
26616 */ |
26044 function limitToFilter(){ |
26617 function limitToFilter() { |
26045 return function(input, limit) { |
26618 return function(input, limit) { |
26046 if (isNumber(input)) input = input.toString(); |
26619 if (isNumber(input)) input = input.toString(); |
26047 if (!isArray(input) && !isString(input)) return input; |
26620 if (!isArray(input) && !isString(input)) return input; |
26048 |
26621 |
26049 if (Math.abs(Number(limit)) === Infinity) { |
26622 if (Math.abs(Number(limit)) === Infinity) { |
26059 } else { |
26632 } else { |
26060 return ""; |
26633 return ""; |
26061 } |
26634 } |
26062 } |
26635 } |
26063 |
26636 |
26064 var out = [], |
26637 var i, n; |
26065 i, n; |
|
26066 |
26638 |
26067 // if abs(limit) exceeds maximum length, trim it |
26639 // if abs(limit) exceeds maximum length, trim it |
26068 if (limit > input.length) |
26640 if (limit > input.length) |
26069 limit = input.length; |
26641 limit = input.length; |
26070 else if (limit < -input.length) |
26642 else if (limit < -input.length) |
26072 |
26644 |
26073 if (limit > 0) { |
26645 if (limit > 0) { |
26074 i = 0; |
26646 i = 0; |
26075 n = limit; |
26647 n = limit; |
26076 } else { |
26648 } else { |
26649 // zero and NaN check on limit - return empty array |
|
26650 if (!limit) return []; |
|
26651 |
|
26077 i = input.length + limit; |
26652 i = input.length + limit; |
26078 n = input.length; |
26653 n = input.length; |
26079 } |
26654 } |
26080 |
26655 |
26081 for (; i<n; i++) { |
26656 return input.slice(i, n); |
26082 out.push(input[i]); |
|
26083 } |
|
26084 |
|
26085 return out; |
|
26086 }; |
26657 }; |
26087 } |
26658 } |
26088 |
26659 |
26089 /** |
26660 /** |
26090 * @ngdoc filter |
26661 * @ngdoc filter |
26200 }]); |
26771 }]); |
26201 </file> |
26772 </file> |
26202 </example> |
26773 </example> |
26203 */ |
26774 */ |
26204 orderByFilter.$inject = ['$parse']; |
26775 orderByFilter.$inject = ['$parse']; |
26205 function orderByFilter($parse){ |
26776 function orderByFilter($parse) { |
26206 return function(array, sortPredicate, reverseOrder) { |
26777 return function(array, sortPredicate, reverseOrder) { |
26207 if (!(isArrayLike(array))) return array; |
26778 if (!(isArrayLike(array))) return array; |
26208 sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; |
26779 sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate]; |
26209 if (sortPredicate.length === 0) { sortPredicate = ['+']; } |
26780 if (sortPredicate.length === 0) { sortPredicate = ['+']; } |
26210 sortPredicate = sortPredicate.map(function(predicate){ |
26781 sortPredicate = sortPredicate.map(function(predicate) { |
26211 var descending = false, get = predicate || identity; |
26782 var descending = false, get = predicate || identity; |
26212 if (isString(predicate)) { |
26783 if (isString(predicate)) { |
26213 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { |
26784 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { |
26214 descending = predicate.charAt(0) == '-'; |
26785 descending = predicate.charAt(0) == '-'; |
26215 predicate = predicate.substring(1); |
26786 predicate = predicate.substring(1); |
26216 } |
26787 } |
26217 if ( predicate === '' ) { |
26788 if (predicate === '') { |
26218 // Effectively no predicate was passed so we compare identity |
26789 // Effectively no predicate was passed so we compare identity |
26219 return reverseComparator(function(a,b) { |
26790 return reverseComparator(compare, descending); |
26220 return compare(a, b); |
|
26221 }, descending); |
|
26222 } |
26791 } |
26223 get = $parse(predicate); |
26792 get = $parse(predicate); |
26224 if (get.constant) { |
26793 if (get.constant) { |
26225 var key = get(); |
26794 var key = get(); |
26226 return reverseComparator(function(a,b) { |
26795 return reverseComparator(function(a, b) { |
26227 return compare(a[key], b[key]); |
26796 return compare(a[key], b[key]); |
26228 }, descending); |
26797 }, descending); |
26229 } |
26798 } |
26230 } |
26799 } |
26231 return reverseComparator(function(a,b){ |
26800 return reverseComparator(function(a, b) { |
26232 return compare(get(a),get(b)); |
26801 return compare(get(a),get(b)); |
26233 }, descending); |
26802 }, descending); |
26234 }); |
26803 }); |
26235 var arrayCopy = []; |
26804 return slice.call(array).sort(reverseComparator(comparator, reverseOrder)); |
26236 for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } |
26805 |
26237 return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); |
26806 function comparator(o1, o2) { |
26238 |
26807 for (var i = 0; i < sortPredicate.length; i++) { |
26239 function comparator(o1, o2){ |
|
26240 for ( var i = 0; i < sortPredicate.length; i++) { |
|
26241 var comp = sortPredicate[i](o1, o2); |
26808 var comp = sortPredicate[i](o1, o2); |
26242 if (comp !== 0) return comp; |
26809 if (comp !== 0) return comp; |
26243 } |
26810 } |
26244 return 0; |
26811 return 0; |
26245 } |
26812 } |
26246 function reverseComparator(comp, descending) { |
26813 function reverseComparator(comp, descending) { |
26247 return descending |
26814 return descending |
26248 ? function(a,b){return comp(b,a);} |
26815 ? function(a, b) {return comp(b,a);} |
26249 : comp; |
26816 : comp; |
26250 } |
26817 } |
26251 function compare(v1, v2){ |
26818 |
26819 function isPrimitive(value) { |
|
26820 switch (typeof value) { |
|
26821 case 'number': /* falls through */ |
|
26822 case 'boolean': /* falls through */ |
|
26823 case 'string': |
|
26824 return true; |
|
26825 default: |
|
26826 return false; |
|
26827 } |
|
26828 } |
|
26829 |
|
26830 function objectToString(value) { |
|
26831 if (value === null) return 'null'; |
|
26832 if (typeof value.valueOf === 'function') { |
|
26833 value = value.valueOf(); |
|
26834 if (isPrimitive(value)) return value; |
|
26835 } |
|
26836 if (typeof value.toString === 'function') { |
|
26837 value = value.toString(); |
|
26838 if (isPrimitive(value)) return value; |
|
26839 } |
|
26840 return ''; |
|
26841 } |
|
26842 |
|
26843 function compare(v1, v2) { |
|
26252 var t1 = typeof v1; |
26844 var t1 = typeof v1; |
26253 var t2 = typeof v2; |
26845 var t2 = typeof v2; |
26254 if (t1 == t2) { |
26846 if (t1 === t2 && t1 === "object") { |
26255 if (isDate(v1) && isDate(v2)) { |
26847 v1 = objectToString(v1); |
26256 v1 = v1.valueOf(); |
26848 v2 = objectToString(v2); |
26257 v2 = v2.valueOf(); |
26849 } |
26258 } |
26850 if (t1 === t2) { |
26259 if (t1 == "string") { |
26851 if (t1 === "string") { |
26260 v1 = v1.toLowerCase(); |
26852 v1 = v1.toLowerCase(); |
26261 v2 = v2.toLowerCase(); |
26853 v2 = v2.toLowerCase(); |
26262 } |
26854 } |
26263 if (v1 === v2) return 0; |
26855 if (v1 === v2) return 0; |
26264 return v1 < v2 ? -1 : 1; |
26856 return v1 < v2 ? -1 : 1; |
26298 if (!attr.href && !attr.xlinkHref && !attr.name) { |
26890 if (!attr.href && !attr.xlinkHref && !attr.name) { |
26299 return function(scope, element) { |
26891 return function(scope, element) { |
26300 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. |
26892 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. |
26301 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? |
26893 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? |
26302 'xlink:href' : 'href'; |
26894 'xlink:href' : 'href'; |
26303 element.on('click', function(event){ |
26895 element.on('click', function(event) { |
26304 // if we have no href url, then don't navigate anywhere. |
26896 // if we have no href url, then don't navigate anywhere. |
26305 if (!element.attr(href)) { |
26897 if (!element.attr(href)) { |
26306 event.preventDefault(); |
26898 event.preventDefault(); |
26307 } |
26899 } |
26308 }); |
26900 }); |
26320 * @description |
26912 * @description |
26321 * Using Angular markup like `{{hash}}` in an href attribute will |
26913 * Using Angular markup like `{{hash}}` in an href attribute will |
26322 * make the link go to the wrong URL if the user clicks it before |
26914 * make the link go to the wrong URL if the user clicks it before |
26323 * Angular has a chance to replace the `{{hash}}` markup with its |
26915 * Angular has a chance to replace the `{{hash}}` markup with its |
26324 * value. Until Angular replaces the markup the link will be broken |
26916 * value. Until Angular replaces the markup the link will be broken |
26325 * and will most likely return a 404 error. |
26917 * and will most likely return a 404 error. The `ngHref` directive |
26326 * |
26918 * solves this problem. |
26327 * The `ngHref` directive solves this problem. |
|
26328 * |
26919 * |
26329 * The wrong way to write it: |
26920 * The wrong way to write it: |
26330 * ```html |
26921 * ```html |
26331 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a> |
26922 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a> |
26332 * ``` |
26923 * ``` |
26777 * - `minlength` |
27368 * - `minlength` |
26778 * - `number` |
27369 * - `number` |
26779 * - `pattern` |
27370 * - `pattern` |
26780 * - `required` |
27371 * - `required` |
26781 * - `url` |
27372 * - `url` |
27373 * - `date` |
|
27374 * - `datetimelocal` |
|
27375 * - `time` |
|
27376 * - `week` |
|
27377 * - `month` |
|
26782 * |
27378 * |
26783 * @description |
27379 * @description |
26784 * `FormController` keeps track of all its controls and nested forms as well as the state of them, |
27380 * `FormController` keeps track of all its controls and nested forms as well as the state of them, |
26785 * such as being valid/invalid or dirty/pristine. |
27381 * such as being valid/invalid or dirty/pristine. |
26786 * |
27382 * |
26965 * in this form. |
27561 * in this form. |
26966 * |
27562 * |
26967 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after |
27563 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after |
26968 * saving or resetting it. |
27564 * saving or resetting it. |
26969 */ |
27565 */ |
26970 form.$setPristine = function () { |
27566 form.$setPristine = function() { |
26971 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); |
27567 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); |
26972 form.$dirty = false; |
27568 form.$dirty = false; |
26973 form.$pristine = true; |
27569 form.$pristine = true; |
26974 form.$submitted = false; |
27570 form.$submitted = false; |
26975 forEach(controls, function(control) { |
27571 forEach(controls, function(control) { |
26988 * untouched state (ng-untouched class). |
27584 * untouched state (ng-untouched class). |
26989 * |
27585 * |
26990 * Setting a form controls back to their untouched state is often useful when setting the form |
27586 * Setting a form controls back to their untouched state is often useful when setting the form |
26991 * back to its pristine state. |
27587 * back to its pristine state. |
26992 */ |
27588 */ |
26993 form.$setUntouched = function () { |
27589 form.$setUntouched = function() { |
26994 forEach(controls, function(control) { |
27590 forEach(controls, function(control) { |
26995 control.$setUntouched(); |
27591 control.$setUntouched(); |
26996 }); |
27592 }); |
26997 }; |
27593 }; |
26998 |
27594 |
27001 * @name form.FormController#$setSubmitted |
27597 * @name form.FormController#$setSubmitted |
27002 * |
27598 * |
27003 * @description |
27599 * @description |
27004 * Sets the form to its submitted state. |
27600 * Sets the form to its submitted state. |
27005 */ |
27601 */ |
27006 form.$setSubmitted = function () { |
27602 form.$setSubmitted = function() { |
27007 $animate.addClass(element, SUBMITTED_CLASS); |
27603 $animate.addClass(element, SUBMITTED_CLASS); |
27008 form.$submitted = true; |
27604 form.$submitted = true; |
27009 parentForm.$setSubmitted(); |
27605 parentForm.$setSubmitted(); |
27010 }; |
27606 }; |
27011 } |
27607 } |
27199 scope.$apply(function() { |
27795 scope.$apply(function() { |
27200 controller.$commitViewValue(); |
27796 controller.$commitViewValue(); |
27201 controller.$setSubmitted(); |
27797 controller.$setSubmitted(); |
27202 }); |
27798 }); |
27203 |
27799 |
27204 event.preventDefault |
27800 event.preventDefault(); |
27205 ? event.preventDefault() |
|
27206 : event.returnValue = false; // IE |
|
27207 }; |
27801 }; |
27208 |
27802 |
27209 addEventListenerFn(formElement[0], 'submit', handleFormSubmission); |
27803 addEventListenerFn(formElement[0], 'submit', handleFormSubmission); |
27210 |
27804 |
27211 // unregister the preventDefault listener so that we don't not leak memory but in a |
27805 // unregister the preventDefault listener so that we don't not leak memory but in a |
27228 alias = newValue; |
27822 alias = newValue; |
27229 setter(scope, alias, controller, alias); |
27823 setter(scope, alias, controller, alias); |
27230 parentFormCtrl.$$renameControl(controller, alias); |
27824 parentFormCtrl.$$renameControl(controller, alias); |
27231 }); |
27825 }); |
27232 } |
27826 } |
27233 if (parentFormCtrl !== nullFormCtrl) { |
27827 formElement.on('$destroy', function() { |
27234 formElement.on('$destroy', function() { |
27828 parentFormCtrl.$removeControl(controller); |
27235 parentFormCtrl.$removeControl(controller); |
27829 if (alias) { |
27236 if (alias) { |
27830 setter(scope, alias, undefined, alias); |
27237 setter(scope, alias, undefined, alias); |
27831 } |
27238 } |
27832 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards |
27239 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards |
27833 }); |
27240 }); |
|
27241 } |
|
27242 } |
27834 } |
27243 }; |
27835 }; |
27244 } |
27836 } |
27245 }; |
27837 }; |
27246 |
27838 |
27280 * @name input[text] |
27872 * @name input[text] |
27281 * |
27873 * |
27282 * @description |
27874 * @description |
27283 * Standard HTML text input with angular data binding, inherited by most of the `input` elements. |
27875 * Standard HTML text input with angular data binding, inherited by most of the `input` elements. |
27284 * |
27876 * |
27285 * *NOTE* Not every feature offered is available for all input types. |
|
27286 * |
27877 * |
27287 * @param {string} ngModel Assignable angular expression to data-bind to. |
27878 * @param {string} ngModel Assignable angular expression to data-bind to. |
27288 * @param {string=} name Property name of the form under which the control is published. |
27879 * @param {string=} name Property name of the form under which the control is published. |
27289 * @param {string=} required Adds `required` validation error key if the value is not entered. |
27880 * @param {string=} required Adds `required` validation error key if the value is not entered. |
27290 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
27881 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
27291 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
27882 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
27292 * `required` when you want to data-bind to the `required` attribute. |
27883 * `required` when you want to data-bind to the `required` attribute. |
27293 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
27884 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
27294 * minlength. |
27885 * minlength. |
27295 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
27886 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
27296 * maxlength. |
27887 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of |
27297 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
27888 * any length. |
27298 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
27889 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string |
27299 * patterns defined as scope expressions. |
27890 * that contains the regular expression body that will be converted to a regular expression |
27891 * as in the ngPattern directive. |
|
27892 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match |
|
27893 * a RegExp found by evaluating the Angular expression given in the attribute value. |
|
27894 * If the expression evaluates to a RegExp object then this is used directly. |
|
27895 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` |
|
27896 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. |
|
27300 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
27897 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
27301 * interaction with the input element. |
27898 * interaction with the input element. |
27302 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. |
27899 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. |
27303 * This parameter is ignored for input[type=password] controls, which will never trim the |
27900 * This parameter is ignored for input[type=password] controls, which will never trim the |
27304 * input. |
27901 * input. |
27364 * @description |
27961 * @description |
27365 * Input with date validation and transformation. In browsers that do not yet support |
27962 * Input with date validation and transformation. In browsers that do not yet support |
27366 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 |
27963 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 |
27367 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many |
27964 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many |
27368 * modern browsers do not yet support this input type, it is important to provide cues to users on the |
27965 * modern browsers do not yet support this input type, it is important to provide cues to users on the |
27369 * expected input format via a placeholder or label. The model must always be a Date object. |
27966 * expected input format via a placeholder or label. |
27967 * |
|
27968 * The model must always be a Date object, otherwise Angular will throw an error. |
|
27969 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. |
|
27370 * |
27970 * |
27371 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27971 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27372 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27972 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27373 * |
27973 * |
27374 * @param {string} ngModel Assignable angular expression to data-bind to. |
27974 * @param {string} ngModel Assignable angular expression to data-bind to. |
27447 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), |
28047 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), |
27448 'yyyy-MM-dd'), |
28048 'yyyy-MM-dd'), |
27449 |
28049 |
27450 /** |
28050 /** |
27451 * @ngdoc input |
28051 * @ngdoc input |
27452 * @name input[dateTimeLocal] |
28052 * @name input[datetime-local] |
27453 * |
28053 * |
27454 * @description |
28054 * @description |
27455 * Input with datetime validation and transformation. In browsers that do not yet support |
28055 * Input with datetime validation and transformation. In browsers that do not yet support |
27456 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
28056 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
27457 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. The model must be a Date object. |
28057 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. |
28058 * |
|
28059 * The model must always be a Date object, otherwise Angular will throw an error. |
|
28060 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. |
|
27458 * |
28061 * |
27459 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
28062 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27460 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
28063 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27461 * |
28064 * |
27462 * @param {string} ngModel Assignable angular expression to data-bind to. |
28065 * @param {string} ngModel Assignable angular expression to data-bind to. |
27543 * Input with time validation and transformation. In browsers that do not yet support |
28146 * Input with time validation and transformation. In browsers that do not yet support |
27544 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
28147 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
27545 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a |
28148 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a |
27546 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. |
28149 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. |
27547 * |
28150 * |
28151 * The model must always be a Date object, otherwise Angular will throw an error. |
|
28152 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. |
|
28153 * |
|
27548 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
28154 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27549 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
28155 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27550 * |
28156 * |
27551 * @param {string} ngModel Assignable angular expression to data-bind to. |
28157 * @param {string} ngModel Assignable angular expression to data-bind to. |
27552 * @param {string=} name Property name of the form under which the control is published. |
28158 * @param {string=} name Property name of the form under which the control is published. |
27629 * @name input[week] |
28235 * @name input[week] |
27630 * |
28236 * |
27631 * @description |
28237 * @description |
27632 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support |
28238 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support |
27633 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
28239 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
27634 * week format (yyyy-W##), for example: `2013-W02`. The model must always be a Date object. |
28240 * week format (yyyy-W##), for example: `2013-W02`. |
28241 * |
|
28242 * The model must always be a Date object, otherwise Angular will throw an error. |
|
28243 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. |
|
27635 * |
28244 * |
27636 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
28245 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27637 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
28246 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27638 * |
28247 * |
27639 * @param {string} ngModel Assignable angular expression to data-bind to. |
28248 * @param {string} ngModel Assignable angular expression to data-bind to. |
27715 * @name input[month] |
28324 * @name input[month] |
27716 * |
28325 * |
27717 * @description |
28326 * @description |
27718 * Input with month validation and transformation. In browsers that do not yet support |
28327 * Input with month validation and transformation. In browsers that do not yet support |
27719 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
28328 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 |
27720 * month format (yyyy-MM), for example: `2009-01`. The model must always be a Date object. In the event the model is |
28329 * month format (yyyy-MM), for example: `2009-01`. |
27721 * not set to the first of the month, the first of that model's month is assumed. |
28330 * |
28331 * The model must always be a Date object, otherwise Angular will throw an error. |
|
28332 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. |
|
28333 * If the model is not set to the first of the month, the next view to model update will set it |
|
28334 * to the first of the month. |
|
27722 * |
28335 * |
27723 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
28336 * The timezone to be used to read/write the `Date` instance in the model can be defined using |
27724 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
28337 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. |
27725 * |
28338 * |
27726 * @param {string} ngModel Assignable angular expression to data-bind to. |
28339 * @param {string} ngModel Assignable angular expression to data-bind to. |
27805 * |
28418 * |
27806 * @description |
28419 * @description |
27807 * Text input with number validation and transformation. Sets the `number` validation |
28420 * Text input with number validation and transformation. Sets the `number` validation |
27808 * error if not a valid number. |
28421 * error if not a valid number. |
27809 * |
28422 * |
28423 * The model must always be a number, otherwise Angular will throw an error. |
|
28424 * |
|
27810 * @param {string} ngModel Assignable angular expression to data-bind to. |
28425 * @param {string} ngModel Assignable angular expression to data-bind to. |
27811 * @param {string=} name Property name of the form under which the control is published. |
28426 * @param {string=} name Property name of the form under which the control is published. |
27812 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. |
28427 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. |
27813 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. |
28428 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. |
27814 * @param {string=} required Sets `required` validation error key if the value is not entered. |
28429 * @param {string=} required Sets `required` validation error key if the value is not entered. |
27816 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
28431 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
27817 * `required` when you want to data-bind to the `required` attribute. |
28432 * `required` when you want to data-bind to the `required` attribute. |
27818 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
28433 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
27819 * minlength. |
28434 * minlength. |
27820 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
28435 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
27821 * maxlength. |
28436 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of |
27822 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
28437 * any length. |
27823 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
28438 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string |
27824 * patterns defined as scope expressions. |
28439 * that contains the regular expression body that will be converted to a regular expression |
28440 * as in the ngPattern directive. |
|
28441 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match |
|
28442 * a RegExp found by evaluating the Angular expression given in the attribute value. |
|
28443 * If the expression evaluates to a RegExp object then this is used directly. |
|
28444 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` |
|
28445 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. |
|
27825 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
28446 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
27826 * interaction with the input element. |
28447 * interaction with the input element. |
27827 * |
28448 * |
27828 * @example |
28449 * @example |
27829 <example name="number-input-directive" module="numberExample"> |
28450 <example name="number-input-directive" module="numberExample"> |
27883 * |
28504 * |
27884 * @description |
28505 * @description |
27885 * Text input with URL validation. Sets the `url` validation error key if the content is not a |
28506 * Text input with URL validation. Sets the `url` validation error key if the content is not a |
27886 * valid URL. |
28507 * valid URL. |
27887 * |
28508 * |
28509 * <div class="alert alert-warning"> |
|
28510 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex |
|
28511 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify |
|
28512 * the built-in validators (see the {@link guide/forms Forms guide}) |
|
28513 * </div> |
|
28514 * |
|
27888 * @param {string} ngModel Assignable angular expression to data-bind to. |
28515 * @param {string} ngModel Assignable angular expression to data-bind to. |
27889 * @param {string=} name Property name of the form under which the control is published. |
28516 * @param {string=} name Property name of the form under which the control is published. |
27890 * @param {string=} required Sets `required` validation error key if the value is not entered. |
28517 * @param {string=} required Sets `required` validation error key if the value is not entered. |
27891 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
28518 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
27892 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
28519 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
27893 * `required` when you want to data-bind to the `required` attribute. |
28520 * `required` when you want to data-bind to the `required` attribute. |
27894 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
28521 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
27895 * minlength. |
28522 * minlength. |
27896 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
28523 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
27897 * maxlength. |
28524 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of |
27898 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
28525 * any length. |
27899 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
28526 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string |
27900 * patterns defined as scope expressions. |
28527 * that contains the regular expression body that will be converted to a regular expression |
28528 * as in the ngPattern directive. |
|
28529 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match |
|
28530 * a RegExp found by evaluating the Angular expression given in the attribute value. |
|
28531 * If the expression evaluates to a RegExp object then this is used directly. |
|
28532 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` |
|
28533 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. |
|
27901 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
28534 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
27902 * interaction with the input element. |
28535 * interaction with the input element. |
27903 * |
28536 * |
27904 * @example |
28537 * @example |
27905 <example name="url-input-directive" module="urlExample"> |
28538 <example name="url-input-directive" module="urlExample"> |
27960 * |
28593 * |
27961 * @description |
28594 * @description |
27962 * Text input with email validation. Sets the `email` validation error key if not a valid email |
28595 * Text input with email validation. Sets the `email` validation error key if not a valid email |
27963 * address. |
28596 * address. |
27964 * |
28597 * |
28598 * <div class="alert alert-warning"> |
|
28599 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex |
|
28600 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can |
|
28601 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) |
|
28602 * </div> |
|
28603 * |
|
27965 * @param {string} ngModel Assignable angular expression to data-bind to. |
28604 * @param {string} ngModel Assignable angular expression to data-bind to. |
27966 * @param {string=} name Property name of the form under which the control is published. |
28605 * @param {string=} name Property name of the form under which the control is published. |
27967 * @param {string=} required Sets `required` validation error key if the value is not entered. |
28606 * @param {string=} required Sets `required` validation error key if the value is not entered. |
27968 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
28607 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
27969 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
28608 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
27970 * `required` when you want to data-bind to the `required` attribute. |
28609 * `required` when you want to data-bind to the `required` attribute. |
27971 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
28610 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
27972 * minlength. |
28611 * minlength. |
27973 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
28612 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
27974 * maxlength. |
28613 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of |
27975 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
28614 * any length. |
27976 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
28615 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string |
27977 * patterns defined as scope expressions. |
28616 * that contains the regular expression body that will be converted to a regular expression |
28617 * as in the ngPattern directive. |
|
28618 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match |
|
28619 * a RegExp found by evaluating the Angular expression given in the attribute value. |
|
28620 * If the expression evaluates to a RegExp object then this is used directly. |
|
28621 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` |
|
28622 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. |
|
27978 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
28623 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
27979 * interaction with the input element. |
28624 * interaction with the input element. |
27980 * |
28625 * |
27981 * @example |
28626 * @example |
27982 <example name="email-input-directive" module="emailExample"> |
28627 <example name="email-input-directive" module="emailExample"> |
28138 'submit': noop, |
28783 'submit': noop, |
28139 'reset': noop, |
28784 'reset': noop, |
28140 'file': noop |
28785 'file': noop |
28141 }; |
28786 }; |
28142 |
28787 |
28143 function testFlags(validity, flags) { |
|
28144 var i, flag; |
|
28145 if (flags) { |
|
28146 for (i=0; i<flags.length; ++i) { |
|
28147 flag = flags[i]; |
|
28148 if (validity[flag]) { |
|
28149 return true; |
|
28150 } |
|
28151 } |
|
28152 } |
|
28153 return false; |
|
28154 } |
|
28155 |
|
28156 function stringBasedInputType(ctrl) { |
28788 function stringBasedInputType(ctrl) { |
28157 ctrl.$formatters.push(function(value) { |
28789 ctrl.$formatters.push(function(value) { |
28158 return ctrl.$isEmpty(value) ? value : value.toString(); |
28790 return ctrl.$isEmpty(value) ? value : value.toString(); |
28159 }); |
28791 }); |
28160 } |
28792 } |
28163 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
28795 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
28164 stringBasedInputType(ctrl); |
28796 stringBasedInputType(ctrl); |
28165 } |
28797 } |
28166 |
28798 |
28167 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { |
28799 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { |
28168 var validity = element.prop(VALIDITY_STATE_PROPERTY); |
|
28169 var placeholder = element[0].placeholder, noevent = {}; |
|
28170 var type = lowercase(element[0].type); |
28800 var type = lowercase(element[0].type); |
28171 |
28801 |
28172 // In composition mode, users are still inputing intermediate text buffer, |
28802 // In composition mode, users are still inputing intermediate text buffer, |
28173 // hold the listener until composition is done. |
28803 // hold the listener until composition is done. |
28174 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent |
28804 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent |
28184 listener(); |
28814 listener(); |
28185 }); |
28815 }); |
28186 } |
28816 } |
28187 |
28817 |
28188 var listener = function(ev) { |
28818 var listener = function(ev) { |
28819 if (timeout) { |
|
28820 $browser.defer.cancel(timeout); |
|
28821 timeout = null; |
|
28822 } |
|
28189 if (composing) return; |
28823 if (composing) return; |
28190 var value = element.val(), |
28824 var value = element.val(), |
28191 event = ev && ev.type; |
28825 event = ev && ev.type; |
28192 |
|
28193 // IE (11 and under) seem to emit an 'input' event if the placeholder value changes. |
|
28194 // We don't want to dirty the value when this happens, so we abort here. Unfortunately, |
|
28195 // IE also sends input events for other non-input-related things, (such as focusing on a |
|
28196 // form control), so this change is not entirely enough to solve this. |
|
28197 if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) { |
|
28198 placeholder = element[0].placeholder; |
|
28199 return; |
|
28200 } |
|
28201 |
28826 |
28202 // By default we will trim the value |
28827 // By default we will trim the value |
28203 // If the attribute ng-trim exists we will avoid trimming |
28828 // If the attribute ng-trim exists we will avoid trimming |
28204 // If input type is 'password', the value is never trimmed |
28829 // If input type is 'password', the value is never trimmed |
28205 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { |
28830 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { |
28219 if ($sniffer.hasEvent('input')) { |
28844 if ($sniffer.hasEvent('input')) { |
28220 element.on('input', listener); |
28845 element.on('input', listener); |
28221 } else { |
28846 } else { |
28222 var timeout; |
28847 var timeout; |
28223 |
28848 |
28224 var deferListener = function(ev) { |
28849 var deferListener = function(ev, input, origValue) { |
28225 if (!timeout) { |
28850 if (!timeout) { |
28226 timeout = $browser.defer(function() { |
28851 timeout = $browser.defer(function() { |
28227 listener(ev); |
|
28228 timeout = null; |
28852 timeout = null; |
28853 if (!input || input.value !== origValue) { |
|
28854 listener(ev); |
|
28855 } |
|
28229 }); |
28856 }); |
28230 } |
28857 } |
28231 }; |
28858 }; |
28232 |
28859 |
28233 element.on('keydown', function(event) { |
28860 element.on('keydown', function(event) { |
28235 |
28862 |
28236 // ignore |
28863 // ignore |
28237 // command modifiers arrows |
28864 // command modifiers arrows |
28238 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; |
28865 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; |
28239 |
28866 |
28240 deferListener(event); |
28867 deferListener(event, this, this.value); |
28241 }); |
28868 }); |
28242 |
28869 |
28243 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it |
28870 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it |
28244 if ($sniffer.hasEvent('paste')) { |
28871 if ($sniffer.hasEvent('paste')) { |
28245 element.on('paste cut', deferListener); |
28872 element.on('paste cut', deferListener); |
28249 // if user paste into input using mouse on older browser |
28876 // if user paste into input using mouse on older browser |
28250 // or form autocomplete on newer browser, we need "change" event to catch it |
28877 // or form autocomplete on newer browser, we need "change" event to catch it |
28251 element.on('change', listener); |
28878 element.on('change', listener); |
28252 |
28879 |
28253 ctrl.$render = function() { |
28880 ctrl.$render = function() { |
28254 element.val(ctrl.$isEmpty(ctrl.$modelValue) ? '' : ctrl.$viewValue); |
28881 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); |
28255 }; |
28882 }; |
28256 } |
28883 } |
28257 |
28884 |
28258 function weekParser(isoWeek, existingDate) { |
28885 function weekParser(isoWeek, existingDate) { |
28259 if (isDate(isoWeek)) { |
28886 if (isDate(isoWeek)) { |
28297 |
28924 |
28298 if (isString(iso)) { |
28925 if (isString(iso)) { |
28299 // When a date is JSON'ified to wraps itself inside of an extra |
28926 // When a date is JSON'ified to wraps itself inside of an extra |
28300 // set of double quotes. This makes the date parsing code unable |
28927 // set of double quotes. This makes the date parsing code unable |
28301 // to match the date string and parse it as a date. |
28928 // to match the date string and parse it as a date. |
28302 if (iso.charAt(0) == '"' && iso.charAt(iso.length-1) == '"') { |
28929 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') { |
28303 iso = iso.substring(1, iso.length-1); |
28930 iso = iso.substring(1, iso.length - 1); |
28304 } |
28931 } |
28305 if (ISO_DATE_REGEXP.test(iso)) { |
28932 if (ISO_DATE_REGEXP.test(iso)) { |
28306 return new Date(iso); |
28933 return new Date(iso); |
28307 } |
28934 } |
28308 regexp.lastIndex = 0; |
28935 regexp.lastIndex = 0; |
28359 } |
28986 } |
28360 return undefined; |
28987 return undefined; |
28361 }); |
28988 }); |
28362 |
28989 |
28363 ctrl.$formatters.push(function(value) { |
28990 ctrl.$formatters.push(function(value) { |
28364 if (!ctrl.$isEmpty(value)) { |
28991 if (value && !isDate(value)) { |
28365 if (!isDate(value)) { |
28992 throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); |
28366 throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); |
28993 } |
28367 } |
28994 if (isValidDate(value)) { |
28368 previousDate = value; |
28995 previousDate = value; |
28369 if (previousDate && timezone === 'UTC') { |
28996 if (previousDate && timezone === 'UTC') { |
28370 var timezoneOffset = 60000 * previousDate.getTimezoneOffset(); |
28997 var timezoneOffset = 60000 * previousDate.getTimezoneOffset(); |
28371 previousDate = new Date(previousDate.getTime() + timezoneOffset); |
28998 previousDate = new Date(previousDate.getTime() + timezoneOffset); |
28372 } |
28999 } |
28373 return $filter('date')(value, format, timezone); |
29000 return $filter('date')(value, format, timezone); |
28374 } else { |
29001 } else { |
28375 previousDate = null; |
29002 previousDate = null; |
29003 return ''; |
|
28376 } |
29004 } |
28377 return ''; |
|
28378 }); |
29005 }); |
28379 |
29006 |
28380 if (isDefined(attr.min) || attr.ngMin) { |
29007 if (isDefined(attr.min) || attr.ngMin) { |
28381 var minVal; |
29008 var minVal; |
28382 ctrl.$validators.min = function(value) { |
29009 ctrl.$validators.min = function(value) { |
28383 return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal; |
29010 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; |
28384 }; |
29011 }; |
28385 attr.$observe('min', function(val) { |
29012 attr.$observe('min', function(val) { |
28386 minVal = parseObservedDateValue(val); |
29013 minVal = parseObservedDateValue(val); |
28387 ctrl.$validate(); |
29014 ctrl.$validate(); |
28388 }); |
29015 }); |
28389 } |
29016 } |
28390 |
29017 |
28391 if (isDefined(attr.max) || attr.ngMax) { |
29018 if (isDefined(attr.max) || attr.ngMax) { |
28392 var maxVal; |
29019 var maxVal; |
28393 ctrl.$validators.max = function(value) { |
29020 ctrl.$validators.max = function(value) { |
28394 return ctrl.$isEmpty(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; |
29021 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; |
28395 }; |
29022 }; |
28396 attr.$observe('max', function(val) { |
29023 attr.$observe('max', function(val) { |
28397 maxVal = parseObservedDateValue(val); |
29024 maxVal = parseObservedDateValue(val); |
28398 ctrl.$validate(); |
29025 ctrl.$validate(); |
28399 }); |
29026 }); |
28400 } |
29027 } |
28401 // Override the standard $isEmpty to detect invalid dates as well |
29028 |
28402 ctrl.$isEmpty = function(value) { |
29029 function isValidDate(value) { |
28403 // Invalid Date: getTime() returns NaN |
29030 // Invalid Date: getTime() returns NaN |
28404 return !value || (value.getTime && value.getTime() !== value.getTime()); |
29031 return value && !(value.getTime && value.getTime() !== value.getTime()); |
28405 }; |
29032 } |
28406 |
29033 |
28407 function parseObservedDateValue(val) { |
29034 function parseObservedDateValue(val) { |
28408 return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; |
29035 return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; |
28409 } |
29036 } |
28410 }; |
29037 }; |
28484 // in browsers, i.e. we can always read out input.value even if it is not valid! |
29111 // in browsers, i.e. we can always read out input.value even if it is not valid! |
28485 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
29112 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
28486 stringBasedInputType(ctrl); |
29113 stringBasedInputType(ctrl); |
28487 |
29114 |
28488 ctrl.$$parserName = 'url'; |
29115 ctrl.$$parserName = 'url'; |
28489 ctrl.$validators.url = function(value) { |
29116 ctrl.$validators.url = function(modelValue, viewValue) { |
29117 var value = modelValue || viewValue; |
|
28490 return ctrl.$isEmpty(value) || URL_REGEXP.test(value); |
29118 return ctrl.$isEmpty(value) || URL_REGEXP.test(value); |
28491 }; |
29119 }; |
28492 } |
29120 } |
28493 |
29121 |
28494 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { |
29122 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { |
28496 // in browsers, i.e. we can always read out input.value even if it is not valid! |
29124 // in browsers, i.e. we can always read out input.value even if it is not valid! |
28497 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
29125 baseInputType(scope, element, attr, ctrl, $sniffer, $browser); |
28498 stringBasedInputType(ctrl); |
29126 stringBasedInputType(ctrl); |
28499 |
29127 |
28500 ctrl.$$parserName = 'email'; |
29128 ctrl.$$parserName = 'email'; |
28501 ctrl.$validators.email = function(value) { |
29129 ctrl.$validators.email = function(modelValue, viewValue) { |
29130 var value = modelValue || viewValue; |
|
28502 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); |
29131 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); |
28503 }; |
29132 }; |
28504 } |
29133 } |
28505 |
29134 |
28506 function radioInputType(scope, element, attr, ctrl) { |
29135 function radioInputType(scope, element, attr, ctrl) { |
28550 |
29179 |
28551 ctrl.$render = function() { |
29180 ctrl.$render = function() { |
28552 element[0].checked = ctrl.$viewValue; |
29181 element[0].checked = ctrl.$viewValue; |
28553 }; |
29182 }; |
28554 |
29183 |
28555 // Override the standard `$isEmpty` because an empty checkbox is never equal to the trueValue |
29184 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` |
29185 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert |
|
29186 // it to a boolean. |
|
28556 ctrl.$isEmpty = function(value) { |
29187 ctrl.$isEmpty = function(value) { |
28557 return value !== trueValue; |
29188 return value === false; |
28558 }; |
29189 }; |
28559 |
29190 |
28560 ctrl.$formatters.push(function(value) { |
29191 ctrl.$formatters.push(function(value) { |
28561 return equals(value, trueValue); |
29192 return equals(value, trueValue); |
28562 }); |
29193 }); |
28584 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
29215 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of |
28585 * `required` when you want to data-bind to the `required` attribute. |
29216 * `required` when you want to data-bind to the `required` attribute. |
28586 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
29217 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
28587 * minlength. |
29218 * minlength. |
28588 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
29219 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
28589 * maxlength. |
29220 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any |
29221 * length. |
|
28590 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
29222 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
28591 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
29223 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
28592 * patterns defined as scope expressions. |
29224 * patterns defined as scope expressions. |
28593 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
29225 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
28594 * interaction with the input element. |
29226 * interaction with the input element. |
28600 * @ngdoc directive |
29232 * @ngdoc directive |
28601 * @name input |
29233 * @name input |
28602 * @restrict E |
29234 * @restrict E |
28603 * |
29235 * |
28604 * @description |
29236 * @description |
28605 * HTML input element control with angular data-binding. Input control follows HTML5 input types |
29237 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, |
28606 * and polyfills the HTML5 validation behavior for older browsers. |
29238 * input state control, and validation. |
28607 * |
29239 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. |
28608 * *NOTE* Not every feature offered is available for all input types. |
29240 * |
29241 * <div class="alert alert-warning"> |
|
29242 * **Note:** Not every feature offered is available for all input types. |
|
29243 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. |
|
29244 * </div> |
|
28609 * |
29245 * |
28610 * @param {string} ngModel Assignable angular expression to data-bind to. |
29246 * @param {string} ngModel Assignable angular expression to data-bind to. |
28611 * @param {string=} name Property name of the form under which the control is published. |
29247 * @param {string=} name Property name of the form under which the control is published. |
28612 * @param {string=} required Sets `required` validation error key if the value is not entered. |
29248 * @param {string=} required Sets `required` validation error key if the value is not entered. |
28613 * @param {boolean=} ngRequired Sets `required` attribute if set to true |
29249 * @param {boolean=} ngRequired Sets `required` attribute if set to true |
28614 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
29250 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than |
28615 * minlength. |
29251 * minlength. |
28616 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
29252 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than |
28617 * maxlength. |
29253 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any |
29254 * length. |
|
28618 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
29255 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the |
28619 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
29256 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for |
28620 * patterns defined as scope expressions. |
29257 * patterns defined as scope expressions. |
28621 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
29258 * @param {string=} ngChange Angular expression to be executed when input changes due to user |
28622 * interaction with the input element. |
29259 * interaction with the input element. |
28739 /** |
29376 /** |
28740 * @ngdoc type |
29377 * @ngdoc type |
28741 * @name ngModel.NgModelController |
29378 * @name ngModel.NgModelController |
28742 * |
29379 * |
28743 * @property {string} $viewValue Actual string value in the view. |
29380 * @property {string} $viewValue Actual string value in the view. |
28744 * @property {*} $modelValue The value in the model, that the control is bound to. |
29381 * @property {*} $modelValue The value in the model that the control is bound to. |
28745 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever |
29382 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever |
28746 the control reads value from the DOM. Each function is called, in turn, passing the value |
29383 the control reads value from the DOM. The functions are called in array order, each passing |
28747 through to the next. The last return value is used to populate the model. |
29384 its return value through to the next. The last return value is forwarded to the |
28748 Used to sanitize / convert the value as well as validation. For validation, |
29385 {@link ngModel.NgModelController#$validators `$validators`} collection. |
28749 the parsers should update the validity state using |
29386 |
28750 {@link ngModel.NgModelController#$setValidity $setValidity()}, |
29387 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue |
28751 and return `undefined` for invalid values. |
29388 `$viewValue`}. |
29389 |
|
29390 Returning `undefined` from a parser means a parse error occurred. In that case, |
|
29391 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` |
|
29392 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} |
|
29393 is set to `true`. The parse error is stored in `ngModel.$error.parse`. |
|
28752 |
29394 |
28753 * |
29395 * |
28754 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever |
29396 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever |
28755 the model value changes. Each function is called, in turn, passing the value through to the |
29397 the model value changes. The functions are called in reverse array order, each passing the value through to the |
28756 next. Used to format / convert values for display in the control and validation. |
29398 next. The last return value is used as the actual DOM value. |
29399 Used to format / convert values for display in the control. |
|
28757 * ```js |
29400 * ```js |
28758 * function formatter(value) { |
29401 * function formatter(value) { |
28759 * if (value) { |
29402 * if (value) { |
28760 * return value.toUpperCase(); |
29403 * return value.toUpperCase(); |
28761 * } |
29404 * } |
28782 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to |
29425 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to |
28783 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided |
29426 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided |
28784 * is expected to return a promise when it is run during the model validation process. Once the promise |
29427 * is expected to return a promise when it is run during the model validation process. Once the promise |
28785 * is delivered then the validation status will be set to true when fulfilled and false when rejected. |
29428 * is delivered then the validation status will be set to true when fulfilled and false when rejected. |
28786 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model |
29429 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model |
28787 * value will only be updated once all validators have been fulfilled. Also, keep in mind that all |
29430 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator |
28788 * asynchronous validators will only run once all synchronous validators have passed. |
29431 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators |
29432 * will only run once all synchronous validators have passed. |
|
28789 * |
29433 * |
28790 * Please note that if $http is used then it is important that the server returns a success HTTP response code |
29434 * Please note that if $http is used then it is important that the server returns a success HTTP response code |
28791 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. |
29435 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. |
28792 * |
29436 * |
28793 * ```js |
29437 * ```js |
28804 * return true; |
29448 * return true; |
28805 * }); |
29449 * }); |
28806 * }; |
29450 * }; |
28807 * ``` |
29451 * ``` |
28808 * |
29452 * |
28809 * @param {string} name The name of the validator. |
|
28810 * @param {Function} validationFn The validation function that will be run. |
|
28811 * |
|
28812 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the |
29453 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the |
28813 * view value has changed. It is called with no arguments, and its return value is ignored. |
29454 * view value has changed. It is called with no arguments, and its return value is ignored. |
28814 * This can be used in place of additional $watches against the model value. |
29455 * This can be used in place of additional $watches against the model value. |
28815 * |
29456 * |
28816 * @property {Object} $error An object hash with all failing validator ids as keys. |
29457 * @property {Object} $error An object hash with all failing validator ids as keys. |
28820 * @property {boolean} $touched True if control has lost focus. |
29461 * @property {boolean} $touched True if control has lost focus. |
28821 * @property {boolean} $pristine True if user has not interacted with the control yet. |
29462 * @property {boolean} $pristine True if user has not interacted with the control yet. |
28822 * @property {boolean} $dirty True if user has already interacted with the control. |
29463 * @property {boolean} $dirty True if user has already interacted with the control. |
28823 * @property {boolean} $valid True if there is no error. |
29464 * @property {boolean} $valid True if there is no error. |
28824 * @property {boolean} $invalid True if at least one error on the control. |
29465 * @property {boolean} $invalid True if at least one error on the control. |
29466 * @property {string} $name The name attribute of the control. |
|
28825 * |
29467 * |
28826 * @description |
29468 * @description |
28827 * |
29469 * |
28828 * `NgModelController` provides API for the `ng-model` directive. The controller contains |
29470 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. |
28829 * services for data-binding, validation, CSS updates, and value formatting and parsing. It |
29471 * The controller contains services for data-binding, validation, CSS updates, and value formatting |
28830 * purposefully does not contain any logic which deals with DOM rendering or listening to |
29472 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or |
28831 * DOM events. Such DOM related logic should be provided by other directives which make use of |
29473 * listening to DOM events. |
28832 * `NgModelController` for data-binding. |
29474 * Such DOM related logic should be provided by other directives which make use of |
28833 * |
29475 * `NgModelController` for data-binding to control elements. |
28834 * ## Custom Control Example |
29476 * Angular provides this DOM logic for most {@link input `input`} elements. |
29477 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example |
|
29478 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. |
|
29479 * |
|
29480 * @example |
|
29481 * ### Custom Control Example |
|
28835 * This example shows how to use `NgModelController` with a custom control to achieve |
29482 * This example shows how to use `NgModelController` with a custom control to achieve |
28836 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) |
29483 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) |
28837 * collaborate together to achieve the desired result. |
29484 * collaborate together to achieve the desired result. |
28838 * |
29485 * |
28839 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element |
29486 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element |
28840 * contents be edited in place by the user. This will not work on older browsers. |
29487 * contents be edited in place by the user. This will not work on older browsers. |
28841 * |
29488 * |
28842 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} |
29489 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} |
28843 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). |
29490 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). |
28844 * However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks |
29491 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks |
28845 * that content using the `$sce` service. |
29492 * that content using the `$sce` service. |
28846 * |
29493 * |
28847 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> |
29494 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> |
28848 <file name="style.css"> |
29495 <file name="style.css"> |
28849 [contenteditable] { |
29496 [contenteditable] { |
28871 element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); |
29518 element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); |
28872 }; |
29519 }; |
28873 |
29520 |
28874 // Listen for change events to enable binding |
29521 // Listen for change events to enable binding |
28875 element.on('blur keyup change', function() { |
29522 element.on('blur keyup change', function() { |
28876 scope.$apply(read); |
29523 scope.$evalAsync(read); |
28877 }); |
29524 }); |
28878 read(); // initialize |
29525 read(); // initialize |
28879 |
29526 |
28880 // Write data to the model |
29527 // Write data to the model |
28881 function read() { |
29528 function read() { |
28926 */ |
29573 */ |
28927 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', |
29574 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', |
28928 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { |
29575 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { |
28929 this.$viewValue = Number.NaN; |
29576 this.$viewValue = Number.NaN; |
28930 this.$modelValue = Number.NaN; |
29577 this.$modelValue = Number.NaN; |
29578 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. |
|
28931 this.$validators = {}; |
29579 this.$validators = {}; |
28932 this.$asyncValidators = {}; |
29580 this.$asyncValidators = {}; |
28933 this.$parsers = []; |
29581 this.$parsers = []; |
28934 this.$formatters = []; |
29582 this.$formatters = []; |
28935 this.$viewChangeListeners = []; |
29583 this.$viewChangeListeners = []; |
28944 this.$pending = undefined; // keep pending keys here |
29592 this.$pending = undefined; // keep pending keys here |
28945 this.$name = $interpolate($attr.name || '', false)($scope); |
29593 this.$name = $interpolate($attr.name || '', false)($scope); |
28946 |
29594 |
28947 |
29595 |
28948 var parsedNgModel = $parse($attr.ngModel), |
29596 var parsedNgModel = $parse($attr.ngModel), |
29597 parsedNgModelAssign = parsedNgModel.assign, |
|
29598 ngModelGet = parsedNgModel, |
|
29599 ngModelSet = parsedNgModelAssign, |
|
28949 pendingDebounce = null, |
29600 pendingDebounce = null, |
28950 ctrl = this; |
29601 ctrl = this; |
28951 |
29602 |
28952 var ngModelGet = function ngModelGet() { |
|
28953 var modelValue = parsedNgModel($scope); |
|
28954 if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) { |
|
28955 modelValue = modelValue(); |
|
28956 } |
|
28957 return modelValue; |
|
28958 }; |
|
28959 |
|
28960 var ngModelSet = function ngModelSet(newValue) { |
|
28961 var getterSetter; |
|
28962 if (ctrl.$options && ctrl.$options.getterSetter && |
|
28963 isFunction(getterSetter = parsedNgModel($scope))) { |
|
28964 |
|
28965 getterSetter(ctrl.$modelValue); |
|
28966 } else { |
|
28967 parsedNgModel.assign($scope, ctrl.$modelValue); |
|
28968 } |
|
28969 }; |
|
28970 |
|
28971 this.$$setOptions = function(options) { |
29603 this.$$setOptions = function(options) { |
28972 ctrl.$options = options; |
29604 ctrl.$options = options; |
28973 |
29605 if (options && options.getterSetter) { |
28974 if (!parsedNgModel.assign && (!options || !options.getterSetter)) { |
29606 var invokeModelGetter = $parse($attr.ngModel + '()'), |
29607 invokeModelSetter = $parse($attr.ngModel + '($$$p)'); |
|
29608 |
|
29609 ngModelGet = function($scope) { |
|
29610 var modelValue = parsedNgModel($scope); |
|
29611 if (isFunction(modelValue)) { |
|
29612 modelValue = invokeModelGetter($scope); |
|
29613 } |
|
29614 return modelValue; |
|
29615 }; |
|
29616 ngModelSet = function($scope, newValue) { |
|
29617 if (isFunction(parsedNgModel($scope))) { |
|
29618 invokeModelSetter($scope, {$$$p: ctrl.$modelValue}); |
|
29619 } else { |
|
29620 parsedNgModelAssign($scope, ctrl.$modelValue); |
|
29621 } |
|
29622 }; |
|
29623 } else if (!parsedNgModel.assign) { |
|
28975 throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}", |
29624 throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}", |
28976 $attr.ngModel, startingTag($element)); |
29625 $attr.ngModel, startingTag($element)); |
28977 } |
29626 } |
28978 }; |
29627 }; |
28979 |
29628 |
29002 /** |
29651 /** |
29003 * @ngdoc method |
29652 * @ngdoc method |
29004 * @name ngModel.NgModelController#$isEmpty |
29653 * @name ngModel.NgModelController#$isEmpty |
29005 * |
29654 * |
29006 * @description |
29655 * @description |
29007 * This is called when we need to determine if the value of the input is empty. |
29656 * This is called when we need to determine if the value of an input is empty. |
29008 * |
29657 * |
29009 * For instance, the required directive does this to work out if the input has data or not. |
29658 * For instance, the required directive does this to work out if the input has data or not. |
29659 * |
|
29010 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. |
29660 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. |
29011 * |
29661 * |
29012 * You can override this for input directives whose concept of being empty is different to the |
29662 * You can override this for input directives whose concept of being empty is different to the |
29013 * default. The `checkboxInputType` directive does this because in its case a value of `false` |
29663 * default. The `checkboxInputType` directive does this because in its case a value of `false` |
29014 * implies empty. |
29664 * implies empty. |
29015 * |
29665 * |
29016 * @param {*} value Model value to check. |
29666 * @param {*} value The value of the input to check for emptiness. |
29017 * @returns {boolean} True if `value` is empty. |
29667 * @returns {boolean} True if `value` is "empty". |
29018 */ |
29668 */ |
29019 this.$isEmpty = function(value) { |
29669 this.$isEmpty = function(value) { |
29020 return isUndefined(value) || value === '' || value === null || value !== value; |
29670 return isUndefined(value) || value === '' || value === null || value !== value; |
29021 }; |
29671 }; |
29022 |
29672 |
29026 /** |
29676 /** |
29027 * @ngdoc method |
29677 * @ngdoc method |
29028 * @name ngModel.NgModelController#$setValidity |
29678 * @name ngModel.NgModelController#$setValidity |
29029 * |
29679 * |
29030 * @description |
29680 * @description |
29031 * Change the validity state, and notifies the form. |
29681 * Change the validity state, and notify the form. |
29032 * |
29682 * |
29033 * This method can be called within $parsers/$formatters. However, if possible, please use the |
29683 * This method can be called within $parsers/$formatters or a custom validation implementation. |
29034 * `ngModel.$validators` pipeline which is designed to call this method automatically. |
29684 * However, in most cases it should be sufficient to use the `ngModel.$validators` and |
29685 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically. |
|
29035 * |
29686 * |
29036 * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign |
29687 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned |
29037 * to `$error[validationErrorKey]` and `$pending[validationErrorKey]` |
29688 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` |
29038 * so that it is available for data-binding. |
29689 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. |
29039 * The `validationErrorKey` should be in camelCase and will get converted into dash-case |
29690 * The `validationErrorKey` should be in camelCase and will get converted into dash-case |
29040 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` |
29691 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` |
29041 * class and can be bound to as `{{someForm.someControl.$error.myError}}` . |
29692 * class and can be bound to as `{{someForm.someControl.$error.myError}}` . |
29042 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), |
29693 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), |
29043 * or skipped (null). |
29694 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. |
29695 * Skipped is used by Angular when validators do not run because of parse errors and |
|
29696 * when `$asyncValidators` do not run because any of the `$validators` failed. |
|
29044 */ |
29697 */ |
29045 addSetValidityMethod({ |
29698 addSetValidityMethod({ |
29046 ctrl: this, |
29699 ctrl: this, |
29047 $element: $element, |
29700 $element: $element, |
29048 set: function(object, property) { |
29701 set: function(object, property) { |
29060 * @name ngModel.NgModelController#$setPristine |
29713 * @name ngModel.NgModelController#$setPristine |
29061 * |
29714 * |
29062 * @description |
29715 * @description |
29063 * Sets the control to its pristine state. |
29716 * Sets the control to its pristine state. |
29064 * |
29717 * |
29065 * This method can be called to remove the 'ng-dirty' class and set the control to its pristine |
29718 * This method can be called to remove the `ng-dirty` class and set the control to its pristine |
29066 * state (ng-pristine class). A model is considered to be pristine when the model has not been changed |
29719 * state (`ng-pristine` class). A model is considered to be pristine when the control |
29067 * from when first compiled within then form. |
29720 * has not been changed from when first compiled. |
29068 */ |
29721 */ |
29069 this.$setPristine = function () { |
29722 this.$setPristine = function() { |
29070 ctrl.$dirty = false; |
29723 ctrl.$dirty = false; |
29071 ctrl.$pristine = true; |
29724 ctrl.$pristine = true; |
29072 $animate.removeClass($element, DIRTY_CLASS); |
29725 $animate.removeClass($element, DIRTY_CLASS); |
29073 $animate.addClass($element, PRISTINE_CLASS); |
29726 $animate.addClass($element, PRISTINE_CLASS); |
29727 }; |
|
29728 |
|
29729 /** |
|
29730 * @ngdoc method |
|
29731 * @name ngModel.NgModelController#$setDirty |
|
29732 * |
|
29733 * @description |
|
29734 * Sets the control to its dirty state. |
|
29735 * |
|
29736 * This method can be called to remove the `ng-pristine` class and set the control to its dirty |
|
29737 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed |
|
29738 * from when first compiled. |
|
29739 */ |
|
29740 this.$setDirty = function() { |
|
29741 ctrl.$dirty = true; |
|
29742 ctrl.$pristine = false; |
|
29743 $animate.removeClass($element, PRISTINE_CLASS); |
|
29744 $animate.addClass($element, DIRTY_CLASS); |
|
29745 parentForm.$setDirty(); |
|
29074 }; |
29746 }; |
29075 |
29747 |
29076 /** |
29748 /** |
29077 * @ngdoc method |
29749 * @ngdoc method |
29078 * @name ngModel.NgModelController#$setUntouched |
29750 * @name ngModel.NgModelController#$setUntouched |
29079 * |
29751 * |
29080 * @description |
29752 * @description |
29081 * Sets the control to its untouched state. |
29753 * Sets the control to its untouched state. |
29082 * |
29754 * |
29083 * This method can be called to remove the 'ng-touched' class and set the control to its |
29755 * This method can be called to remove the `ng-touched` class and set the control to its |
29084 * untouched state (ng-untouched class). Upon compilation, a model is set as untouched |
29756 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched |
29085 * by default, however this function can be used to restore that state if the model has |
29757 * by default, however this function can be used to restore that state if the model has |
29086 * already been touched by the user. |
29758 * already been touched by the user. |
29087 */ |
29759 */ |
29088 this.$setUntouched = function() { |
29760 this.$setUntouched = function() { |
29089 ctrl.$touched = false; |
29761 ctrl.$touched = false; |
29096 * @name ngModel.NgModelController#$setTouched |
29768 * @name ngModel.NgModelController#$setTouched |
29097 * |
29769 * |
29098 * @description |
29770 * @description |
29099 * Sets the control to its touched state. |
29771 * Sets the control to its touched state. |
29100 * |
29772 * |
29101 * This method can be called to remove the 'ng-untouched' class and set the control to its |
29773 * This method can be called to remove the `ng-untouched` class and set the control to its |
29102 * touched state (ng-touched class). A model is considered to be touched when the user has |
29774 * touched state (`ng-touched` class). A model is considered to be touched when the user has |
29103 * first interacted (focussed) on the model input element and then shifted focus away (blurred) |
29775 * first focused the control element and then shifted focus away from the control (blur event). |
29104 * from the input element. |
|
29105 */ |
29776 */ |
29106 this.$setTouched = function() { |
29777 this.$setTouched = function() { |
29107 ctrl.$touched = true; |
29778 ctrl.$touched = true; |
29108 ctrl.$untouched = false; |
29779 ctrl.$untouched = false; |
29109 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS); |
29780 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS); |
29133 * <example name="ng-model-cancel-update" module="cancel-update-example"> |
29804 * <example name="ng-model-cancel-update" module="cancel-update-example"> |
29134 * <file name="app.js"> |
29805 * <file name="app.js"> |
29135 * angular.module('cancel-update-example', []) |
29806 * angular.module('cancel-update-example', []) |
29136 * |
29807 * |
29137 * .controller('CancelUpdateController', ['$scope', function($scope) { |
29808 * .controller('CancelUpdateController', ['$scope', function($scope) { |
29138 * $scope.resetWithCancel = function (e) { |
29809 * $scope.resetWithCancel = function(e) { |
29139 * if (e.keyCode == 27) { |
29810 * if (e.keyCode == 27) { |
29140 * $scope.myForm.myInput1.$rollbackViewValue(); |
29811 * $scope.myForm.myInput1.$rollbackViewValue(); |
29141 * $scope.myValue = ''; |
29812 * $scope.myValue = ''; |
29142 * } |
29813 * } |
29143 * }; |
29814 * }; |
29144 * $scope.resetWithoutCancel = function (e) { |
29815 * $scope.resetWithoutCancel = function(e) { |
29145 * if (e.keyCode == 27) { |
29816 * if (e.keyCode == 27) { |
29146 * $scope.myValue = ''; |
29817 * $scope.myValue = ''; |
29147 * } |
29818 * } |
29148 * }; |
29819 * }; |
29149 * }]); |
29820 * }]); |
29177 /** |
29848 /** |
29178 * @ngdoc method |
29849 * @ngdoc method |
29179 * @name ngModel.NgModelController#$validate |
29850 * @name ngModel.NgModelController#$validate |
29180 * |
29851 * |
29181 * @description |
29852 * @description |
29182 * Runs each of the registered validators (first synchronous validators and then asynchronous validators). |
29853 * Runs each of the registered validators (first synchronous validators and then |
29854 * asynchronous validators). |
|
29855 * If the validity changes to invalid, the model will be set to `undefined`, |
|
29856 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. |
|
29857 * If the validity changes to valid, it will set the model to the last available valid |
|
29858 * modelValue, i.e. either the last parsed value or the last value set from the scope. |
|
29183 */ |
29859 */ |
29184 this.$validate = function() { |
29860 this.$validate = function() { |
29185 // ignore $validate before model is initialized |
29861 // ignore $validate before model is initialized |
29186 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { |
29862 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { |
29187 return; |
29863 return; |
29188 } |
29864 } |
29189 this.$$parseAndValidate(); |
29865 |
29866 var viewValue = ctrl.$$lastCommittedViewValue; |
|
29867 // Note: we use the $$rawModelValue as $modelValue might have been |
|
29868 // set to undefined during a view -> model update that found validation |
|
29869 // errors. We can't parse the view here, since that could change |
|
29870 // the model although neither viewValue nor the model on the scope changed |
|
29871 var modelValue = ctrl.$$rawModelValue; |
|
29872 |
|
29873 // Check if the there's a parse error, so we don't unset it accidentially |
|
29874 var parserName = ctrl.$$parserName || 'parse'; |
|
29875 var parserValid = ctrl.$error[parserName] ? false : undefined; |
|
29876 |
|
29877 var prevValid = ctrl.$valid; |
|
29878 var prevModelValue = ctrl.$modelValue; |
|
29879 |
|
29880 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; |
|
29881 |
|
29882 ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) { |
|
29883 // If there was no change in validity, don't update the model |
|
29884 // This prevents changing an invalid modelValue to undefined |
|
29885 if (!allowInvalid && prevValid !== allValid) { |
|
29886 // Note: Don't check ctrl.$valid here, as we could have |
|
29887 // external validators (e.g. calculated on the server), |
|
29888 // that just call $setValidity and need the model value |
|
29889 // to calculate their validity. |
|
29890 ctrl.$modelValue = allValid ? modelValue : undefined; |
|
29891 |
|
29892 if (ctrl.$modelValue !== prevModelValue) { |
|
29893 ctrl.$$writeModelToScope(); |
|
29894 } |
|
29895 } |
|
29896 }); |
|
29897 |
|
29190 }; |
29898 }; |
29191 |
29899 |
29192 this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) { |
29900 this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) { |
29193 currentValidationRunId++; |
29901 currentValidationRunId++; |
29194 var localValidationRunId = currentValidationRunId; |
29902 var localValidationRunId = currentValidationRunId; |
29303 } |
30011 } |
29304 ctrl.$$lastCommittedViewValue = viewValue; |
30012 ctrl.$$lastCommittedViewValue = viewValue; |
29305 |
30013 |
29306 // change to dirty |
30014 // change to dirty |
29307 if (ctrl.$pristine) { |
30015 if (ctrl.$pristine) { |
29308 ctrl.$dirty = true; |
30016 this.$setDirty(); |
29309 ctrl.$pristine = false; |
|
29310 $animate.removeClass($element, PRISTINE_CLASS); |
|
29311 $animate.addClass($element, DIRTY_CLASS); |
|
29312 parentForm.$setDirty(); |
|
29313 } |
30017 } |
29314 this.$$parseAndValidate(); |
30018 this.$$parseAndValidate(); |
29315 }; |
30019 }; |
29316 |
30020 |
29317 this.$$parseAndValidate = function() { |
30021 this.$$parseAndValidate = function() { |
29318 var viewValue = ctrl.$$lastCommittedViewValue; |
30022 var viewValue = ctrl.$$lastCommittedViewValue; |
29319 var modelValue = viewValue; |
30023 var modelValue = viewValue; |
29320 var parserValid = isUndefined(modelValue) ? undefined : true; |
30024 var parserValid = isUndefined(modelValue) ? undefined : true; |
29321 |
30025 |
29322 if (parserValid) { |
30026 if (parserValid) { |
29323 for(var i = 0; i < ctrl.$parsers.length; i++) { |
30027 for (var i = 0; i < ctrl.$parsers.length; i++) { |
29324 modelValue = ctrl.$parsers[i](modelValue); |
30028 modelValue = ctrl.$parsers[i](modelValue); |
29325 if (isUndefined(modelValue)) { |
30029 if (isUndefined(modelValue)) { |
29326 parserValid = false; |
30030 parserValid = false; |
29327 break; |
30031 break; |
29328 } |
30032 } |
29329 } |
30033 } |
29330 } |
30034 } |
29331 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { |
30035 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { |
29332 // ctrl.$modelValue has not been touched yet... |
30036 // ctrl.$modelValue has not been touched yet... |
29333 ctrl.$modelValue = ngModelGet(); |
30037 ctrl.$modelValue = ngModelGet($scope); |
29334 } |
30038 } |
29335 var prevModelValue = ctrl.$modelValue; |
30039 var prevModelValue = ctrl.$modelValue; |
29336 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; |
30040 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; |
30041 ctrl.$$rawModelValue = modelValue; |
|
30042 |
|
29337 if (allowInvalid) { |
30043 if (allowInvalid) { |
29338 ctrl.$modelValue = modelValue; |
30044 ctrl.$modelValue = modelValue; |
29339 writeToModelIfNeeded(); |
30045 writeToModelIfNeeded(); |
29340 } |
30046 } |
29341 ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) { |
30047 |
30048 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date. |
|
30049 // This can happen if e.g. $setViewValue is called from inside a parser |
|
30050 ctrl.$$runValidators(parserValid, modelValue, ctrl.$$lastCommittedViewValue, function(allValid) { |
|
29342 if (!allowInvalid) { |
30051 if (!allowInvalid) { |
29343 // Note: Don't check ctrl.$valid here, as we could have |
30052 // Note: Don't check ctrl.$valid here, as we could have |
29344 // external validators (e.g. calculated on the server), |
30053 // external validators (e.g. calculated on the server), |
29345 // that just call $setValidity and need the model value |
30054 // that just call $setValidity and need the model value |
29346 // to calculate their validity. |
30055 // to calculate their validity. |
29355 } |
30064 } |
29356 } |
30065 } |
29357 }; |
30066 }; |
29358 |
30067 |
29359 this.$$writeModelToScope = function() { |
30068 this.$$writeModelToScope = function() { |
29360 ngModelSet(ctrl.$modelValue); |
30069 ngModelSet($scope, ctrl.$modelValue); |
29361 forEach(ctrl.$viewChangeListeners, function(listener) { |
30070 forEach(ctrl.$viewChangeListeners, function(listener) { |
29362 try { |
30071 try { |
29363 listener(); |
30072 listener(); |
29364 } catch(e) { |
30073 } catch (e) { |
29365 $exceptionHandler(e); |
30074 $exceptionHandler(e); |
29366 } |
30075 } |
29367 }); |
30076 }); |
29368 }; |
30077 }; |
29369 |
30078 |
29451 // 3. ng-change kicks in and reverts scope value to 'a' |
30160 // 3. ng-change kicks in and reverts scope value to 'a' |
29452 // -> scope value did not change since the last digest as |
30161 // -> scope value did not change since the last digest as |
29453 // ng-change executes in apply phase |
30162 // ng-change executes in apply phase |
29454 // 4. view should be changed back to 'a' |
30163 // 4. view should be changed back to 'a' |
29455 $scope.$watch(function ngModelWatch() { |
30164 $scope.$watch(function ngModelWatch() { |
29456 var modelValue = ngModelGet(); |
30165 var modelValue = ngModelGet($scope); |
29457 |
30166 |
29458 // if scope model value and ngModel value are out of sync |
30167 // if scope model value and ngModel value are out of sync |
29459 // TODO(perf): why not move this to the action fn? |
30168 // TODO(perf): why not move this to the action fn? |
29460 if (modelValue !== ctrl.$modelValue) { |
30169 if (modelValue !== ctrl.$modelValue) { |
29461 ctrl.$modelValue = modelValue; |
30170 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; |
29462 |
30171 |
29463 var formatters = ctrl.$formatters, |
30172 var formatters = ctrl.$formatters, |
29464 idx = formatters.length; |
30173 idx = formatters.length; |
29465 |
30174 |
29466 var viewValue = modelValue; |
30175 var viewValue = modelValue; |
29467 while(idx--) { |
30176 while (idx--) { |
29468 viewValue = formatters[idx](viewValue); |
30177 viewValue = formatters[idx](viewValue); |
29469 } |
30178 } |
29470 if (ctrl.$viewValue !== viewValue) { |
30179 if (ctrl.$viewValue !== viewValue) { |
29471 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; |
30180 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; |
29472 ctrl.$render(); |
30181 ctrl.$render(); |
29483 /** |
30192 /** |
29484 * @ngdoc directive |
30193 * @ngdoc directive |
29485 * @name ngModel |
30194 * @name ngModel |
29486 * |
30195 * |
29487 * @element input |
30196 * @element input |
30197 * @priority 1 |
|
29488 * |
30198 * |
29489 * @description |
30199 * @description |
29490 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a |
30200 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a |
29491 * property on the scope using {@link ngModel.NgModelController NgModelController}, |
30201 * property on the scope using {@link ngModel.NgModelController NgModelController}, |
29492 * which is created and exposed by this directive. |
30202 * which is created and exposed by this directive. |
29504 * current scope. If the property doesn't already exist on this scope, it will be created |
30214 * current scope. If the property doesn't already exist on this scope, it will be created |
29505 * implicitly and added to the scope. |
30215 * implicitly and added to the scope. |
29506 * |
30216 * |
29507 * For best practices on using `ngModel`, see: |
30217 * For best practices on using `ngModel`, see: |
29508 * |
30218 * |
29509 * - [https://github.com/angular/angular.js/wiki/Understanding-Scopes] |
30219 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) |
29510 * |
30220 * |
29511 * For basic examples, how to use `ngModel`, see: |
30221 * For basic examples, how to use `ngModel`, see: |
29512 * |
30222 * |
29513 * - {@link ng.directive:input input} |
30223 * - {@link ng.directive:input input} |
29514 * - {@link input[text] text} |
30224 * - {@link input[text] text} |
29516 * - {@link input[radio] radio} |
30226 * - {@link input[radio] radio} |
29517 * - {@link input[number] number} |
30227 * - {@link input[number] number} |
29518 * - {@link input[email] email} |
30228 * - {@link input[email] email} |
29519 * - {@link input[url] url} |
30229 * - {@link input[url] url} |
29520 * - {@link input[date] date} |
30230 * - {@link input[date] date} |
29521 * - {@link input[dateTimeLocal] dateTimeLocal} |
30231 * - {@link input[datetime-local] datetime-local} |
29522 * - {@link input[time] time} |
30232 * - {@link input[time] time} |
29523 * - {@link input[month] month} |
30233 * - {@link input[month] month} |
29524 * - {@link input[week] week} |
30234 * - {@link input[week] week} |
29525 * - {@link ng.directive:select select} |
30235 * - {@link ng.directive:select select} |
29526 * - {@link ng.directive:textarea textarea} |
30236 * - {@link ng.directive:textarea textarea} |
29527 * |
30237 * |
29528 * # CSS classes |
30238 * # CSS classes |
29529 * The following CSS classes are added and removed on the associated input/select/textarea element |
30239 * The following CSS classes are added and removed on the associated input/select/textarea element |
29530 * depending on the validity of the model. |
30240 * depending on the validity of the model. |
29531 * |
30241 * |
29532 * - `ng-valid` is set if the model is valid. |
30242 * - `ng-valid`: the model is valid |
29533 * - `ng-invalid` is set if the model is invalid. |
30243 * - `ng-invalid`: the model is invalid |
29534 * - `ng-pristine` is set if the model is pristine. |
30244 * - `ng-valid-[key]`: for each valid key added by `$setValidity` |
29535 * - `ng-dirty` is set if the model is dirty. |
30245 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity` |
30246 * - `ng-pristine`: the control hasn't been interacted with yet |
|
30247 * - `ng-dirty`: the control has been interacted with |
|
30248 * - `ng-touched`: the control has been blurred |
|
30249 * - `ng-untouched`: the control hasn't been blurred |
|
30250 * - `ng-pending`: any `$asyncValidators` are unfulfilled |
|
29536 * |
30251 * |
29537 * Keep in mind that ngAnimate can detect each of these classes when added and removed. |
30252 * Keep in mind that ngAnimate can detect each of these classes when added and removed. |
29538 * |
30253 * |
29539 * ## Animation Hooks |
30254 * ## Animation Hooks |
29540 * |
30255 * |
29624 <file name="app.js"> |
30339 <file name="app.js"> |
29625 angular.module('getterSetterExample', []) |
30340 angular.module('getterSetterExample', []) |
29626 .controller('ExampleController', ['$scope', function($scope) { |
30341 .controller('ExampleController', ['$scope', function($scope) { |
29627 var _name = 'Brian'; |
30342 var _name = 'Brian'; |
29628 $scope.user = { |
30343 $scope.user = { |
29629 name: function (newName) { |
30344 name: function(newName) { |
29630 if (angular.isDefined(newName)) { |
30345 if (angular.isDefined(newName)) { |
29631 _name = newName; |
30346 _name = newName; |
29632 } |
30347 } |
29633 return _name; |
30348 return _name; |
29634 } |
30349 } |
29635 }; |
30350 }; |
29636 }]); |
30351 }]); |
29637 </file> |
30352 </file> |
29638 * </example> |
30353 * </example> |
29639 */ |
30354 */ |
29640 var ngModelDirective = function() { |
30355 var ngModelDirective = ['$rootScope', function($rootScope) { |
29641 return { |
30356 return { |
29642 restrict: 'A', |
30357 restrict: 'A', |
29643 require: ['ngModel', '^?form', '^?ngModelOptions'], |
30358 require: ['ngModel', '^?form', '^?ngModelOptions'], |
29644 controller: NgModelController, |
30359 controller: NgModelController, |
29645 // Prelink needs to run before any input directive |
30360 // Prelink needs to run before any input directive |
29679 } |
30394 } |
29680 |
30395 |
29681 element.on('blur', function(ev) { |
30396 element.on('blur', function(ev) { |
29682 if (modelCtrl.$touched) return; |
30397 if (modelCtrl.$touched) return; |
29683 |
30398 |
29684 scope.$apply(function() { |
30399 if ($rootScope.$$phase) { |
29685 modelCtrl.$setTouched(); |
30400 scope.$evalAsync(modelCtrl.$setTouched); |
29686 }); |
30401 } else { |
30402 scope.$apply(modelCtrl.$setTouched); |
|
30403 } |
|
29687 }); |
30404 }); |
29688 } |
30405 } |
29689 }; |
30406 }; |
29690 } |
30407 } |
29691 }; |
30408 }; |
29692 }; |
30409 }]; |
29693 |
30410 |
29694 |
30411 |
29695 /** |
30412 /** |
29696 * @ngdoc directive |
30413 * @ngdoc directive |
29697 * @name ngChange |
30414 * @name ngChange |
29776 require: '?ngModel', |
30493 require: '?ngModel', |
29777 link: function(scope, elm, attr, ctrl) { |
30494 link: function(scope, elm, attr, ctrl) { |
29778 if (!ctrl) return; |
30495 if (!ctrl) return; |
29779 attr.required = true; // force truthy in case we are on non input element |
30496 attr.required = true; // force truthy in case we are on non input element |
29780 |
30497 |
29781 ctrl.$validators.required = function(value) { |
30498 ctrl.$validators.required = function(modelValue, viewValue) { |
29782 return !attr.required || !ctrl.$isEmpty(value); |
30499 return !attr.required || !ctrl.$isEmpty(viewValue); |
29783 }; |
30500 }; |
29784 |
30501 |
29785 attr.$observe('required', function() { |
30502 attr.$observe('required', function() { |
29786 ctrl.$validate(); |
30503 ctrl.$validate(); |
29787 }); |
30504 }); |
29798 if (!ctrl) return; |
30515 if (!ctrl) return; |
29799 |
30516 |
29800 var regexp, patternExp = attr.ngPattern || attr.pattern; |
30517 var regexp, patternExp = attr.ngPattern || attr.pattern; |
29801 attr.$observe('pattern', function(regex) { |
30518 attr.$observe('pattern', function(regex) { |
29802 if (isString(regex) && regex.length > 0) { |
30519 if (isString(regex) && regex.length > 0) { |
29803 regex = new RegExp(regex); |
30520 regex = new RegExp('^' + regex + '$'); |
29804 } |
30521 } |
29805 |
30522 |
29806 if (regex && !regex.test) { |
30523 if (regex && !regex.test) { |
29807 throw minErr('ngPattern')('noregexp', |
30524 throw minErr('ngPattern')('noregexp', |
29808 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, |
30525 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, |
29826 restrict: 'A', |
30543 restrict: 'A', |
29827 require: '?ngModel', |
30544 require: '?ngModel', |
29828 link: function(scope, elm, attr, ctrl) { |
30545 link: function(scope, elm, attr, ctrl) { |
29829 if (!ctrl) return; |
30546 if (!ctrl) return; |
29830 |
30547 |
29831 var maxlength = 0; |
30548 var maxlength = -1; |
29832 attr.$observe('maxlength', function(value) { |
30549 attr.$observe('maxlength', function(value) { |
29833 maxlength = int(value) || 0; |
30550 var intVal = int(value); |
30551 maxlength = isNaN(intVal) ? -1 : intVal; |
|
29834 ctrl.$validate(); |
30552 ctrl.$validate(); |
29835 }); |
30553 }); |
29836 ctrl.$validators.maxlength = function(modelValue, viewValue) { |
30554 ctrl.$validators.maxlength = function(modelValue, viewValue) { |
29837 return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength; |
30555 return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength); |
29838 }; |
30556 }; |
29839 } |
30557 } |
29840 }; |
30558 }; |
29841 }; |
30559 }; |
29842 |
30560 |
29851 attr.$observe('minlength', function(value) { |
30569 attr.$observe('minlength', function(value) { |
29852 minlength = int(value) || 0; |
30570 minlength = int(value) || 0; |
29853 ctrl.$validate(); |
30571 ctrl.$validate(); |
29854 }); |
30572 }); |
29855 ctrl.$validators.minlength = function(modelValue, viewValue) { |
30573 ctrl.$validators.minlength = function(modelValue, viewValue) { |
29856 return ctrl.$isEmpty(modelValue) || viewValue.length >= minlength; |
30574 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; |
29857 }; |
30575 }; |
29858 } |
30576 } |
29859 }; |
30577 }; |
29860 }; |
30578 }; |
29861 |
30579 |
29991 /** |
30709 /** |
29992 * @ngdoc directive |
30710 * @ngdoc directive |
29993 * @name ngValue |
30711 * @name ngValue |
29994 * |
30712 * |
29995 * @description |
30713 * @description |
29996 * Binds the given expression to the value of `input[select]` or `input[radio]`, so |
30714 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`}, |
29997 * that when the element is selected, the `ngModel` of that element is set to the |
30715 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to |
29998 * bound value. |
30716 * the bound value. |
29999 * |
30717 * |
30000 * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as |
30718 * `ngValue` is useful when dynamically generating lists of radio buttons using |
30001 * shown below. |
30719 * {@link ngRepeat `ngRepeat`}, as shown below. |
30720 * |
|
30721 * Likewise, `ngValue` can be used to generate `<option>` elements for |
|
30722 * the {@link select `select`} element. In that case however, only strings are supported |
|
30723 * for the `value `attribute, so the resulting `ngModel` will always be a string. |
|
30724 * Support for `select` models with non-string values is available via `ngOptions`. |
|
30002 * |
30725 * |
30003 * @element input |
30726 * @element input |
30004 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute |
30727 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute |
30005 * of the `input` element |
30728 * of the `input` element |
30006 * |
30729 * |
30084 * to have access to the updated model. |
30807 * to have access to the updated model. |
30085 * |
30808 * |
30086 * `ngModelOptions` has an effect on the element it's declared on and its descendants. |
30809 * `ngModelOptions` has an effect on the element it's declared on and its descendants. |
30087 * |
30810 * |
30088 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are: |
30811 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are: |
30089 * - `updateOn`: string specifying which event should be the input bound to. You can set several |
30812 * - `updateOn`: string specifying which event should the input be bound to. You can set several |
30090 * events using an space delimited list. There is a special event called `default` that |
30813 * events using an space delimited list. There is a special event called `default` that |
30091 * matches the default events belonging of the control. |
30814 * matches the default events belonging of the control. |
30092 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A |
30815 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A |
30093 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a |
30816 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a |
30094 * custom value for each event. For example: |
30817 * custom value for each event. For example: |
30126 <file name="app.js"> |
30849 <file name="app.js"> |
30127 angular.module('optionsExample', []) |
30850 angular.module('optionsExample', []) |
30128 .controller('ExampleController', ['$scope', function($scope) { |
30851 .controller('ExampleController', ['$scope', function($scope) { |
30129 $scope.user = { name: 'say', data: '' }; |
30852 $scope.user = { name: 'say', data: '' }; |
30130 |
30853 |
30131 $scope.cancel = function (e) { |
30854 $scope.cancel = function(e) { |
30132 if (e.keyCode == 27) { |
30855 if (e.keyCode == 27) { |
30133 $scope.userForm.userName.$rollbackViewValue(); |
30856 $scope.userForm.userName.$rollbackViewValue(); |
30134 } |
30857 } |
30135 }; |
30858 }; |
30136 }]); |
30859 }]); |
30200 <file name="app.js"> |
30923 <file name="app.js"> |
30201 angular.module('getterSetterExample', []) |
30924 angular.module('getterSetterExample', []) |
30202 .controller('ExampleController', ['$scope', function($scope) { |
30925 .controller('ExampleController', ['$scope', function($scope) { |
30203 var _name = 'Brian'; |
30926 var _name = 'Brian'; |
30204 $scope.user = { |
30927 $scope.user = { |
30205 name: function (newName) { |
30928 name: function(newName) { |
30206 return angular.isDefined(newName) ? (_name = newName) : _name; |
30929 return angular.isDefined(newName) ? (_name = newName) : _name; |
30207 } |
30930 } |
30208 }; |
30931 }; |
30209 }]); |
30932 }]); |
30210 </file> |
30933 </file> |
30423 * Try it here: enter text in text box and watch the greeting change. |
31146 * Try it here: enter text in text box and watch the greeting change. |
30424 <example module="bindExample"> |
31147 <example module="bindExample"> |
30425 <file name="index.html"> |
31148 <file name="index.html"> |
30426 <script> |
31149 <script> |
30427 angular.module('bindExample', []) |
31150 angular.module('bindExample', []) |
30428 .controller('ExampleController', ['$scope', function ($scope) { |
31151 .controller('ExampleController', ['$scope', function($scope) { |
30429 $scope.salutation = 'Hello'; |
31152 $scope.salutation = 'Hello'; |
30430 $scope.name = 'World'; |
31153 $scope.name = 'World'; |
30431 }]); |
31154 }]); |
30432 </script> |
31155 </script> |
30433 <div ng-controller="ExampleController"> |
31156 <div ng-controller="ExampleController"> |
30474 /** |
31197 /** |
30475 * @ngdoc directive |
31198 * @ngdoc directive |
30476 * @name ngBindHtml |
31199 * @name ngBindHtml |
30477 * |
31200 * |
30478 * @description |
31201 * @description |
30479 * Creates a binding that will innerHTML the result of evaluating the `expression` into the current |
31202 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default, |
30480 * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link |
31203 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service. |
30481 * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` |
31204 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link |
30482 * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in |
31205 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize} |
30483 * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to |
31206 * in your module's dependencies, you need to include "angular-sanitize.js" in your application. |
30484 * include "angular-sanitize.js" in your application. |
|
30485 * |
31207 * |
30486 * You may also bypass sanitization for values you know are safe. To do so, bind to |
31208 * You may also bypass sanitization for values you know are safe. To do so, bind to |
30487 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example |
31209 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example |
30488 * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. |
31210 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}. |
30489 * |
31211 * |
30490 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you |
31212 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you |
30491 * will have an exception (instead of an exploit.) |
31213 * will have an exception (instead of an exploit.) |
30492 * |
31214 * |
30493 * @element ANY |
31215 * @element ANY |
30578 function removeClasses(classes) { |
31300 function removeClasses(classes) { |
30579 var newClasses = digestClassCounts(classes, -1); |
31301 var newClasses = digestClassCounts(classes, -1); |
30580 attr.$removeClass(newClasses); |
31302 attr.$removeClass(newClasses); |
30581 } |
31303 } |
30582 |
31304 |
30583 function digestClassCounts (classes, count) { |
31305 function digestClassCounts(classes, count) { |
30584 var classCounts = element.data('$classCounts') || {}; |
31306 var classCounts = element.data('$classCounts') || {}; |
30585 var classesToUpdate = []; |
31307 var classesToUpdate = []; |
30586 forEach(classes, function (className) { |
31308 forEach(classes, function(className) { |
30587 if (count > 0 || classCounts[className]) { |
31309 if (count > 0 || classCounts[className]) { |
30588 classCounts[className] = (classCounts[className] || 0) + count; |
31310 classCounts[className] = (classCounts[className] || 0) + count; |
30589 if (classCounts[className] === +(count > 0)) { |
31311 if (classCounts[className] === +(count > 0)) { |
30590 classesToUpdate.push(className); |
31312 classesToUpdate.push(className); |
30591 } |
31313 } |
30593 }); |
31315 }); |
30594 element.data('$classCounts', classCounts); |
31316 element.data('$classCounts', classCounts); |
30595 return classesToUpdate.join(' '); |
31317 return classesToUpdate.join(' '); |
30596 } |
31318 } |
30597 |
31319 |
30598 function updateClasses (oldClasses, newClasses) { |
31320 function updateClasses(oldClasses, newClasses) { |
30599 var toAdd = arrayDifference(newClasses, oldClasses); |
31321 var toAdd = arrayDifference(newClasses, oldClasses); |
30600 var toRemove = arrayDifference(oldClasses, newClasses); |
31322 var toRemove = arrayDifference(oldClasses, newClasses); |
30601 toAdd = digestClassCounts(toAdd, 1); |
31323 toAdd = digestClassCounts(toAdd, 1); |
30602 toRemove = digestClassCounts(toRemove, -1); |
31324 toRemove = digestClassCounts(toRemove, -1); |
30603 if (toAdd && toAdd.length) { |
31325 if (toAdd && toAdd.length) { |
30625 |
31347 |
30626 function arrayDifference(tokens1, tokens2) { |
31348 function arrayDifference(tokens1, tokens2) { |
30627 var values = []; |
31349 var values = []; |
30628 |
31350 |
30629 outer: |
31351 outer: |
30630 for(var i = 0; i < tokens1.length; i++) { |
31352 for (var i = 0; i < tokens1.length; i++) { |
30631 var token = tokens1[i]; |
31353 var token = tokens1[i]; |
30632 for(var j = 0; j < tokens2.length; j++) { |
31354 for (var j = 0; j < tokens2.length; j++) { |
30633 if(token == tokens2[j]) continue outer; |
31355 if (token == tokens2[j]) continue outer; |
30634 } |
31356 } |
30635 values.push(token); |
31357 values.push(token); |
30636 } |
31358 } |
30637 return values; |
31359 return values; |
30638 } |
31360 } |
30639 |
31361 |
30640 function arrayClasses (classVal) { |
31362 function arrayClasses(classVal) { |
30641 if (isArray(classVal)) { |
31363 if (isArray(classVal)) { |
30642 return classVal; |
31364 return classVal; |
30643 } else if (isString(classVal)) { |
31365 } else if (isString(classVal)) { |
30644 return classVal.split(' '); |
31366 return classVal.split(' '); |
30645 } else if (isObject(classVal)) { |
31367 } else if (isObject(classVal)) { |
30646 var classes = [], i = 0; |
31368 var classes = []; |
30647 forEach(classVal, function(v, k) { |
31369 forEach(classVal, function(v, k) { |
30648 if (v) { |
31370 if (v) { |
30649 classes = classes.concat(k.split(' ')); |
31371 classes = classes.concat(k.split(' ')); |
30650 } |
31372 } |
30651 }); |
31373 }); |
30795 |
31517 |
30796 ## ngClass and pre-existing CSS3 Transitions/Animations |
31518 ## ngClass and pre-existing CSS3 Transitions/Animations |
30797 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. |
31519 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. |
30798 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder |
31520 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder |
30799 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure |
31521 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure |
30800 to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and |
31522 to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and |
30801 {@link ngAnimate.$animate#removeclass $animate.removeClass}. |
31523 {@link ng.$animate#removeClass $animate.removeClass}. |
30802 */ |
31524 */ |
30803 var ngClassDirective = classDirective('', true); |
31525 var ngClassDirective = classDirective('', true); |
30804 |
31526 |
30805 /** |
31527 /** |
30806 * @ngdoc directive |
31528 * @ngdoc directive |
31202 * |
31924 * |
31203 * @element html |
31925 * @element html |
31204 * @description |
31926 * @description |
31205 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. |
31927 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. |
31206 * |
31928 * |
31207 * This is necessary when developing things like Google Chrome Extensions. |
31929 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. |
31208 * |
31930 * |
31209 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). |
31931 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). |
31210 * For Angular to be CSP compatible there are only two things that we need to do differently: |
31932 * For Angular to be CSP compatible there are only two things that we need to do differently: |
31211 * |
31933 * |
31212 * - don't use `Function` constructor to generate optimized value getters |
31934 * - don't use `Function` constructor to generate optimized value getters |
31400 }); |
32122 }); |
31401 </file> |
32123 </file> |
31402 </example> |
32124 </example> |
31403 */ |
32125 */ |
31404 /* |
32126 /* |
31405 * A directive that allows creation of custom onclick handlers that are defined as angular |
32127 * A collection of directives that allows creation of custom event handlers that are defined as |
31406 * expressions and are compiled and executed within the current scope. |
32128 * angular expressions and are compiled and executed within the current scope. |
31407 * |
|
31408 * Events that are handled via these handler are always configured not to propagate further. |
|
31409 */ |
32129 */ |
31410 var ngEventDirectives = {}; |
32130 var ngEventDirectives = {}; |
31411 |
32131 |
31412 // For events that might fire synchronously during DOM manipulation |
32132 // For events that might fire synchronously during DOM manipulation |
31413 // we need to execute their event handlers asynchronously using $evalAsync, |
32133 // we need to execute their event handlers asynchronously using $evalAsync, |
31422 var directiveName = directiveNormalize('ng-' + eventName); |
32142 var directiveName = directiveNormalize('ng-' + eventName); |
31423 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { |
32143 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { |
31424 return { |
32144 return { |
31425 restrict: 'A', |
32145 restrict: 'A', |
31426 compile: function($element, attr) { |
32146 compile: function($element, attr) { |
31427 var fn = $parse(attr[directiveName]); |
32147 // We expose the powerful $event object on the scope that provides access to the Window, |
32148 // etc. that isn't protected by the fast paths in $parse. We explicitly request better |
|
32149 // checks at the cost of speed since event handler expressions are not executed as |
|
32150 // frequently as regular change detection. |
|
32151 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true); |
|
31428 return function ngEventHandler(scope, element) { |
32152 return function ngEventHandler(scope, element) { |
31429 element.on(eventName, function(event) { |
32153 element.on(eventName, function(event) { |
31430 var callback = function() { |
32154 var callback = function() { |
31431 fn(scope, {$event:event}); |
32155 fn(scope, {$event:event}); |
31432 }; |
32156 }; |
31898 <example module="ngAnimate" deps="angular-animate.js" animations="true"> |
32622 <example module="ngAnimate" deps="angular-animate.js" animations="true"> |
31899 <file name="index.html"> |
32623 <file name="index.html"> |
31900 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> |
32624 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> |
31901 Show when checked: |
32625 Show when checked: |
31902 <span ng-if="checked" class="animate-if"> |
32626 <span ng-if="checked" class="animate-if"> |
31903 I'm removed when the checkbox is unchecked. |
32627 This is removed when the checkbox is unchecked. |
31904 </span> |
32628 </span> |
31905 </file> |
32629 </file> |
31906 <file name="animations.css"> |
32630 <file name="animations.css"> |
31907 .animate-if { |
32631 .animate-if { |
31908 background:white; |
32632 background:white; |
31933 transclude: 'element', |
32657 transclude: 'element', |
31934 priority: 600, |
32658 priority: 600, |
31935 terminal: true, |
32659 terminal: true, |
31936 restrict: 'A', |
32660 restrict: 'A', |
31937 $$tlb: true, |
32661 $$tlb: true, |
31938 link: function ($scope, $element, $attr, ctrl, $transclude) { |
32662 link: function($scope, $element, $attr, ctrl, $transclude) { |
31939 var block, childScope, previousElements; |
32663 var block, childScope, previousElements; |
31940 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { |
32664 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { |
31941 |
32665 |
31942 if (value) { |
32666 if (value) { |
31943 if (!childScope) { |
32667 if (!childScope) { |
31944 $transclude(function (clone, newScope) { |
32668 $transclude(function(clone, newScope) { |
31945 childScope = newScope; |
32669 childScope = newScope; |
31946 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); |
32670 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); |
31947 // Note: We only need the first/last node of the cloned nodes. |
32671 // Note: We only need the first/last node of the cloned nodes. |
31948 // However, we need to keep the reference to the jqlite wrapper as it might be changed later |
32672 // However, we need to keep the reference to the jqlite wrapper as it might be changed later |
31949 // by a directive with templateUrl when its template arrives. |
32673 // by a directive with templateUrl when its template arrives. |
31952 }; |
32676 }; |
31953 $animate.enter(clone, $element.parent(), $element); |
32677 $animate.enter(clone, $element.parent(), $element); |
31954 }); |
32678 }); |
31955 } |
32679 } |
31956 } else { |
32680 } else { |
31957 if(previousElements) { |
32681 if (previousElements) { |
31958 previousElements.remove(); |
32682 previousElements.remove(); |
31959 previousElements = null; |
32683 previousElements = null; |
31960 } |
32684 } |
31961 if(childScope) { |
32685 if (childScope) { |
31962 childScope.$destroy(); |
32686 childScope.$destroy(); |
31963 childScope = null; |
32687 childScope = null; |
31964 } |
32688 } |
31965 if(block) { |
32689 if (block) { |
31966 previousElements = getBlockNodes(block.clone); |
32690 previousElements = getBlockNodes(block.clone); |
31967 $animate.leave(previousElements).then(function() { |
32691 $animate.leave(previousElements).then(function() { |
31968 previousElements = null; |
32692 previousElements = null; |
31969 }); |
32693 }); |
31970 block = null; |
32694 block = null; |
31982 * |
32706 * |
31983 * @description |
32707 * @description |
31984 * Fetches, compiles and includes an external HTML fragment. |
32708 * Fetches, compiles and includes an external HTML fragment. |
31985 * |
32709 * |
31986 * By default, the template URL is restricted to the same domain and protocol as the |
32710 * By default, the template URL is restricted to the same domain and protocol as the |
31987 * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl |
32711 * application document. This is done by calling {@link $sce#getTrustedResourceUrl |
31988 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols |
32712 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols |
31989 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or |
32713 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or |
31990 * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link |
32714 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link |
31991 * ng.$sce Strict Contextual Escaping}. |
32715 * ng.$sce Strict Contextual Escaping}. |
31992 * |
32716 * |
31993 * In addition, the browser's |
32717 * In addition, the browser's |
31994 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) |
32718 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) |
31995 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) |
32719 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) |
32171 currentScope, |
32895 currentScope, |
32172 previousElement, |
32896 previousElement, |
32173 currentElement; |
32897 currentElement; |
32174 |
32898 |
32175 var cleanupLastIncludeContent = function() { |
32899 var cleanupLastIncludeContent = function() { |
32176 if(previousElement) { |
32900 if (previousElement) { |
32177 previousElement.remove(); |
32901 previousElement.remove(); |
32178 previousElement = null; |
32902 previousElement = null; |
32179 } |
32903 } |
32180 if(currentScope) { |
32904 if (currentScope) { |
32181 currentScope.$destroy(); |
32905 currentScope.$destroy(); |
32182 currentScope = null; |
32906 currentScope = null; |
32183 } |
32907 } |
32184 if(currentElement) { |
32908 if (currentElement) { |
32185 $animate.leave(currentElement).then(function() { |
32909 $animate.leave(currentElement).then(function() { |
32186 previousElement = null; |
32910 previousElement = null; |
32187 }); |
32911 }); |
32188 previousElement = currentElement; |
32912 previousElement = currentElement; |
32189 currentElement = null; |
32913 currentElement = null; |
32257 // specially. |
32981 // specially. |
32258 $element.empty(); |
32982 $element.empty(); |
32259 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope, |
32983 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope, |
32260 function namespaceAdaptedClone(clone) { |
32984 function namespaceAdaptedClone(clone) { |
32261 $element.append(clone); |
32985 $element.append(clone); |
32262 }, undefined, undefined, $element); |
32986 }, {futureParentElement: $element}); |
32263 return; |
32987 return; |
32264 } |
32988 } |
32265 |
32989 |
32266 $element.html(ctrl.template); |
32990 $element.html(ctrl.template); |
32267 $compile($element.contents())(scope); |
32991 $compile($element.contents())(scope); |
32541 }); |
33265 }); |
32542 </file> |
33266 </file> |
32543 </example> |
33267 </example> |
32544 */ |
33268 */ |
32545 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { |
33269 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { |
32546 var BRACE = /{}/g; |
33270 var BRACE = /{}/g, |
33271 IS_WHEN = /^when(Minus)?(.+)$/; |
|
33272 |
|
32547 return { |
33273 return { |
32548 restrict: 'EA', |
33274 restrict: 'EA', |
32549 link: function(scope, element, attr) { |
33275 link: function(scope, element, attr) { |
32550 var numberExp = attr.count, |
33276 var numberExp = attr.count, |
32551 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs |
33277 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs |
32552 offset = attr.offset || 0, |
33278 offset = attr.offset || 0, |
32553 whens = scope.$eval(whenExp) || {}, |
33279 whens = scope.$eval(whenExp) || {}, |
32554 whensExpFns = {}, |
33280 whensExpFns = {}, |
32555 startSymbol = $interpolate.startSymbol(), |
33281 startSymbol = $interpolate.startSymbol(), |
32556 endSymbol = $interpolate.endSymbol(), |
33282 endSymbol = $interpolate.endSymbol(), |
32557 isWhen = /^when(Minus)?(.+)$/; |
33283 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol, |
33284 watchRemover = angular.noop, |
|
33285 lastCount; |
|
32558 |
33286 |
32559 forEach(attr, function(expression, attributeName) { |
33287 forEach(attr, function(expression, attributeName) { |
32560 if (isWhen.test(attributeName)) { |
33288 var tmpMatch = IS_WHEN.exec(attributeName); |
32561 whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] = |
33289 if (tmpMatch) { |
32562 element.attr(attr.$attr[attributeName]); |
33290 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]); |
33291 whens[whenKey] = element.attr(attr.$attr[attributeName]); |
|
32563 } |
33292 } |
32564 }); |
33293 }); |
32565 forEach(whens, function(expression, key) { |
33294 forEach(whens, function(expression, key) { |
32566 whensExpFns[key] = |
33295 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement)); |
32567 $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + |
33296 |
32568 offset + endSymbol)); |
|
32569 }); |
33297 }); |
32570 |
33298 |
32571 scope.$watch(function ngPluralizeWatch() { |
33299 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { |
32572 var value = parseFloat(scope.$eval(numberExp)); |
33300 var count = parseFloat(newVal); |
32573 |
33301 var countIsNaN = isNaN(count); |
32574 if (!isNaN(value)) { |
33302 |
32575 //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, |
33303 if (!countIsNaN && !(count in whens)) { |
32576 //check it against pluralization rules in $locale service |
33304 // If an explicit number rule such as 1, 2, 3... is defined, just use it. |
32577 if (!(value in whens)) value = $locale.pluralCat(value - offset); |
33305 // Otherwise, check it against pluralization rules in $locale service. |
32578 return whensExpFns[value](scope); |
33306 count = $locale.pluralCat(count - offset); |
32579 } else { |
|
32580 return ''; |
|
32581 } |
33307 } |
32582 }, function ngPluralizeWatchAction(newVal) { |
33308 |
32583 element.text(newVal); |
33309 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. |
33310 // In JS `NaN !== NaN`, so we have to exlicitly check. |
|
33311 if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) { |
|
33312 watchRemover(); |
|
33313 watchRemover = scope.$watch(whensExpFns[count], updateElementText); |
|
33314 lastCount = count; |
|
33315 } |
|
32584 }); |
33316 }); |
33317 |
|
33318 function updateElementText(newText) { |
|
33319 element.text(newText || ''); |
|
33320 } |
|
32585 } |
33321 } |
32586 }; |
33322 }; |
32587 }]; |
33323 }]; |
32588 |
33324 |
32589 /** |
33325 /** |
32684 * before specifying a tracking expression. |
33420 * before specifying a tracking expression. |
32685 * |
33421 * |
32686 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements |
33422 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements |
32687 * will be associated by item identity in the array. |
33423 * will be associated by item identity in the array. |
32688 * |
33424 * |
32689 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the |
|
32690 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message |
|
32691 * when a filter is active on the repeater, but the filtered result set is empty. |
|
32692 * |
|
32693 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after |
|
32694 * the items have been processed through the filter. |
|
32695 * |
|
32696 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique |
33425 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique |
32697 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements |
33426 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements |
32698 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM |
33427 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM |
32699 * element in the same way in the DOM. |
33428 * element in the same way in the DOM. |
32700 * |
33429 * |
32702 * case the object identity does not matter. Two objects are considered equivalent as long as their `id` |
33431 * case the object identity does not matter. Two objects are considered equivalent as long as their `id` |
32703 * property is same. |
33432 * property is same. |
32704 * |
33433 * |
32705 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter |
33434 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter |
32706 * to items in conjunction with a tracking expression. |
33435 * to items in conjunction with a tracking expression. |
33436 * |
|
33437 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the |
|
33438 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message |
|
33439 * when a filter is active on the repeater, but the filtered result set is empty. |
|
33440 * |
|
33441 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after |
|
33442 * the items have been processed through the filter. |
|
32707 * |
33443 * |
32708 * @example |
33444 * @example |
32709 * This example initializes the scope to a list of names and |
33445 * This example initializes the scope to a list of names and |
32710 * then uses `ngRepeat` to display every person: |
33446 * then uses `ngRepeat` to display every person: |
32711 <example module="ngAnimate" deps="angular-animate.js" animations="true"> |
33447 <example module="ngAnimate" deps="angular-animate.js" animations="true"> |
32841 var lhs = match[1]; |
33577 var lhs = match[1]; |
32842 var rhs = match[2]; |
33578 var rhs = match[2]; |
32843 var aliasAs = match[3]; |
33579 var aliasAs = match[3]; |
32844 var trackByExp = match[4]; |
33580 var trackByExp = match[4]; |
32845 |
33581 |
32846 match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); |
33582 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/); |
32847 |
33583 |
32848 if (!match) { |
33584 if (!match) { |
32849 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", |
33585 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", |
32850 lhs); |
33586 lhs); |
32851 } |
33587 } |
32862 var hashFnLocals = {$id: hashKey}; |
33598 var hashFnLocals = {$id: hashKey}; |
32863 |
33599 |
32864 if (trackByExp) { |
33600 if (trackByExp) { |
32865 trackByExpGetter = $parse(trackByExp); |
33601 trackByExpGetter = $parse(trackByExp); |
32866 } else { |
33602 } else { |
32867 trackByIdArrayFn = function (key, value) { |
33603 trackByIdArrayFn = function(key, value) { |
32868 return hashKey(value); |
33604 return hashKey(value); |
32869 }; |
33605 }; |
32870 trackByIdObjFn = function (key) { |
33606 trackByIdObjFn = function(key) { |
32871 return key; |
33607 return key; |
32872 }; |
33608 }; |
32873 } |
33609 } |
32874 |
33610 |
32875 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) { |
33611 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) { |
32945 delete lastBlockMap[trackById]; |
33681 delete lastBlockMap[trackById]; |
32946 nextBlockMap[trackById] = block; |
33682 nextBlockMap[trackById] = block; |
32947 nextBlockOrder[index] = block; |
33683 nextBlockOrder[index] = block; |
32948 } else if (nextBlockMap[trackById]) { |
33684 } else if (nextBlockMap[trackById]) { |
32949 // if collision detected. restore lastBlockMap and throw an error |
33685 // if collision detected. restore lastBlockMap and throw an error |
32950 forEach(nextBlockOrder, function (block) { |
33686 forEach(nextBlockOrder, function(block) { |
32951 if (block && block.scope) lastBlockMap[block.id] = block; |
33687 if (block && block.scope) lastBlockMap[block.id] = block; |
32952 }); |
33688 }); |
32953 throw ngRepeatMinErr('dupes', |
33689 throw ngRepeatMinErr('dupes', |
32954 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", |
33690 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", |
32955 expression, trackById, toJson(value)); |
33691 expression, trackById, value); |
32956 } else { |
33692 } else { |
32957 // new never before seen block |
33693 // new never before seen block |
32958 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; |
33694 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; |
32959 nextBlockMap[trackById] = true; |
33695 nextBlockMap[trackById] = true; |
32960 } |
33696 } |
33061 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the |
33797 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the |
33062 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. |
33798 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. |
33063 * |
33799 * |
33064 * ### Overriding `.ng-hide` |
33800 * ### Overriding `.ng-hide` |
33065 * |
33801 * |
33066 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change |
33802 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change |
33067 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` |
33803 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` |
33068 * class in CSS: |
33804 * class in CSS: |
33069 * |
33805 * |
33070 * ```css |
33806 * ```css |
33071 * .ng-hide { |
33807 * .ng-hide { |
33072 * /* this is just another form of hiding an element */ |
33808 * /* this is just another form of hiding an element */ |
33073 * display:block!important; |
33809 * display: block!important; |
33074 * position:absolute; |
33810 * position: absolute; |
33075 * top:-9999px; |
33811 * top: -9999px; |
33076 * left:-9999px; |
33812 * left: -9999px; |
33077 * } |
33813 * } |
33078 * ``` |
33814 * ``` |
33079 * |
33815 * |
33080 * By default you don't need to override in CSS anything and the animations will work around the display style. |
33816 * By default you don't need to override in CSS anything and the animations will work around the display style. |
33081 * |
33817 * |
33091 * //a working example can be found at the bottom of this page |
33827 * //a working example can be found at the bottom of this page |
33092 * // |
33828 * // |
33093 * .my-element.ng-hide-add, .my-element.ng-hide-remove { |
33829 * .my-element.ng-hide-add, .my-element.ng-hide-remove { |
33094 * /* this is required as of 1.3x to properly |
33830 * /* this is required as of 1.3x to properly |
33095 * apply all styling in a show/hide animation */ |
33831 * apply all styling in a show/hide animation */ |
33096 * transition:0s linear all; |
33832 * transition: 0s linear all; |
33097 * } |
33833 * } |
33098 * |
33834 * |
33099 * .my-element.ng-hide-add-active, |
33835 * .my-element.ng-hide-add-active, |
33100 * .my-element.ng-hide-remove-active { |
33836 * .my-element.ng-hide-remove-active { |
33101 * /* the transition is defined in the active class */ |
33837 * /* the transition is defined in the active class */ |
33102 * transition:1s linear all; |
33838 * transition: 1s linear all; |
33103 * } |
33839 * } |
33104 * |
33840 * |
33105 * .my-element.ng-hide-add { ... } |
33841 * .my-element.ng-hide-add { ... } |
33106 * .my-element.ng-hide-add.ng-hide-add-active { ... } |
33842 * .my-element.ng-hide-add.ng-hide-add-active { ... } |
33107 * .my-element.ng-hide-remove { ... } |
33843 * .my-element.ng-hide-remove { ... } |
33135 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. |
33871 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. |
33136 </div> |
33872 </div> |
33137 </div> |
33873 </div> |
33138 </file> |
33874 </file> |
33139 <file name="glyphicons.css"> |
33875 <file name="glyphicons.css"> |
33140 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); |
33876 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); |
33141 </file> |
33877 </file> |
33142 <file name="animations.css"> |
33878 <file name="animations.css"> |
33143 .animate-show { |
33879 .animate-show { |
33144 line-height:20px; |
33880 line-height: 20px; |
33145 opacity:1; |
33881 opacity: 1; |
33146 padding:10px; |
33882 padding: 10px; |
33147 border:1px solid black; |
33883 border: 1px solid black; |
33148 background:white; |
33884 background: white; |
33149 } |
33885 } |
33150 |
33886 |
33151 .animate-show.ng-hide-add.ng-hide-add-active, |
33887 .animate-show.ng-hide-add.ng-hide-add-active, |
33152 .animate-show.ng-hide-remove.ng-hide-remove-active { |
33888 .animate-show.ng-hide-remove.ng-hide-remove-active { |
33153 -webkit-transition:all linear 0.5s; |
33889 -webkit-transition: all linear 0.5s; |
33154 transition:all linear 0.5s; |
33890 transition: all linear 0.5s; |
33155 } |
33891 } |
33156 |
33892 |
33157 .animate-show.ng-hide { |
33893 .animate-show.ng-hide { |
33158 line-height:0; |
33894 line-height: 0; |
33159 opacity:0; |
33895 opacity: 0; |
33160 padding:0 10px; |
33896 padding: 0 10px; |
33161 } |
33897 } |
33162 |
33898 |
33163 .check-element { |
33899 .check-element { |
33164 padding:10px; |
33900 padding: 10px; |
33165 border:1px solid black; |
33901 border: 1px solid black; |
33166 background:white; |
33902 background: white; |
33167 } |
33903 } |
33168 </file> |
33904 </file> |
33169 <file name="protractor.js" type="protractor"> |
33905 <file name="protractor.js" type="protractor"> |
33170 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); |
33906 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); |
33171 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); |
33907 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); |
33185 var ngShowDirective = ['$animate', function($animate) { |
33921 var ngShowDirective = ['$animate', function($animate) { |
33186 return { |
33922 return { |
33187 restrict: 'A', |
33923 restrict: 'A', |
33188 multiElement: true, |
33924 multiElement: true, |
33189 link: function(scope, element, attr) { |
33925 link: function(scope, element, attr) { |
33190 scope.$watch(attr.ngShow, function ngShowWatchAction(value){ |
33926 scope.$watch(attr.ngShow, function ngShowWatchAction(value) { |
33191 // we're adding a temporary, animation-specific class for ng-hide since this way |
33927 // we're adding a temporary, animation-specific class for ng-hide since this way |
33192 // we can control when the element is actually displayed on screen without having |
33928 // we can control when the element is actually displayed on screen without having |
33193 // to have a global/greedy CSS selector that breaks when other animations are run. |
33929 // to have a global/greedy CSS selector that breaks when other animations are run. |
33194 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 |
33930 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 |
33195 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS); |
33931 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, { |
33932 tempClasses: NG_HIDE_IN_PROGRESS_CLASS |
|
33933 }); |
|
33196 }); |
33934 }); |
33197 } |
33935 } |
33198 }; |
33936 }; |
33199 }]; |
33937 }]; |
33200 |
33938 |
33233 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the |
33971 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the |
33234 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. |
33972 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. |
33235 * |
33973 * |
33236 * ### Overriding `.ng-hide` |
33974 * ### Overriding `.ng-hide` |
33237 * |
33975 * |
33238 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change |
33976 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change |
33239 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` |
33977 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` |
33240 * class in CSS: |
33978 * class in CSS: |
33241 * |
33979 * |
33242 * ```css |
33980 * ```css |
33243 * .ng-hide { |
33981 * .ng-hide { |
33244 * /* this is just another form of hiding an element */ |
33982 * /* this is just another form of hiding an element */ |
33245 * display:block!important; |
33983 * display: block!important; |
33246 * position:absolute; |
33984 * position: absolute; |
33247 * top:-9999px; |
33985 * top: -9999px; |
33248 * left:-9999px; |
33986 * left: -9999px; |
33249 * } |
33987 * } |
33250 * ``` |
33988 * ``` |
33251 * |
33989 * |
33252 * By default you don't need to override in CSS anything and the animations will work around the display style. |
33990 * By default you don't need to override in CSS anything and the animations will work around the display style. |
33253 * |
33991 * |
33260 * ```css |
33998 * ```css |
33261 * // |
33999 * // |
33262 * //a working example can be found at the bottom of this page |
34000 * //a working example can be found at the bottom of this page |
33263 * // |
34001 * // |
33264 * .my-element.ng-hide-add, .my-element.ng-hide-remove { |
34002 * .my-element.ng-hide-add, .my-element.ng-hide-remove { |
33265 * transition:0.5s linear all; |
34003 * transition: 0.5s linear all; |
33266 * } |
34004 * } |
33267 * |
34005 * |
33268 * .my-element.ng-hide-add { ... } |
34006 * .my-element.ng-hide-add { ... } |
33269 * .my-element.ng-hide-add.ng-hide-add-active { ... } |
34007 * .my-element.ng-hide-add.ng-hide-add-active { ... } |
33270 * .my-element.ng-hide-remove { ... } |
34008 * .my-element.ng-hide-remove { ... } |
33298 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. |
34036 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. |
33299 </div> |
34037 </div> |
33300 </div> |
34038 </div> |
33301 </file> |
34039 </file> |
33302 <file name="glyphicons.css"> |
34040 <file name="glyphicons.css"> |
33303 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); |
34041 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); |
33304 </file> |
34042 </file> |
33305 <file name="animations.css"> |
34043 <file name="animations.css"> |
33306 .animate-hide { |
34044 .animate-hide { |
33307 -webkit-transition:all linear 0.5s; |
34045 -webkit-transition: all linear 0.5s; |
33308 transition:all linear 0.5s; |
34046 transition: all linear 0.5s; |
33309 line-height:20px; |
34047 line-height: 20px; |
33310 opacity:1; |
34048 opacity: 1; |
33311 padding:10px; |
34049 padding: 10px; |
33312 border:1px solid black; |
34050 border: 1px solid black; |
33313 background:white; |
34051 background: white; |
33314 } |
34052 } |
33315 |
34053 |
33316 .animate-hide.ng-hide { |
34054 .animate-hide.ng-hide { |
33317 line-height:0; |
34055 line-height: 0; |
33318 opacity:0; |
34056 opacity: 0; |
33319 padding:0 10px; |
34057 padding: 0 10px; |
33320 } |
34058 } |
33321 |
34059 |
33322 .check-element { |
34060 .check-element { |
33323 padding:10px; |
34061 padding: 10px; |
33324 border:1px solid black; |
34062 border: 1px solid black; |
33325 background:white; |
34063 background: white; |
33326 } |
34064 } |
33327 </file> |
34065 </file> |
33328 <file name="protractor.js" type="protractor"> |
34066 <file name="protractor.js" type="protractor"> |
33329 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); |
34067 var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); |
33330 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); |
34068 var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); |
33344 var ngHideDirective = ['$animate', function($animate) { |
34082 var ngHideDirective = ['$animate', function($animate) { |
33345 return { |
34083 return { |
33346 restrict: 'A', |
34084 restrict: 'A', |
33347 multiElement: true, |
34085 multiElement: true, |
33348 link: function(scope, element, attr) { |
34086 link: function(scope, element, attr) { |
33349 scope.$watch(attr.ngHide, function ngHideWatchAction(value){ |
34087 scope.$watch(attr.ngHide, function ngHideWatchAction(value) { |
33350 // The comment inside of the ngShowDirective explains why we add and |
34088 // The comment inside of the ngShowDirective explains why we add and |
33351 // remove a temporary class for the show/hide animation |
34089 // remove a temporary class for the show/hide animation |
33352 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, NG_HIDE_IN_PROGRESS_CLASS); |
34090 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, { |
34091 tempClasses: NG_HIDE_IN_PROGRESS_CLASS |
|
34092 }); |
|
33353 }); |
34093 }); |
33354 } |
34094 } |
33355 }; |
34095 }; |
33356 }]; |
34096 }]; |
33357 |
34097 |
33647 $scope.title = 'Lorem Ipsum'; |
34387 $scope.title = 'Lorem Ipsum'; |
33648 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; |
34388 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; |
33649 }]); |
34389 }]); |
33650 </script> |
34390 </script> |
33651 <div ng-controller="ExampleController"> |
34391 <div ng-controller="ExampleController"> |
33652 <input ng-model="title"><br> |
34392 <input ng-model="title"> <br/> |
33653 <textarea ng-model="text"></textarea> <br/> |
34393 <textarea ng-model="text"></textarea> <br/> |
33654 <pane title="{{title}}">{{text}}</pane> |
34394 <pane title="{{title}}">{{text}}</pane> |
33655 </div> |
34395 </div> |
33656 </file> |
34396 </file> |
33657 <file name="protractor.js" type="protractor"> |
34397 <file name="protractor.js" type="protractor"> |
33725 restrict: 'E', |
34465 restrict: 'E', |
33726 terminal: true, |
34466 terminal: true, |
33727 compile: function(element, attr) { |
34467 compile: function(element, attr) { |
33728 if (attr.type == 'text/ng-template') { |
34468 if (attr.type == 'text/ng-template') { |
33729 var templateUrl = attr.id, |
34469 var templateUrl = attr.id, |
33730 // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent |
|
33731 text = element[0].text; |
34470 text = element[0].text; |
33732 |
34471 |
33733 $templateCache.put(templateUrl, text); |
34472 $templateCache.put(templateUrl, text); |
33734 } |
34473 } |
33735 } |
34474 } |
33747 * |
34486 * |
33748 * # `ngOptions` |
34487 * # `ngOptions` |
33749 * |
34488 * |
33750 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` |
34489 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` |
33751 * elements for the `<select>` element using the array or object obtained by evaluating the |
34490 * elements for the `<select>` element using the array or object obtained by evaluating the |
33752 * `ngOptions` comprehension_expression. |
34491 * `ngOptions` comprehension expression. |
34492 * |
|
34493 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a |
|
34494 * similar result. However, `ngOptions` provides some benefits such as reducing memory and |
|
34495 * increasing speed by not creating a new scope for each repeated instance, as well as providing |
|
34496 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the |
|
34497 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound |
|
34498 * to a non-string value. This is because an option element can only be bound to string values at |
|
34499 * present. |
|
33753 * |
34500 * |
33754 * When an item in the `<select>` menu is selected, the array element or object property |
34501 * When an item in the `<select>` menu is selected, the array element or object property |
33755 * represented by the selected option will be bound to the model identified by the `ngModel` |
34502 * represented by the selected option will be bound to the model identified by the `ngModel` |
33756 * directive. |
34503 * directive. |
34504 * |
|
34505 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can |
|
34506 * be nested into the `<select>` element. This element will then represent the `null` or "not selected" |
|
34507 * option. See example below for demonstration. |
|
33757 * |
34508 * |
33758 * <div class="alert alert-warning"> |
34509 * <div class="alert alert-warning"> |
33759 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an |
34510 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an |
33760 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). |
34511 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). |
33761 * </div> |
34512 * </div> |
33762 * |
34513 * |
33763 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can |
34514 * ## `select` **`as`** |
33764 * be nested into the `<select>` element. This element will then represent the `null` or "not selected" |
34515 * |
33765 * option. See example below for demonstration. |
34516 * Using `select` **`as`** will bind the result of the `select` expression to the model, but |
34517 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) |
|
34518 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression |
|
34519 * is used, the result of that expression will be set as the value of the `option` and `select` elements. |
|
34520 * |
|
34521 * |
|
34522 * ### `select` **`as`** and **`track by`** |
|
33766 * |
34523 * |
33767 * <div class="alert alert-warning"> |
34524 * <div class="alert alert-warning"> |
33768 * **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead |
34525 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together. |
33769 * of {@link ng.directive:ngRepeat ngRepeat} when you want the |
|
33770 * `select` model to be bound to a non-string value. This is because an option element can only |
|
33771 * be bound to string values at present. |
|
33772 * </div> |
34526 * </div> |
33773 * |
34527 * |
33774 * <div class="alert alert-info"> |
34528 * Consider the following example: |
33775 * **Note:** Using `select as` will bind the result of the `select as` expression to the model, but |
34529 * |
33776 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) |
34530 * ```html |
33777 * or property name (for object data sources) of the value within the collection. |
34531 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"> |
33778 * </div> |
34532 * ``` |
34533 * |
|
34534 * ```js |
|
34535 * $scope.values = [{ |
|
34536 * id: 1, |
|
34537 * label: 'aLabel', |
|
34538 * subItem: { name: 'aSubItem' } |
|
34539 * }, { |
|
34540 * id: 2, |
|
34541 * label: 'bLabel', |
|
34542 * subItem: { name: 'bSubItem' } |
|
34543 * }]; |
|
34544 * |
|
34545 * $scope.selected = { name: 'aSubItem' }; |
|
34546 * ``` |
|
34547 * |
|
34548 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element |
|
34549 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the |
|
34550 * following: |
|
34551 * |
|
34552 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]` |
|
34553 * 2. Apply **`track by`** to the already selected value in `ngModel`. |
|
34554 * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected |
|
34555 * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to |
|
34556 * a wrong object, the selected element can't be found, `<select>` is always reset to the "not |
|
34557 * selected" option. |
|
34558 * |
|
33779 * |
34559 * |
33780 * @param {string} ngModel Assignable angular expression to data-bind to. |
34560 * @param {string} ngModel Assignable angular expression to data-bind to. |
33781 * @param {string=} name Property name of the form under which the control is published. |
34561 * @param {string=} name Property name of the form under which the control is published. |
33782 * @param {string=} required The control is considered valid only if value is entered. |
34562 * @param {string=} required The control is considered valid only if value is entered. |
33783 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
34563 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to |
33786 * @param {comprehension_expression=} ngOptions in one of the following forms: |
34566 * @param {comprehension_expression=} ngOptions in one of the following forms: |
33787 * |
34567 * |
33788 * * for array data sources: |
34568 * * for array data sources: |
33789 * * `label` **`for`** `value` **`in`** `array` |
34569 * * `label` **`for`** `value` **`in`** `array` |
33790 * * `select` **`as`** `label` **`for`** `value` **`in`** `array` |
34570 * * `select` **`as`** `label` **`for`** `value` **`in`** `array` |
33791 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` |
34571 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` |
33792 * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` |
34572 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` |
34573 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr` |
|
34574 * (for including a filter with `track by`) |
|
33793 * * for object data sources: |
34575 * * for object data sources: |
33794 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` |
34576 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` |
33795 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` |
34577 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` |
33796 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` |
34578 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` |
33797 * * `select` **`as`** `label` **`group by`** `group` |
34579 * * `select` **`as`** `label` **`group by`** `group` |
33811 * DOM element. |
34593 * DOM element. |
33812 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be |
34594 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be |
33813 * used to identify the objects in the array. The `trackexpr` will most likely refer to the |
34595 * used to identify the objects in the array. The `trackexpr` will most likely refer to the |
33814 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved |
34596 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved |
33815 * even when the options are recreated (e.g. reloaded from the server). |
34597 * even when the options are recreated (e.g. reloaded from the server). |
33816 |
|
33817 * <div class="alert alert-info"> |
|
33818 * **Note:** Using `select as` together with `trackexpr` is not possible (and will throw). |
|
33819 * Reasoning: |
|
33820 * - Example: <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"> |
|
33821 * values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItemß'}}], |
|
33822 * $scope.selected = {name: 'aSubItem'}; |
|
33823 * - track by is always applied to `value`, with purpose to preserve the selection, |
|
33824 * (to `item` in this case) |
|
33825 * - to calculate whether an item is selected we do the following: |
|
33826 * 1. apply `track by` to the values in the array, e.g. |
|
33827 * In the example: [1,2] |
|
33828 * 2. apply `track by` to the already selected value in `ngModel`: |
|
33829 * In the example: this is not possible, as `track by` refers to `item.id`, but the selected |
|
33830 * value from `ngModel` is `{name: aSubItem}`. |
|
33831 * |
|
33832 * </div> |
|
33833 * |
34598 * |
33834 * @example |
34599 * @example |
33835 <example module="selectExample"> |
34600 <example module="selectExample"> |
33836 <file name="index.html"> |
34601 <file name="index.html"> |
33837 <script> |
34602 <script> |
33937 if (unknownOption.parent()) unknownOption.remove(); |
34702 if (unknownOption.parent()) unknownOption.remove(); |
33938 } |
34703 } |
33939 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459 |
34704 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459 |
33940 // Adding an <option selected="selected"> element to a <select required="required"> should |
34705 // Adding an <option selected="selected"> element to a <select required="required"> should |
33941 // automatically select the new element |
34706 // automatically select the new element |
33942 if (element[0].hasAttribute('selected')) { |
34707 if (element && element[0].hasAttribute('selected')) { |
33943 element[0].selected = true; |
34708 element[0].selected = true; |
33944 } |
34709 } |
33945 }; |
34710 }; |
33946 |
34711 |
33947 |
34712 |
33948 self.removeOption = function(value) { |
34713 self.removeOption = function(value) { |
33949 if (this.hasOption(value)) { |
34714 if (this.hasOption(value)) { |
33950 delete optionsMap[value]; |
34715 delete optionsMap[value]; |
33951 if (ngModelCtrl.$viewValue == value) { |
34716 if (ngModelCtrl.$viewValue === value) { |
33952 this.renderUnknownOption(value); |
34717 this.renderUnknownOption(value); |
33953 } |
34718 } |
33954 } |
34719 } |
33955 }; |
34720 }; |
33956 |
34721 |
33990 optionTemplate = jqLite(document.createElement('option')), |
34755 optionTemplate = jqLite(document.createElement('option')), |
33991 optGroupTemplate =jqLite(document.createElement('optgroup')), |
34756 optGroupTemplate =jqLite(document.createElement('optgroup')), |
33992 unknownOption = optionTemplate.clone(); |
34757 unknownOption = optionTemplate.clone(); |
33993 |
34758 |
33994 // find "null" option |
34759 // find "null" option |
33995 for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) { |
34760 for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) { |
33996 if (children[i].value === '') { |
34761 if (children[i].value === '') { |
33997 emptyOption = nullOption = children.eq(i); |
34762 emptyOption = nullOption = children.eq(i); |
33998 break; |
34763 break; |
33999 } |
34764 } |
34000 } |
34765 } |
34092 groupByFn = $parse(match[3] || ''), |
34857 groupByFn = $parse(match[3] || ''), |
34093 valueFn = $parse(match[2] ? match[1] : valueName), |
34858 valueFn = $parse(match[2] ? match[1] : valueName), |
34094 valuesFn = $parse(match[7]), |
34859 valuesFn = $parse(match[7]), |
34095 track = match[8], |
34860 track = match[8], |
34096 trackFn = track ? $parse(match[8]) : null, |
34861 trackFn = track ? $parse(match[8]) : null, |
34862 trackKeysCache = {}, |
|
34097 // This is an array of array of existing option groups in DOM. |
34863 // This is an array of array of existing option groups in DOM. |
34098 // We try to reuse these if possible |
34864 // We try to reuse these if possible |
34099 // - optionGroupsCache[0] is the options with no option group |
34865 // - optionGroupsCache[0] is the options with no option group |
34100 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element |
34866 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element |
34101 optionGroupsCache = [[{element: selectElement, label:''}]], |
34867 optionGroupsCache = [[{element: selectElement, label:''}]], |
34102 //re-usable object to represent option's locals |
34868 //re-usable object to represent option's locals |
34103 locals = {}; |
34869 locals = {}; |
34104 |
34870 |
34105 if (trackFn && selectAsFn) { |
|
34106 throw ngOptionsMinErr('trkslct', |
|
34107 "Comprehension expression cannot contain both selectAs '{0}' " + |
|
34108 "and trackBy '{1}' expressions.", |
|
34109 selectAs, track); |
|
34110 } |
|
34111 |
|
34112 if (nullOption) { |
34871 if (nullOption) { |
34113 // compile the element since there might be bindings in it |
34872 // compile the element since there might be bindings in it |
34114 $compile(nullOption)(scope); |
34873 $compile(nullOption)(scope); |
34115 |
34874 |
34116 // remove the class, which is added automatically because we recompile the element and it |
34875 // remove the class, which is added automatically because we recompile the element and it |
34144 return exprFn(scope, locals); |
34903 return exprFn(scope, locals); |
34145 } |
34904 } |
34146 |
34905 |
34147 function selectionChanged() { |
34906 function selectionChanged() { |
34148 scope.$apply(function() { |
34907 scope.$apply(function() { |
34149 var optionGroup, |
34908 var collection = valuesFn(scope) || []; |
34150 collection = valuesFn(scope) || [], |
|
34151 key, value, optionElement, index, groupIndex, length, groupLength, trackIndex; |
|
34152 var viewValue; |
34909 var viewValue; |
34153 if (multiple) { |
34910 if (multiple) { |
34154 viewValue = []; |
34911 viewValue = []; |
34155 forEach(selectElement.val(), function(selectedKey) { |
34912 forEach(selectElement.val(), function(selectedKey) { |
34913 selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey; |
|
34156 viewValue.push(getViewValue(selectedKey, collection[selectedKey])); |
34914 viewValue.push(getViewValue(selectedKey, collection[selectedKey])); |
34157 }); |
34915 }); |
34158 } else { |
34916 } else { |
34159 var selectedKey = selectElement.val(); |
34917 var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val(); |
34160 viewValue = getViewValue(selectedKey, collection[selectedKey]); |
34918 viewValue = getViewValue(selectedKey, collection[selectedKey]); |
34161 } |
34919 } |
34162 ctrl.$setViewValue(viewValue); |
34920 ctrl.$setViewValue(viewValue); |
34163 render(); |
34921 render(); |
34164 }); |
34922 }); |
34197 } |
34955 } |
34198 |
34956 |
34199 function createIsSelectedFn(viewValue) { |
34957 function createIsSelectedFn(viewValue) { |
34200 var selectedSet; |
34958 var selectedSet; |
34201 if (multiple) { |
34959 if (multiple) { |
34202 if (!selectAs && trackFn && isArray(viewValue)) { |
34960 if (trackFn && isArray(viewValue)) { |
34203 |
34961 |
34204 selectedSet = new HashMap([]); |
34962 selectedSet = new HashMap([]); |
34205 for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) { |
34963 for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) { |
34206 // tracking by key |
34964 // tracking by key |
34207 selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true); |
34965 selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true); |
34208 } |
34966 } |
34209 } else { |
34967 } else { |
34210 selectedSet = new HashMap(viewValue); |
34968 selectedSet = new HashMap(viewValue); |
34211 } |
34969 } |
34212 } else if (!selectAsFn && trackFn) { |
34970 } else if (trackFn) { |
34213 viewValue = callExpression(trackFn, null, viewValue); |
34971 viewValue = callExpression(trackFn, null, viewValue); |
34214 } |
34972 } |
34973 |
|
34215 return function isSelected(key, value) { |
34974 return function isSelected(key, value) { |
34216 var compareValueFn; |
34975 var compareValueFn; |
34217 if (selectAsFn) { |
34976 if (trackFn) { |
34977 compareValueFn = trackFn; |
|
34978 } else if (selectAsFn) { |
|
34218 compareValueFn = selectAsFn; |
34979 compareValueFn = selectAsFn; |
34219 } else if (trackFn) { |
|
34220 compareValueFn = trackFn; |
|
34221 } else { |
34980 } else { |
34222 compareValueFn = valueFn; |
34981 compareValueFn = valueFn; |
34223 } |
34982 } |
34224 |
34983 |
34225 if (multiple) { |
34984 if (multiple) { |
34226 return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value))); |
34985 return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value))); |
34227 } else { |
34986 } else { |
34228 return viewValue == callExpression(compareValueFn, key, value); |
34987 return viewValue === callExpression(compareValueFn, key, value); |
34229 } |
34988 } |
34230 }; |
34989 }; |
34231 } |
34990 } |
34232 |
34991 |
34233 function scheduleRendering() { |
34992 function scheduleRendering() { |
34234 if (!renderScheduled) { |
34993 if (!renderScheduled) { |
34235 scope.$$postDigest(render); |
34994 scope.$$postDigest(render); |
34236 renderScheduled = true; |
34995 renderScheduled = true; |
34237 } |
34996 } |
34997 } |
|
34998 |
|
34999 /** |
|
35000 * A new labelMap is created with each render. |
|
35001 * This function is called for each existing option with added=false, |
|
35002 * and each new option with added=true. |
|
35003 * - Labels that are passed to this method twice, |
|
35004 * (once with added=true and once with added=false) will end up with a value of 0, and |
|
35005 * will cause no change to happen to the corresponding option. |
|
35006 * - Labels that are passed to this method only once with added=false will end up with a |
|
35007 * value of -1 and will eventually be passed to selectCtrl.removeOption() |
|
35008 * - Labels that are passed to this method only once with added=true will end up with a |
|
35009 * value of 1 and will eventually be passed to selectCtrl.addOption() |
|
35010 */ |
|
35011 function updateLabelMap(labelMap, label, added) { |
|
35012 labelMap[label] = labelMap[label] || 0; |
|
35013 labelMap[label] += (added ? 1 : -1); |
|
34238 } |
35014 } |
34239 |
35015 |
34240 function render() { |
35016 function render() { |
34241 renderScheduled = false; |
35017 renderScheduled = false; |
34242 |
35018 |
34252 keys = keyName ? sortedKeys(values) : values, |
35028 keys = keyName ? sortedKeys(values) : values, |
34253 key, |
35029 key, |
34254 value, |
35030 value, |
34255 groupLength, length, |
35031 groupLength, length, |
34256 groupIndex, index, |
35032 groupIndex, index, |
35033 labelMap = {}, |
|
34257 selected, |
35034 selected, |
34258 isSelected = createIsSelectedFn(viewValue), |
35035 isSelected = createIsSelectedFn(viewValue), |
34259 anySelected = false, |
35036 anySelected = false, |
34260 lastElement, |
35037 lastElement, |
34261 element, |
35038 element, |
34262 label; |
35039 label, |
35040 optionId; |
|
35041 |
|
35042 trackKeysCache = {}; |
|
34263 |
35043 |
34264 // We now build up the list of options we need (we merge later) |
35044 // We now build up the list of options we need (we merge later) |
34265 for (index = 0; length = keys.length, index < length; index++) { |
35045 for (index = 0; length = keys.length, index < length; index++) { |
34266 key = index; |
35046 key = index; |
34267 if (keyName) { |
35047 if (keyName) { |
34268 key = keys[index]; |
35048 key = keys[index]; |
34269 if ( key.charAt(0) === '$' ) continue; |
35049 if (key.charAt(0) === '$') continue; |
34270 } |
35050 } |
34271 value = values[key]; |
35051 value = values[key]; |
34272 |
35052 |
34273 optionGroupName = callExpression(groupByFn, key, value) || ''; |
35053 optionGroupName = callExpression(groupByFn, key, value) || ''; |
34274 if (!(optionGroup = optionGroups[optionGroupName])) { |
35054 if (!(optionGroup = optionGroups[optionGroupName])) { |
34281 |
35061 |
34282 label = callExpression(displayFn, key, value); // what will be seen by the user |
35062 label = callExpression(displayFn, key, value); // what will be seen by the user |
34283 |
35063 |
34284 // doing displayFn(scope, locals) || '' overwrites zero values |
35064 // doing displayFn(scope, locals) || '' overwrites zero values |
34285 label = isDefined(label) ? label : ''; |
35065 label = isDefined(label) ? label : ''; |
35066 optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index); |
|
35067 if (trackFn) { |
|
35068 trackKeysCache[optionId] = key; |
|
35069 } |
|
35070 |
|
34286 optionGroup.push({ |
35071 optionGroup.push({ |
34287 // either the index into array or key from object |
35072 // either the index into array or key from object |
34288 id: (keyName ? keys[index] : index), |
35073 id: optionId, |
34289 label: label, |
35074 label: label, |
34290 selected: selected // determine if we should be selected |
35075 selected: selected // determine if we should be selected |
34291 }); |
35076 }); |
34292 } |
35077 } |
34293 if (!multiple) { |
35078 if (!multiple) { |
34328 existingParent.element.attr('label', existingParent.label = optionGroupName); |
35113 existingParent.element.attr('label', existingParent.label = optionGroupName); |
34329 } |
35114 } |
34330 } |
35115 } |
34331 |
35116 |
34332 lastElement = null; // start at the beginning |
35117 lastElement = null; // start at the beginning |
34333 for(index = 0, length = optionGroup.length; index < length; index++) { |
35118 for (index = 0, length = optionGroup.length; index < length; index++) { |
34334 option = optionGroup[index]; |
35119 option = optionGroup[index]; |
34335 if ((existingOption = existingOptions[index+1])) { |
35120 if ((existingOption = existingOptions[index + 1])) { |
34336 // reuse elements |
35121 // reuse elements |
34337 lastElement = existingOption.element; |
35122 lastElement = existingOption.element; |
34338 if (existingOption.label !== option.label) { |
35123 if (existingOption.label !== option.label) { |
35124 updateLabelMap(labelMap, existingOption.label, false); |
|
35125 updateLabelMap(labelMap, option.label, true); |
|
34339 lastElement.text(existingOption.label = option.label); |
35126 lastElement.text(existingOption.label = option.label); |
35127 lastElement.prop('label', existingOption.label); |
|
34340 } |
35128 } |
34341 if (existingOption.id !== option.id) { |
35129 if (existingOption.id !== option.id) { |
34342 lastElement.val(existingOption.id = option.id); |
35130 lastElement.val(existingOption.id = option.id); |
34343 } |
35131 } |
34344 // lastElement.prop('selected') provided by jQuery has side-effects |
35132 // lastElement.prop('selected') provided by jQuery has side-effects |
34364 // rather then the element. |
35152 // rather then the element. |
34365 (element = optionTemplate.clone()) |
35153 (element = optionTemplate.clone()) |
34366 .val(option.id) |
35154 .val(option.id) |
34367 .prop('selected', option.selected) |
35155 .prop('selected', option.selected) |
34368 .attr('selected', option.selected) |
35156 .attr('selected', option.selected) |
35157 .prop('label', option.label) |
|
34369 .text(option.label); |
35158 .text(option.label); |
34370 } |
35159 } |
34371 |
35160 |
34372 existingOptions.push(existingOption = { |
35161 existingOptions.push(existingOption = { |
34373 element: element, |
35162 element: element, |
34374 label: option.label, |
35163 label: option.label, |
34375 id: option.id, |
35164 id: option.id, |
34376 selected: option.selected |
35165 selected: option.selected |
34377 }); |
35166 }); |
34378 selectCtrl.addOption(option.label, element); |
35167 updateLabelMap(labelMap, option.label, true); |
34379 if (lastElement) { |
35168 if (lastElement) { |
34380 lastElement.after(element); |
35169 lastElement.after(element); |
34381 } else { |
35170 } else { |
34382 existingParent.element.append(element); |
35171 existingParent.element.append(element); |
34383 } |
35172 } |
34384 lastElement = element; |
35173 lastElement = element; |
34385 } |
35174 } |
34386 } |
35175 } |
34387 // remove any excessive OPTIONs in a group |
35176 // remove any excessive OPTIONs in a group |
34388 index++; // increment since the existingOptions[0] is parent element not OPTION |
35177 index++; // increment since the existingOptions[0] is parent element not OPTION |
34389 while(existingOptions.length > index) { |
35178 while (existingOptions.length > index) { |
34390 option = existingOptions.pop(); |
35179 option = existingOptions.pop(); |
34391 selectCtrl.removeOption(option.label); |
35180 updateLabelMap(labelMap, option.label, false); |
34392 option.element.remove(); |
35181 option.element.remove(); |
34393 } |
35182 } |
34394 } |
35183 } |
34395 // remove any excessive OPTGROUPs from select |
35184 // remove any excessive OPTGROUPs from select |
34396 while(optionGroupsCache.length > groupIndex) { |
35185 while (optionGroupsCache.length > groupIndex) { |
34397 optionGroupsCache.pop()[0].element.remove(); |
35186 // remove all the labels in the option group |
35187 optionGroup = optionGroupsCache.pop(); |
|
35188 for (index = 1; index < optionGroup.length; ++index) { |
|
35189 updateLabelMap(labelMap, optionGroup[index].label, false); |
|
35190 } |
|
35191 optionGroup[0].element.remove(); |
|
34398 } |
35192 } |
35193 forEach(labelMap, function(count, label) { |
|
35194 if (count > 0) { |
|
35195 selectCtrl.addOption(label); |
|
35196 } else if (count < 0) { |
|
35197 selectCtrl.removeOption(label); |
|
35198 } |
|
35199 }); |
|
34399 } |
35200 } |
34400 } |
35201 } |
34401 } |
35202 } |
34402 }; |
35203 }; |
34403 }]; |
35204 }]; |
34417 if (!interpolateFn) { |
35218 if (!interpolateFn) { |
34418 attr.$set('value', element.text()); |
35219 attr.$set('value', element.text()); |
34419 } |
35220 } |
34420 } |
35221 } |
34421 |
35222 |
34422 return function (scope, element, attr) { |
35223 return function(scope, element, attr) { |
34423 var selectCtrlName = '$selectController', |
35224 var selectCtrlName = '$selectController', |
34424 parent = element.parent(), |
35225 parent = element.parent(), |
34425 selectCtrl = parent.data(selectCtrlName) || |
35226 selectCtrl = parent.data(selectCtrlName) || |
34426 parent.parent().data(selectCtrlName); // in case we are in optgroup |
35227 parent.parent().data(selectCtrlName); // in case we are in optgroup |
34427 |
35228 |
38640 "</ul>\n" + |
39441 "</ul>\n" + |
38641 ""); |
39442 ""); |
38642 }]); |
39443 }]); |
38643 |
39444 |
38644 /** |
39445 /** |
38645 * @license AngularJS v1.3.0-rc.5 |
39446 * @license AngularJS v1.3.8 |
38646 * (c) 2010-2014 Google, Inc. http://angularjs.org |
39447 * (c) 2010-2014 Google, Inc. http://angularjs.org |
38647 * License: MIT |
39448 * License: MIT |
38648 */ |
39449 */ |
38649 (function(window, angular, undefined) {'use strict'; |
39450 (function(window, angular, undefined) {'use strict'; |
38650 |
39451 |
38676 * Create a shallow copy of an object and clear other fields from the destination |
39477 * Create a shallow copy of an object and clear other fields from the destination |
38677 */ |
39478 */ |
38678 function shallowClearAndCopy(src, dst) { |
39479 function shallowClearAndCopy(src, dst) { |
38679 dst = dst || {}; |
39480 dst = dst || {}; |
38680 |
39481 |
38681 angular.forEach(dst, function(value, key){ |
39482 angular.forEach(dst, function(value, key) { |
38682 delete dst[key]; |
39483 delete dst[key]; |
38683 }); |
39484 }); |
38684 |
39485 |
38685 for (var key in src) { |
39486 for (var key in src) { |
38686 if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { |
39487 if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { |
38725 * which can pose problems with server backends that do not expect that |
39526 * which can pose problems with server backends that do not expect that |
38726 * behavior. This can be disabled by configuring the `$resourceProvider` like |
39527 * behavior. This can be disabled by configuring the `$resourceProvider` like |
38727 * this: |
39528 * this: |
38728 * |
39529 * |
38729 * ```js |
39530 * ```js |
38730 app.config(['$resourceProvider', function ($resourceProvider) { |
39531 app.config(['$resourceProvider', function($resourceProvider) { |
38731 // Don't strip trailing slashes from calculated URLs |
39532 // Don't strip trailing slashes from calculated URLs |
38732 $resourceProvider.defaults.stripTrailingSlashes = false; |
39533 $resourceProvider.defaults.stripTrailingSlashes = false; |
38733 }]); |
39534 }]); |
38734 * ``` |
39535 * ``` |
38735 * |
39536 * |
38757 * If the parameter value is prefixed with `@` then the value for that parameter will be extracted |
39558 * If the parameter value is prefixed with `@` then the value for that parameter will be extracted |
38758 * from the corresponding property on the `data` object (provided when calling an action method). For |
39559 * from the corresponding property on the `data` object (provided when calling an action method). For |
38759 * example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam` |
39560 * example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam` |
38760 * will be `data.someProp`. |
39561 * will be `data.someProp`. |
38761 * |
39562 * |
38762 * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend |
39563 * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend |
38763 * the default set of resource actions. The declaration should be created in the format of {@link |
39564 * the default set of resource actions. The declaration should be created in the format of {@link |
38764 * ng.$http#usage_parameters $http.config}: |
39565 * ng.$http#usage $http.config}: |
38765 * |
39566 * |
38766 * {action1: {method:?, params:?, isArray:?, headers:?, ...}, |
39567 * {action1: {method:?, params:?, isArray:?, headers:?, ...}, |
38767 * action2: {method:?, params:?, isArray:?, headers:?, ...}, |
39568 * action2: {method:?, params:?, isArray:?, headers:?, ...}, |
38768 * ...} |
39569 * ...} |
38769 * |
39570 * |
38991 * // This will PUT /notes/ID with the note object in the request payload |
39792 * // This will PUT /notes/ID with the note object in the request payload |
38992 * }]); |
39793 * }]); |
38993 * ``` |
39794 * ``` |
38994 */ |
39795 */ |
38995 angular.module('ngResource', ['ng']). |
39796 angular.module('ngResource', ['ng']). |
38996 provider('$resource', function () { |
39797 provider('$resource', function() { |
38997 var provider = this; |
39798 var provider = this; |
38998 |
39799 |
38999 this.defaults = { |
39800 this.defaults = { |
39000 // Strip slashes by default |
39801 // Strip slashes by default |
39001 stripTrailingSlashes: true, |
39802 stripTrailingSlashes: true, |
39008 'remove': {method: 'DELETE'}, |
39809 'remove': {method: 'DELETE'}, |
39009 'delete': {method: 'DELETE'} |
39810 'delete': {method: 'DELETE'} |
39010 } |
39811 } |
39011 }; |
39812 }; |
39012 |
39813 |
39013 this.$get = ['$http', '$q', function ($http, $q) { |
39814 this.$get = ['$http', '$q', function($http, $q) { |
39014 |
39815 |
39015 var noop = angular.noop, |
39816 var noop = angular.noop, |
39016 forEach = angular.forEach, |
39817 forEach = angular.forEach, |
39017 extend = angular.extend, |
39818 extend = angular.extend, |
39018 copy = angular.copy, |
39819 copy = angular.copy, |
39062 this.defaults = extend({}, provider.defaults, defaults); |
39863 this.defaults = extend({}, provider.defaults, defaults); |
39063 this.urlParams = {}; |
39864 this.urlParams = {}; |
39064 } |
39865 } |
39065 |
39866 |
39066 Route.prototype = { |
39867 Route.prototype = { |
39067 setUrlParams: function (config, params, actionUrl) { |
39868 setUrlParams: function(config, params, actionUrl) { |
39068 var self = this, |
39869 var self = this, |
39069 url = actionUrl || self.template, |
39870 url = actionUrl || self.template, |
39070 val, |
39871 val, |
39071 encodedVal; |
39872 encodedVal; |
39072 |
39873 |
39073 var urlParams = self.urlParams = {}; |
39874 var urlParams = self.urlParams = {}; |
39074 forEach(url.split(/\W/), function (param) { |
39875 forEach(url.split(/\W/), function(param) { |
39075 if (param === 'hasOwnProperty') { |
39876 if (param === 'hasOwnProperty') { |
39076 throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); |
39877 throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name."); |
39077 } |
39878 } |
39078 if (!(new RegExp("^\\d+$").test(param)) && param && |
39879 if (!(new RegExp("^\\d+$").test(param)) && param && |
39079 (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { |
39880 (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) { |
39081 } |
39882 } |
39082 }); |
39883 }); |
39083 url = url.replace(/\\:/g, ':'); |
39884 url = url.replace(/\\:/g, ':'); |
39084 |
39885 |
39085 params = params || {}; |
39886 params = params || {}; |
39086 forEach(self.urlParams, function (_, urlParam) { |
39887 forEach(self.urlParams, function(_, urlParam) { |
39087 val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; |
39888 val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; |
39088 if (angular.isDefined(val) && val !== null) { |
39889 if (angular.isDefined(val) && val !== null) { |
39089 encodedVal = encodeUriSegment(val); |
39890 encodedVal = encodeUriSegment(val); |
39090 url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function (match, p1) { |
39891 url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) { |
39091 return encodedVal + p1; |
39892 return encodedVal + p1; |
39092 }); |
39893 }); |
39093 } else { |
39894 } else { |
39094 url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function (match, |
39895 url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match, |
39095 leadingSlashes, tail) { |
39896 leadingSlashes, tail) { |
39096 if (tail.charAt(0) == '/') { |
39897 if (tail.charAt(0) == '/') { |
39097 return tail; |
39898 return tail; |
39098 } else { |
39899 } else { |
39099 return leadingSlashes + tail; |
39900 return leadingSlashes + tail; |
39113 // replace escaped `/\.` with `/.` |
39914 // replace escaped `/\.` with `/.` |
39114 config.url = url.replace(/\/\\\./, '/.'); |
39915 config.url = url.replace(/\/\\\./, '/.'); |
39115 |
39916 |
39116 |
39917 |
39117 // set params - delegate param encoding to $http |
39918 // set params - delegate param encoding to $http |
39118 forEach(params, function (value, key) { |
39919 forEach(params, function(value, key) { |
39119 if (!self.urlParams[key]) { |
39920 if (!self.urlParams[key]) { |
39120 config.params = config.params || {}; |
39921 config.params = config.params || {}; |
39121 config.params[key] = value; |
39922 config.params[key] = value; |
39122 } |
39923 } |
39123 }); |
39924 }); |
39131 actions = extend({}, provider.defaults.actions, actions); |
39932 actions = extend({}, provider.defaults.actions, actions); |
39132 |
39933 |
39133 function extractParams(data, actionParams) { |
39934 function extractParams(data, actionParams) { |
39134 var ids = {}; |
39935 var ids = {}; |
39135 actionParams = extend({}, paramDefaults, actionParams); |
39936 actionParams = extend({}, paramDefaults, actionParams); |
39136 forEach(actionParams, function (value, key) { |
39937 forEach(actionParams, function(value, key) { |
39137 if (isFunction(value)) { value = value(); } |
39938 if (isFunction(value)) { value = value(); } |
39138 ids[key] = value && value.charAt && value.charAt(0) == '@' ? |
39939 ids[key] = value && value.charAt && value.charAt(0) == '@' ? |
39139 lookupDottedPath(data, value.substr(1)) : value; |
39940 lookupDottedPath(data, value.substr(1)) : value; |
39140 }); |
39941 }); |
39141 return ids; |
39942 return ids; |
39147 |
39948 |
39148 function Resource(value) { |
39949 function Resource(value) { |
39149 shallowClearAndCopy(value || {}, this); |
39950 shallowClearAndCopy(value || {}, this); |
39150 } |
39951 } |
39151 |
39952 |
39152 Resource.prototype.toJSON = function () { |
39953 Resource.prototype.toJSON = function() { |
39153 var data = extend({}, this); |
39954 var data = extend({}, this); |
39154 delete data.$promise; |
39955 delete data.$promise; |
39155 delete data.$resolved; |
39956 delete data.$resolved; |
39156 return data; |
39957 return data; |
39157 }; |
39958 }; |
39158 |
39959 |
39159 forEach(actions, function (action, name) { |
39960 forEach(actions, function(action, name) { |
39160 var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); |
39961 var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method); |
39161 |
39962 |
39162 Resource[name] = function (a1, a2, a3, a4) { |
39963 Resource[name] = function(a1, a2, a3, a4) { |
39163 var params = {}, data, success, error; |
39964 var params = {}, data, success, error; |
39164 |
39965 |
39165 /* jshint -W086 */ /* (purposefully fall through case statements) */ |
39966 /* jshint -W086 */ /* (purposefully fall through case statements) */ |
39166 switch (arguments.length) { |
39967 switch (arguments.length) { |
39167 case 4: |
39968 case 4: |
39205 var responseInterceptor = action.interceptor && action.interceptor.response || |
40006 var responseInterceptor = action.interceptor && action.interceptor.response || |
39206 defaultResponseInterceptor; |
40007 defaultResponseInterceptor; |
39207 var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || |
40008 var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || |
39208 undefined; |
40009 undefined; |
39209 |
40010 |
39210 forEach(action, function (value, key) { |
40011 forEach(action, function(value, key) { |
39211 if (key != 'params' && key != 'isArray' && key != 'interceptor') { |
40012 if (key != 'params' && key != 'isArray' && key != 'interceptor') { |
39212 httpConfig[key] = copy(value); |
40013 httpConfig[key] = copy(value); |
39213 } |
40014 } |
39214 }); |
40015 }); |
39215 |
40016 |
39216 if (hasBody) httpConfig.data = data; |
40017 if (hasBody) httpConfig.data = data; |
39217 route.setUrlParams(httpConfig, |
40018 route.setUrlParams(httpConfig, |
39218 extend({}, extractParams(data, action.params || {}), params), |
40019 extend({}, extractParams(data, action.params || {}), params), |
39219 action.url); |
40020 action.url); |
39220 |
40021 |
39221 var promise = $http(httpConfig).then(function (response) { |
40022 var promise = $http(httpConfig).then(function(response) { |
39222 var data = response.data, |
40023 var data = response.data, |
39223 promise = value.$promise; |
40024 promise = value.$promise; |
39224 |
40025 |
39225 if (data) { |
40026 if (data) { |
39226 // Need to convert action.isArray to boolean in case it is undefined |
40027 // Need to convert action.isArray to boolean in case it is undefined |
39232 angular.isArray(data) ? 'array' : 'object'); |
40033 angular.isArray(data) ? 'array' : 'object'); |
39233 } |
40034 } |
39234 // jshint +W018 |
40035 // jshint +W018 |
39235 if (action.isArray) { |
40036 if (action.isArray) { |
39236 value.length = 0; |
40037 value.length = 0; |
39237 forEach(data, function (item) { |
40038 forEach(data, function(item) { |
39238 if (typeof item === "object") { |
40039 if (typeof item === "object") { |
39239 value.push(new Resource(item)); |
40040 value.push(new Resource(item)); |
39240 } else { |
40041 } else { |
39241 // Valid JSON values may be string literals, and these should not be converted |
40042 // Valid JSON values may be string literals, and these should not be converted |
39242 // into objects. These items will not have access to the Resource prototype |
40043 // into objects. These items will not have access to the Resource prototype |
39253 value.$resolved = true; |
40054 value.$resolved = true; |
39254 |
40055 |
39255 response.resource = value; |
40056 response.resource = value; |
39256 |
40057 |
39257 return response; |
40058 return response; |
39258 }, function (response) { |
40059 }, function(response) { |
39259 value.$resolved = true; |
40060 value.$resolved = true; |
39260 |
40061 |
39261 (error || noop)(response); |
40062 (error || noop)(response); |
39262 |
40063 |
39263 return $q.reject(response); |
40064 return $q.reject(response); |
39264 }); |
40065 }); |
39265 |
40066 |
39266 promise = promise.then( |
40067 promise = promise.then( |
39267 function (response) { |
40068 function(response) { |
39268 var value = responseInterceptor(response); |
40069 var value = responseInterceptor(response); |
39269 (success || noop)(value, response.headers); |
40070 (success || noop)(value, response.headers); |
39270 return value; |
40071 return value; |
39271 }, |
40072 }, |
39272 responseErrorInterceptor); |
40073 responseErrorInterceptor); |
39284 // instance call |
40085 // instance call |
39285 return promise; |
40086 return promise; |
39286 }; |
40087 }; |
39287 |
40088 |
39288 |
40089 |
39289 Resource.prototype['$' + name] = function (params, success, error) { |
40090 Resource.prototype['$' + name] = function(params, success, error) { |
39290 if (isFunction(params)) { |
40091 if (isFunction(params)) { |
39291 error = success; success = params; params = {}; |
40092 error = success; success = params; params = {}; |
39292 } |
40093 } |
39293 var result = Resource[name].call(this, params, this, success, error); |
40094 var result = Resource[name].call(this, params, this, success, error); |
39294 return result.$promise || result; |
40095 return result.$promise || result; |
39295 }; |
40096 }; |
39296 }); |
40097 }); |
39297 |
40098 |
39298 Resource.bind = function (additionalParamDefaults) { |
40099 Resource.bind = function(additionalParamDefaults) { |
39299 return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); |
40100 return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); |
39300 }; |
40101 }; |
39301 |
40102 |
39302 return Resource; |
40103 return Resource; |
39303 } |
40104 } |
39308 |
40109 |
39309 |
40110 |
39310 })(window, window.angular); |
40111 })(window, window.angular); |
39311 |
40112 |
39312 /** |
40113 /** |
39313 * @license AngularJS v1.3.0-rc.5 |
40114 * @license AngularJS v1.3.8 |
39314 * (c) 2010-2014 Google, Inc. http://angularjs.org |
40115 * (c) 2010-2014 Google, Inc. http://angularjs.org |
39315 * License: MIT |
40116 * License: MIT |
39316 */ |
40117 */ |
39317 (function(window, angular, undefined) {'use strict'; |
40118 (function(window, angular, undefined) {'use strict'; |
39318 |
40119 |
39348 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. |
40149 * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`. |
39349 * |
40150 * |
39350 * ## Dependencies |
40151 * ## Dependencies |
39351 * Requires the {@link ngRoute `ngRoute`} module to be installed. |
40152 * Requires the {@link ngRoute `ngRoute`} module to be installed. |
39352 */ |
40153 */ |
39353 function $RouteProvider(){ |
40154 function $RouteProvider() { |
39354 function inherit(parent, extra) { |
40155 function inherit(parent, extra) { |
39355 return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra); |
40156 return angular.extend(Object.create(parent), extra); |
39356 } |
40157 } |
39357 |
40158 |
39358 var routes = {}; |
40159 var routes = {}; |
39359 |
40160 |
39360 /** |
40161 /** |
39455 * |
40256 * |
39456 * @description |
40257 * @description |
39457 * Adds a new route definition to the `$route` service. |
40258 * Adds a new route definition to the `$route` service. |
39458 */ |
40259 */ |
39459 this.when = function(path, route) { |
40260 this.when = function(path, route) { |
40261 //copy original route object to preserve params inherited from proto chain |
|
40262 var routeCopy = angular.copy(route); |
|
40263 if (angular.isUndefined(routeCopy.reloadOnSearch)) { |
|
40264 routeCopy.reloadOnSearch = true; |
|
40265 } |
|
40266 if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) { |
|
40267 routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch; |
|
40268 } |
|
39460 routes[path] = angular.extend( |
40269 routes[path] = angular.extend( |
39461 {reloadOnSearch: true}, |
40270 routeCopy, |
39462 route, |
40271 path && pathRegExp(path, routeCopy) |
39463 path && pathRegExp(path, route) |
|
39464 ); |
40272 ); |
39465 |
40273 |
39466 // create redirection for trailing slashes |
40274 // create redirection for trailing slashes |
39467 if (path) { |
40275 if (path) { |
39468 var redirectPath = (path[path.length-1] == '/') |
40276 var redirectPath = (path[path.length - 1] == '/') |
39469 ? path.substr(0, path.length-1) |
40277 ? path.substr(0, path.length - 1) |
39470 : path +'/'; |
40278 : path + '/'; |
39471 |
40279 |
39472 routes[redirectPath] = angular.extend( |
40280 routes[redirectPath] = angular.extend( |
39473 {redirectTo: path}, |
40281 {redirectTo: path}, |
39474 pathRegExp(redirectPath, route) |
40282 pathRegExp(redirectPath, routeCopy) |
39475 ); |
40283 ); |
39476 } |
40284 } |
39477 |
40285 |
39478 return this; |
40286 return this; |
39479 }; |
40287 }; |
40288 |
|
40289 /** |
|
40290 * @ngdoc property |
|
40291 * @name $routeProvider#caseInsensitiveMatch |
|
40292 * @description |
|
40293 * |
|
40294 * A boolean property indicating if routes defined |
|
40295 * using this provider should be matched using a case insensitive |
|
40296 * algorithm. Defaults to `false`. |
|
40297 */ |
|
40298 this.caseInsensitiveMatch = false; |
|
39480 |
40299 |
39481 /** |
40300 /** |
39482 * @param path {string} path |
40301 * @param path {string} path |
39483 * @param opts {Object} options |
40302 * @param opts {Object} options |
39484 * @return {?Object} |
40303 * @return {?Object} |
39497 }, |
40316 }, |
39498 keys = ret.keys = []; |
40317 keys = ret.keys = []; |
39499 |
40318 |
39500 path = path |
40319 path = path |
39501 .replace(/([().])/g, '\\$1') |
40320 .replace(/([().])/g, '\\$1') |
39502 .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){ |
40321 .replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) { |
39503 var optional = option === '?' ? option : null; |
40322 var optional = option === '?' ? option : null; |
39504 var star = option === '*' ? option : null; |
40323 var star = option === '*' ? option : null; |
39505 keys.push({ name: key, optional: !!optional }); |
40324 keys.push({ name: key, optional: !!optional }); |
39506 slash = slash || ''; |
40325 slash = slash || ''; |
39507 return '' |
40326 return '' |
39751 * @description |
40570 * @description |
39752 * Causes `$route` service to reload the current route even if |
40571 * Causes `$route` service to reload the current route even if |
39753 * {@link ng.$location $location} hasn't changed. |
40572 * {@link ng.$location $location} hasn't changed. |
39754 * |
40573 * |
39755 * As a result of that, {@link ngRoute.directive:ngView ngView} |
40574 * As a result of that, {@link ngRoute.directive:ngView ngView} |
39756 * creates new scope, reinstantiates the controller. |
40575 * creates new scope and reinstantiates the controller. |
39757 */ |
40576 */ |
39758 reload: function() { |
40577 reload: function() { |
39759 forceReload = true; |
40578 forceReload = true; |
39760 $rootScope.$evalAsync(function() { |
40579 $rootScope.$evalAsync(function() { |
39761 // Don't support cancellation of a reload for now... |
40580 // Don't support cancellation of a reload for now... |
39944 /** |
40763 /** |
39945 * @returns {string} interpolation of the redirect path with the parameters |
40764 * @returns {string} interpolation of the redirect path with the parameters |
39946 */ |
40765 */ |
39947 function interpolate(string, params) { |
40766 function interpolate(string, params) { |
39948 var result = []; |
40767 var result = []; |
39949 angular.forEach((string||'').split(':'), function(segment, i) { |
40768 angular.forEach((string || '').split(':'), function(segment, i) { |
39950 if (i === 0) { |
40769 if (i === 0) { |
39951 result.push(segment); |
40770 result.push(segment); |
39952 } else { |
40771 } else { |
39953 var segmentMatch = segment.match(/(\w+)(.*)/); |
40772 var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/); |
39954 var key = segmentMatch[1]; |
40773 var key = segmentMatch[1]; |
39955 result.push(params[key]); |
40774 result.push(params[key]); |
39956 result.push(segmentMatch[2] || ''); |
40775 result.push(segmentMatch[2] || ''); |
39957 delete params[key]; |
40776 delete params[key]; |
39958 } |
40777 } |
40079 |
40898 |
40080 <file name="animations.css"> |
40899 <file name="animations.css"> |
40081 .view-animate-container { |
40900 .view-animate-container { |
40082 position:relative; |
40901 position:relative; |
40083 height:100px!important; |
40902 height:100px!important; |
40084 position:relative; |
|
40085 background:white; |
40903 background:white; |
40086 border:1px solid black; |
40904 border:1px solid black; |
40087 height:40px; |
40905 height:40px; |
40088 overflow:hidden; |
40906 overflow:hidden; |
40089 } |
40907 } |
40179 * @eventType emit on the current ngView scope |
40997 * @eventType emit on the current ngView scope |
40180 * @description |
40998 * @description |
40181 * Emitted every time the ngView content is reloaded. |
40999 * Emitted every time the ngView content is reloaded. |
40182 */ |
41000 */ |
40183 ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; |
41001 ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; |
40184 function ngViewFactory( $route, $anchorScroll, $animate) { |
41002 function ngViewFactory($route, $anchorScroll, $animate) { |
40185 return { |
41003 return { |
40186 restrict: 'ECA', |
41004 restrict: 'ECA', |
40187 terminal: true, |
41005 terminal: true, |
40188 priority: 400, |
41006 priority: 400, |
40189 transclude: 'element', |
41007 transclude: 'element', |
40196 |
41014 |
40197 scope.$on('$routeChangeSuccess', update); |
41015 scope.$on('$routeChangeSuccess', update); |
40198 update(); |
41016 update(); |
40199 |
41017 |
40200 function cleanupLastView() { |
41018 function cleanupLastView() { |
40201 if(previousLeaveAnimation) { |
41019 if (previousLeaveAnimation) { |
40202 $animate.cancel(previousLeaveAnimation); |
41020 $animate.cancel(previousLeaveAnimation); |
40203 previousLeaveAnimation = null; |
41021 previousLeaveAnimation = null; |
40204 } |
41022 } |
40205 |
41023 |
40206 if(currentScope) { |
41024 if (currentScope) { |
40207 currentScope.$destroy(); |
41025 currentScope.$destroy(); |
40208 currentScope = null; |
41026 currentScope = null; |
40209 } |
41027 } |
40210 if(currentElement) { |
41028 if (currentElement) { |
40211 previousLeaveAnimation = $animate.leave(currentElement); |
41029 previousLeaveAnimation = $animate.leave(currentElement); |
40212 previousLeaveAnimation.then(function() { |
41030 previousLeaveAnimation.then(function() { |
40213 previousLeaveAnimation = null; |
41031 previousLeaveAnimation = null; |
40214 }); |
41032 }); |
40215 currentElement = null; |
41033 currentElement = null; |
40229 // However, using ng-view on an element with additional content does not make sense... |
41047 // However, using ng-view on an element with additional content does not make sense... |
40230 // Note: We can't remove them in the cloneAttchFn of $transclude as that |
41048 // Note: We can't remove them in the cloneAttchFn of $transclude as that |
40231 // function is called before linking the content, which would apply child |
41049 // function is called before linking the content, which would apply child |
40232 // directives to non existing elements. |
41050 // directives to non existing elements. |
40233 var clone = $transclude(newScope, function(clone) { |
41051 var clone = $transclude(newScope, function(clone) { |
40234 $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter () { |
41052 $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() { |
40235 if (angular.isDefined(autoScrollExp) |
41053 if (angular.isDefined(autoScrollExp) |
40236 && (!autoScrollExp || scope.$eval(autoScrollExp))) { |
41054 && (!autoScrollExp || scope.$eval(autoScrollExp))) { |
40237 $anchorScroll(); |
41055 $anchorScroll(); |
40238 } |
41056 } |
40239 }); |
41057 }); |
40359 }, |
41177 }, |
40360 controller: ['$scope', function($scope){ |
41178 controller: ['$scope', function($scope){ |
40361 // the index of the suggestions that's currently selected |
41179 // the index of the suggestions that's currently selected |
40362 $scope.selectedIndex = -1; |
41180 $scope.selectedIndex = -1; |
40363 |
41181 |
41182 $scope.initLock = true; |
|
41183 |
|
40364 // set new index |
41184 // set new index |
40365 $scope.setIndex = function(i){ |
41185 $scope.setIndex = function(i){ |
40366 $scope.selectedIndex = parseInt(i); |
41186 $scope.selectedIndex = parseInt(i); |
40367 }; |
41187 }; |
40368 |
41188 |
40382 $scope.completing = false; |
41202 $scope.completing = false; |
40383 |
41203 |
40384 // starts autocompleting on typing in something |
41204 // starts autocompleting on typing in something |
40385 $scope.$watch('searchParam', function(newValue, oldValue){ |
41205 $scope.$watch('searchParam', function(newValue, oldValue){ |
40386 |
41206 |
40387 if (oldValue === newValue || !oldValue) { |
41207 if (oldValue === newValue || (!oldValue && $scope.initLock)) { |
40388 return; |
41208 return; |
40389 } |
41209 } |
40390 |
41210 |
40391 if(watching && typeof $scope.searchParam !== 'undefined' && $scope.searchParam !== null) { |
41211 if(watching && typeof $scope.searchParam !== 'undefined' && $scope.searchParam !== null) { |
40392 $scope.completing = true; |
41212 $scope.completing = true; |
40437 |
41257 |
40438 |
41258 |
40439 }], |
41259 }], |
40440 link: function(scope, element, attrs){ |
41260 link: function(scope, element, attrs){ |
40441 |
41261 |
41262 setTimeout(function() { |
|
41263 scope.initLock = false; |
|
41264 scope.$apply(); |
|
41265 }, 250); |
|
41266 |
|
40442 var attr = ''; |
41267 var attr = ''; |
40443 |
41268 |
40444 // Default atts |
41269 // Default atts |
40445 scope.attrs = { |
41270 scope.attrs = { |
40446 "placeholder": "start typing...", |
41271 "placeholder": "start typing...", |
40460 } |
41285 } |
40461 |
41286 |
40462 if (attrs.clickActivation) { |
41287 if (attrs.clickActivation) { |
40463 element[0].onclick = function(e){ |
41288 element[0].onclick = function(e){ |
40464 if(!scope.searchParam){ |
41289 if(!scope.searchParam){ |
40465 scope.completing = true; |
41290 setTimeout(function() { |
40466 scope.$apply(); |
41291 scope.completing = true; |
41292 scope.$apply(); |
|
41293 }, 200); |
|
40467 } |
41294 } |
40468 }; |
41295 }; |
40469 } |
41296 } |
40470 |
41297 |
40471 var key = {left: 37, up: 38, right: 39, down: 40 , enter: 13, esc: 27, tab: 9}; |
41298 var key = {left: 37, up: 38, right: 39, down: 40 , enter: 13, esc: 27, tab: 9}; |
40488 // we do a timeout to prevent hiding it before a click event is registered |
41315 // we do a timeout to prevent hiding it before a click event is registered |
40489 setTimeout(function() { |
41316 setTimeout(function() { |
40490 scope.select(); |
41317 scope.select(); |
40491 scope.setIndex(-1); |
41318 scope.setIndex(-1); |
40492 scope.$apply(); |
41319 scope.$apply(); |
40493 }, 200); |
41320 }, 150); |
40494 }, true); |
41321 }, true); |
40495 |
41322 |
40496 element[0].addEventListener("keydown",function (e){ |
41323 element[0].addEventListener("keydown",function (e){ |
40497 var keycode = e.keyCode || e.which; |
41324 var keycode = e.keyCode || e.which; |
40498 |
41325 |
40499 var l = angular.element(this).find('li').length; |
41326 var l = angular.element(this).find('li').length; |
41327 |
|
41328 // this allows submitting forms by pressing Enter in the autocompleted field |
|
41329 if(!scope.completing || l == 0) return; |
|
40500 |
41330 |
40501 // implementation of the up and down movement in the list of suggestions |
41331 // implementation of the up and down movement in the list of suggestions |
40502 switch (keycode){ |
41332 switch (keycode){ |
40503 case key.up: |
41333 case key.up: |
40504 |
41334 |
40632 */ |
41462 */ |
40633 |
41463 |
40634 !(function() { |
41464 !(function() { |
40635 "use strict"; |
41465 "use strict"; |
40636 |
41466 |
40637 var VERSION = '2.1.0'; |
41467 var VERSION = '3.0.0'; |
40638 |
41468 |
40639 var ENTITIES = {}; |
41469 var ENTITIES = {}; |
40640 |
41470 |
40641 // from http://semplicewebsites.com/removing-accents-javascript |
41471 // from http://semplicewebsites.com/removing-accents-javascript |
40642 var latin_map={"Á":"A","Ă":"A","Ắ":"A","Ặ":"A","Ằ":"A","Ẳ":"A","Ẵ":"A","Ǎ":"A","Â":"A","Ấ":"A","Ậ":"A","Ầ":"A","Ẩ":"A","Ẫ":"A","Ä":"A","Ǟ":"A","Ȧ":"A","Ǡ":"A","Ạ":"A","Ȁ":"A","À":"A","Ả":"A","Ȃ":"A","Ā":"A","Ą":"A","Å":"A","Ǻ":"A","Ḁ":"A","Ⱥ":"A","Ã":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ḃ":"B","Ḅ":"B","Ɓ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ć":"C","Č":"C","Ç":"C","Ḉ":"C","Ĉ":"C","Ċ":"C","Ƈ":"C","Ȼ":"C","Ď":"D","Ḑ":"D","Ḓ":"D","Ḋ":"D","Ḍ":"D","Ɗ":"D","Ḏ":"D","Dz":"D","Dž":"D","Đ":"D","Ƌ":"D","DZ":"DZ","DŽ":"DZ","É":"E","Ĕ":"E","Ě":"E","Ȩ":"E","Ḝ":"E","Ê":"E","Ế":"E","Ệ":"E","Ề":"E","Ể":"E","Ễ":"E","Ḙ":"E","Ë":"E","Ė":"E","Ẹ":"E","Ȅ":"E","È":"E","Ẻ":"E","Ȇ":"E","Ē":"E","Ḗ":"E","Ḕ":"E","Ę":"E","Ɇ":"E","Ẽ":"E","Ḛ":"E","Ꝫ":"ET","Ḟ":"F","Ƒ":"F","Ǵ":"G","Ğ":"G","Ǧ":"G","Ģ":"G","Ĝ":"G","Ġ":"G","Ɠ":"G","Ḡ":"G","Ǥ":"G","Ḫ":"H","Ȟ":"H","Ḩ":"H","Ĥ":"H","Ⱨ":"H","Ḧ":"H","Ḣ":"H","Ḥ":"H","Ħ":"H","Í":"I","Ĭ":"I","Ǐ":"I","Î":"I","Ï":"I","Ḯ":"I","İ":"I","Ị":"I","Ȉ":"I","Ì":"I","Ỉ":"I","Ȋ":"I","Ī":"I","Į":"I","Ɨ":"I","Ĩ":"I","Ḭ":"I","Ꝺ":"D","Ꝼ":"F","Ᵹ":"G","Ꞃ":"R","Ꞅ":"S","Ꞇ":"T","Ꝭ":"IS","Ĵ":"J","Ɉ":"J","Ḱ":"K","Ǩ":"K","Ķ":"K","Ⱪ":"K","Ꝃ":"K","Ḳ":"K","Ƙ":"K","Ḵ":"K","Ꝁ":"K","Ꝅ":"K","Ĺ":"L","Ƚ":"L","Ľ":"L","Ļ":"L","Ḽ":"L","Ḷ":"L","Ḹ":"L","Ⱡ":"L","Ꝉ":"L","Ḻ":"L","Ŀ":"L","Ɫ":"L","Lj":"L","Ł":"L","LJ":"LJ","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ń":"N","Ň":"N","Ņ":"N","Ṋ":"N","Ṅ":"N","Ṇ":"N","Ǹ":"N","Ɲ":"N","Ṉ":"N","Ƞ":"N","Nj":"N","Ñ":"N","NJ":"NJ","Ó":"O","Ŏ":"O","Ǒ":"O","Ô":"O","Ố":"O","Ộ":"O","Ồ":"O","Ổ":"O","Ỗ":"O","Ö":"O","Ȫ":"O","Ȯ":"O","Ȱ":"O","Ọ":"O","Ő":"O","Ȍ":"O","Ò":"O","Ỏ":"O","Ơ":"O","Ớ":"O","Ợ":"O","Ờ":"O","Ở":"O","Ỡ":"O","Ȏ":"O","Ꝋ":"O","Ꝍ":"O","Ō":"O","Ṓ":"O","Ṑ":"O","Ɵ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Õ":"O","Ṍ":"O","Ṏ":"O","Ȭ":"O","Ƣ":"OI","Ꝏ":"OO","Ɛ":"E","Ɔ":"O","Ȣ":"OU","Ṕ":"P","Ṗ":"P","Ꝓ":"P","Ƥ":"P","Ꝕ":"P","Ᵽ":"P","Ꝑ":"P","Ꝙ":"Q","Ꝗ":"Q","Ŕ":"R","Ř":"R","Ŗ":"R","Ṙ":"R","Ṛ":"R","Ṝ":"R","Ȑ":"R","Ȓ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꜿ":"C","Ǝ":"E","Ś":"S","Ṥ":"S","Š":"S","Ṧ":"S","Ş":"S","Ŝ":"S","Ș":"S","Ṡ":"S","Ṣ":"S","Ṩ":"S","ẞ":"SS","Ť":"T","Ţ":"T","Ṱ":"T","Ț":"T","Ⱦ":"T","Ṫ":"T","Ṭ":"T","Ƭ":"T","Ṯ":"T","Ʈ":"T","Ŧ":"T","Ɐ":"A","Ꞁ":"L","Ɯ":"M","Ʌ":"V","Ꜩ":"TZ","Ú":"U","Ŭ":"U","Ǔ":"U","Û":"U","Ṷ":"U","Ü":"U","Ǘ":"U","Ǚ":"U","Ǜ":"U","Ǖ":"U","Ṳ":"U","Ụ":"U","Ű":"U","Ȕ":"U","Ù":"U","Ủ":"U","Ư":"U","Ứ":"U","Ự":"U","Ừ":"U","Ử":"U","Ữ":"U","Ȗ":"U","Ū":"U","Ṻ":"U","Ų":"U","Ů":"U","Ũ":"U","Ṹ":"U","Ṵ":"U","Ꝟ":"V","Ṿ":"V","Ʋ":"V","Ṽ":"V","Ꝡ":"VY","Ẃ":"W","Ŵ":"W","Ẅ":"W","Ẇ":"W","Ẉ":"W","Ẁ":"W","Ⱳ":"W","Ẍ":"X","Ẋ":"X","Ý":"Y","Ŷ":"Y","Ÿ":"Y","Ẏ":"Y","Ỵ":"Y","Ỳ":"Y","Ƴ":"Y","Ỷ":"Y","Ỿ":"Y","Ȳ":"Y","Ɏ":"Y","Ỹ":"Y","Ź":"Z","Ž":"Z","Ẑ":"Z","Ⱬ":"Z","Ż":"Z","Ẓ":"Z","Ȥ":"Z","Ẕ":"Z","Ƶ":"Z","IJ":"IJ","Œ":"OE","ᴀ":"A","ᴁ":"AE","ʙ":"B","ᴃ":"B","ᴄ":"C","ᴅ":"D","ᴇ":"E","ꜰ":"F","ɢ":"G","ʛ":"G","ʜ":"H","ɪ":"I","ʁ":"R","ᴊ":"J","ᴋ":"K","ʟ":"L","ᴌ":"L","ᴍ":"M","ɴ":"N","ᴏ":"O","ɶ":"OE","ᴐ":"O","ᴕ":"OU","ᴘ":"P","ʀ":"R","ᴎ":"N","ᴙ":"R","ꜱ":"S","ᴛ":"T","ⱻ":"E","ᴚ":"R","ᴜ":"U","ᴠ":"V","ᴡ":"W","ʏ":"Y","ᴢ":"Z","á":"a","ă":"a","ắ":"a","ặ":"a","ằ":"a","ẳ":"a","ẵ":"a","ǎ":"a","â":"a","ấ":"a","ậ":"a","ầ":"a","ẩ":"a","ẫ":"a","ä":"a","ǟ":"a","ȧ":"a","ǡ":"a","ạ":"a","ȁ":"a","à":"a","ả":"a","ȃ":"a","ā":"a","ą":"a","ᶏ":"a","ẚ":"a","å":"a","ǻ":"a","ḁ":"a","ⱥ":"a","ã":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ḃ":"b","ḅ":"b","ɓ":"b","ḇ":"b","ᵬ":"b","ᶀ":"b","ƀ":"b","ƃ":"b","ɵ":"o","ć":"c","č":"c","ç":"c","ḉ":"c","ĉ":"c","ɕ":"c","ċ":"c","ƈ":"c","ȼ":"c","ď":"d","ḑ":"d","ḓ":"d","ȡ":"d","ḋ":"d","ḍ":"d","ɗ":"d","ᶑ":"d","ḏ":"d","ᵭ":"d","ᶁ":"d","đ":"d","ɖ":"d","ƌ":"d","ı":"i","ȷ":"j","ɟ":"j","ʄ":"j","dz":"dz","dž":"dz","é":"e","ĕ":"e","ě":"e","ȩ":"e","ḝ":"e","ê":"e","ế":"e","ệ":"e","ề":"e","ể":"e","ễ":"e","ḙ":"e","ë":"e","ė":"e","ẹ":"e","ȅ":"e","è":"e","ẻ":"e","ȇ":"e","ē":"e","ḗ":"e","ḕ":"e","ⱸ":"e","ę":"e","ᶒ":"e","ɇ":"e","ẽ":"e","ḛ":"e","ꝫ":"et","ḟ":"f","ƒ":"f","ᵮ":"f","ᶂ":"f","ǵ":"g","ğ":"g","ǧ":"g","ģ":"g","ĝ":"g","ġ":"g","ɠ":"g","ḡ":"g","ᶃ":"g","ǥ":"g","ḫ":"h","ȟ":"h","ḩ":"h","ĥ":"h","ⱨ":"h","ḧ":"h","ḣ":"h","ḥ":"h","ɦ":"h","ẖ":"h","ħ":"h","ƕ":"hv","í":"i","ĭ":"i","ǐ":"i","î":"i","ï":"i","ḯ":"i","ị":"i","ȉ":"i","ì":"i","ỉ":"i","ȋ":"i","ī":"i","į":"i","ᶖ":"i","ɨ":"i","ĩ":"i","ḭ":"i","ꝺ":"d","ꝼ":"f","ᵹ":"g","ꞃ":"r","ꞅ":"s","ꞇ":"t","ꝭ":"is","ǰ":"j","ĵ":"j","ʝ":"j","ɉ":"j","ḱ":"k","ǩ":"k","ķ":"k","ⱪ":"k","ꝃ":"k","ḳ":"k","ƙ":"k","ḵ":"k","ᶄ":"k","ꝁ":"k","ꝅ":"k","ĺ":"l","ƚ":"l","ɬ":"l","ľ":"l","ļ":"l","ḽ":"l","ȴ":"l","ḷ":"l","ḹ":"l","ⱡ":"l","ꝉ":"l","ḻ":"l","ŀ":"l","ɫ":"l","ᶅ":"l","ɭ":"l","ł":"l","lj":"lj","ſ":"s","ẜ":"s","ẛ":"s","ẝ":"s","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ᵯ":"m","ᶆ":"m","ń":"n","ň":"n","ņ":"n","ṋ":"n","ȵ":"n","ṅ":"n","ṇ":"n","ǹ":"n","ɲ":"n","ṉ":"n","ƞ":"n","ᵰ":"n","ᶇ":"n","ɳ":"n","ñ":"n","nj":"nj","ó":"o","ŏ":"o","ǒ":"o","ô":"o","ố":"o","ộ":"o","ồ":"o","ổ":"o","ỗ":"o","ö":"o","ȫ":"o","ȯ":"o","ȱ":"o","ọ":"o","ő":"o","ȍ":"o","ò":"o","ỏ":"o","ơ":"o","ớ":"o","ợ":"o","ờ":"o","ở":"o","ỡ":"o","ȏ":"o","ꝋ":"o","ꝍ":"o","ⱺ":"o","ō":"o","ṓ":"o","ṑ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","õ":"o","ṍ":"o","ṏ":"o","ȭ":"o","ƣ":"oi","ꝏ":"oo","ɛ":"e","ᶓ":"e","ɔ":"o","ᶗ":"o","ȣ":"ou","ṕ":"p","ṗ":"p","ꝓ":"p","ƥ":"p","ᵱ":"p","ᶈ":"p","ꝕ":"p","ᵽ":"p","ꝑ":"p","ꝙ":"q","ʠ":"q","ɋ":"q","ꝗ":"q","ŕ":"r","ř":"r","ŗ":"r","ṙ":"r","ṛ":"r","ṝ":"r","ȑ":"r","ɾ":"r","ᵳ":"r","ȓ":"r","ṟ":"r","ɼ":"r","ᵲ":"r","ᶉ":"r","ɍ":"r","ɽ":"r","ↄ":"c","ꜿ":"c","ɘ":"e","ɿ":"r","ś":"s","ṥ":"s","š":"s","ṧ":"s","ş":"s","ŝ":"s","ș":"s","ṡ":"s","ṣ":"s","ṩ":"s","ʂ":"s","ᵴ":"s","ᶊ":"s","ȿ":"s","ɡ":"g","ß":"ss","ᴑ":"o","ᴓ":"o","ᴝ":"u","ť":"t","ţ":"t","ṱ":"t","ț":"t","ȶ":"t","ẗ":"t","ⱦ":"t","ṫ":"t","ṭ":"t","ƭ":"t","ṯ":"t","ᵵ":"t","ƫ":"t","ʈ":"t","ŧ":"t","ᵺ":"th","ɐ":"a","ᴂ":"ae","ǝ":"e","ᵷ":"g","ɥ":"h","ʮ":"h","ʯ":"h","ᴉ":"i","ʞ":"k","ꞁ":"l","ɯ":"m","ɰ":"m","ᴔ":"oe","ɹ":"r","ɻ":"r","ɺ":"r","ⱹ":"r","ʇ":"t","ʌ":"v","ʍ":"w","ʎ":"y","ꜩ":"tz","ú":"u","ŭ":"u","ǔ":"u","û":"u","ṷ":"u","ü":"u","ǘ":"u","ǚ":"u","ǜ":"u","ǖ":"u","ṳ":"u","ụ":"u","ű":"u","ȕ":"u","ù":"u","ủ":"u","ư":"u","ứ":"u","ự":"u","ừ":"u","ử":"u","ữ":"u","ȗ":"u","ū":"u","ṻ":"u","ų":"u","ᶙ":"u","ů":"u","ũ":"u","ṹ":"u","ṵ":"u","ᵫ":"ue","ꝸ":"um","ⱴ":"v","ꝟ":"v","ṿ":"v","ʋ":"v","ᶌ":"v","ⱱ":"v","ṽ":"v","ꝡ":"vy","ẃ":"w","ŵ":"w","ẅ":"w","ẇ":"w","ẉ":"w","ẁ":"w","ⱳ":"w","ẘ":"w","ẍ":"x","ẋ":"x","ᶍ":"x","ý":"y","ŷ":"y","ÿ":"y","ẏ":"y","ỵ":"y","ỳ":"y","ƴ":"y","ỷ":"y","ỿ":"y","ȳ":"y","ẙ":"y","ɏ":"y","ỹ":"y","ź":"z","ž":"z","ẑ":"z","ʑ":"z","ⱬ":"z","ż":"z","ẓ":"z","ȥ":"z","ẕ":"z","ᵶ":"z","ᶎ":"z","ʐ":"z","ƶ":"z","ɀ":"z","ff":"ff","ffi":"ffi","ffl":"ffl","fi":"fi","fl":"fl","ij":"ij","œ":"oe","st":"st","ₐ":"a","ₑ":"e","ᵢ":"i","ⱼ":"j","ₒ":"o","ᵣ":"r","ᵤ":"u","ᵥ":"v","ₓ":"x"}; |
41472 var latin_map={"Á":"A","Ă":"A","Ắ":"A","Ặ":"A","Ằ":"A","Ẳ":"A","Ẵ":"A","Ǎ":"A","Â":"A","Ấ":"A","Ậ":"A","Ầ":"A","Ẩ":"A","Ẫ":"A","Ä":"A","Ǟ":"A","Ȧ":"A","Ǡ":"A","Ạ":"A","Ȁ":"A","À":"A","Ả":"A","Ȃ":"A","Ā":"A","Ą":"A","Å":"A","Ǻ":"A","Ḁ":"A","Ⱥ":"A","Ã":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ḃ":"B","Ḅ":"B","Ɓ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ć":"C","Č":"C","Ç":"C","Ḉ":"C","Ĉ":"C","Ċ":"C","Ƈ":"C","Ȼ":"C","Ď":"D","Ḑ":"D","Ḓ":"D","Ḋ":"D","Ḍ":"D","Ɗ":"D","Ḏ":"D","Dz":"D","Dž":"D","Đ":"D","Ƌ":"D","DZ":"DZ","DŽ":"DZ","É":"E","Ĕ":"E","Ě":"E","Ȩ":"E","Ḝ":"E","Ê":"E","Ế":"E","Ệ":"E","Ề":"E","Ể":"E","Ễ":"E","Ḙ":"E","Ë":"E","Ė":"E","Ẹ":"E","Ȅ":"E","È":"E","Ẻ":"E","Ȇ":"E","Ē":"E","Ḗ":"E","Ḕ":"E","Ę":"E","Ɇ":"E","Ẽ":"E","Ḛ":"E","Ꝫ":"ET","Ḟ":"F","Ƒ":"F","Ǵ":"G","Ğ":"G","Ǧ":"G","Ģ":"G","Ĝ":"G","Ġ":"G","Ɠ":"G","Ḡ":"G","Ǥ":"G","Ḫ":"H","Ȟ":"H","Ḩ":"H","Ĥ":"H","Ⱨ":"H","Ḧ":"H","Ḣ":"H","Ḥ":"H","Ħ":"H","Í":"I","Ĭ":"I","Ǐ":"I","Î":"I","Ï":"I","Ḯ":"I","İ":"I","Ị":"I","Ȉ":"I","Ì":"I","Ỉ":"I","Ȋ":"I","Ī":"I","Į":"I","Ɨ":"I","Ĩ":"I","Ḭ":"I","Ꝺ":"D","Ꝼ":"F","Ᵹ":"G","Ꞃ":"R","Ꞅ":"S","Ꞇ":"T","Ꝭ":"IS","Ĵ":"J","Ɉ":"J","Ḱ":"K","Ǩ":"K","Ķ":"K","Ⱪ":"K","Ꝃ":"K","Ḳ":"K","Ƙ":"K","Ḵ":"K","Ꝁ":"K","Ꝅ":"K","Ĺ":"L","Ƚ":"L","Ľ":"L","Ļ":"L","Ḽ":"L","Ḷ":"L","Ḹ":"L","Ⱡ":"L","Ꝉ":"L","Ḻ":"L","Ŀ":"L","Ɫ":"L","Lj":"L","Ł":"L","LJ":"LJ","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ń":"N","Ň":"N","Ņ":"N","Ṋ":"N","Ṅ":"N","Ṇ":"N","Ǹ":"N","Ɲ":"N","Ṉ":"N","Ƞ":"N","Nj":"N","Ñ":"N","NJ":"NJ","Ó":"O","Ŏ":"O","Ǒ":"O","Ô":"O","Ố":"O","Ộ":"O","Ồ":"O","Ổ":"O","Ỗ":"O","Ö":"O","Ȫ":"O","Ȯ":"O","Ȱ":"O","Ọ":"O","Ő":"O","Ȍ":"O","Ò":"O","Ỏ":"O","Ơ":"O","Ớ":"O","Ợ":"O","Ờ":"O","Ở":"O","Ỡ":"O","Ȏ":"O","Ꝋ":"O","Ꝍ":"O","Ō":"O","Ṓ":"O","Ṑ":"O","Ɵ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Õ":"O","Ṍ":"O","Ṏ":"O","Ȭ":"O","Ƣ":"OI","Ꝏ":"OO","Ɛ":"E","Ɔ":"O","Ȣ":"OU","Ṕ":"P","Ṗ":"P","Ꝓ":"P","Ƥ":"P","Ꝕ":"P","Ᵽ":"P","Ꝑ":"P","Ꝙ":"Q","Ꝗ":"Q","Ŕ":"R","Ř":"R","Ŗ":"R","Ṙ":"R","Ṛ":"R","Ṝ":"R","Ȑ":"R","Ȓ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꜿ":"C","Ǝ":"E","Ś":"S","Ṥ":"S","Š":"S","Ṧ":"S","Ş":"S","Ŝ":"S","Ș":"S","Ṡ":"S","Ṣ":"S","Ṩ":"S","ẞ":"SS","Ť":"T","Ţ":"T","Ṱ":"T","Ț":"T","Ⱦ":"T","Ṫ":"T","Ṭ":"T","Ƭ":"T","Ṯ":"T","Ʈ":"T","Ŧ":"T","Ɐ":"A","Ꞁ":"L","Ɯ":"M","Ʌ":"V","Ꜩ":"TZ","Ú":"U","Ŭ":"U","Ǔ":"U","Û":"U","Ṷ":"U","Ü":"U","Ǘ":"U","Ǚ":"U","Ǜ":"U","Ǖ":"U","Ṳ":"U","Ụ":"U","Ű":"U","Ȕ":"U","Ù":"U","Ủ":"U","Ư":"U","Ứ":"U","Ự":"U","Ừ":"U","Ử":"U","Ữ":"U","Ȗ":"U","Ū":"U","Ṻ":"U","Ų":"U","Ů":"U","Ũ":"U","Ṹ":"U","Ṵ":"U","Ꝟ":"V","Ṿ":"V","Ʋ":"V","Ṽ":"V","Ꝡ":"VY","Ẃ":"W","Ŵ":"W","Ẅ":"W","Ẇ":"W","Ẉ":"W","Ẁ":"W","Ⱳ":"W","Ẍ":"X","Ẋ":"X","Ý":"Y","Ŷ":"Y","Ÿ":"Y","Ẏ":"Y","Ỵ":"Y","Ỳ":"Y","Ƴ":"Y","Ỷ":"Y","Ỿ":"Y","Ȳ":"Y","Ɏ":"Y","Ỹ":"Y","Ź":"Z","Ž":"Z","Ẑ":"Z","Ⱬ":"Z","Ż":"Z","Ẓ":"Z","Ȥ":"Z","Ẕ":"Z","Ƶ":"Z","IJ":"IJ","Œ":"OE","ᴀ":"A","ᴁ":"AE","ʙ":"B","ᴃ":"B","ᴄ":"C","ᴅ":"D","ᴇ":"E","ꜰ":"F","ɢ":"G","ʛ":"G","ʜ":"H","ɪ":"I","ʁ":"R","ᴊ":"J","ᴋ":"K","ʟ":"L","ᴌ":"L","ᴍ":"M","ɴ":"N","ᴏ":"O","ɶ":"OE","ᴐ":"O","ᴕ":"OU","ᴘ":"P","ʀ":"R","ᴎ":"N","ᴙ":"R","ꜱ":"S","ᴛ":"T","ⱻ":"E","ᴚ":"R","ᴜ":"U","ᴠ":"V","ᴡ":"W","ʏ":"Y","ᴢ":"Z","á":"a","ă":"a","ắ":"a","ặ":"a","ằ":"a","ẳ":"a","ẵ":"a","ǎ":"a","â":"a","ấ":"a","ậ":"a","ầ":"a","ẩ":"a","ẫ":"a","ä":"a","ǟ":"a","ȧ":"a","ǡ":"a","ạ":"a","ȁ":"a","à":"a","ả":"a","ȃ":"a","ā":"a","ą":"a","ᶏ":"a","ẚ":"a","å":"a","ǻ":"a","ḁ":"a","ⱥ":"a","ã":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ḃ":"b","ḅ":"b","ɓ":"b","ḇ":"b","ᵬ":"b","ᶀ":"b","ƀ":"b","ƃ":"b","ɵ":"o","ć":"c","č":"c","ç":"c","ḉ":"c","ĉ":"c","ɕ":"c","ċ":"c","ƈ":"c","ȼ":"c","ď":"d","ḑ":"d","ḓ":"d","ȡ":"d","ḋ":"d","ḍ":"d","ɗ":"d","ᶑ":"d","ḏ":"d","ᵭ":"d","ᶁ":"d","đ":"d","ɖ":"d","ƌ":"d","ı":"i","ȷ":"j","ɟ":"j","ʄ":"j","dz":"dz","dž":"dz","é":"e","ĕ":"e","ě":"e","ȩ":"e","ḝ":"e","ê":"e","ế":"e","ệ":"e","ề":"e","ể":"e","ễ":"e","ḙ":"e","ë":"e","ė":"e","ẹ":"e","ȅ":"e","è":"e","ẻ":"e","ȇ":"e","ē":"e","ḗ":"e","ḕ":"e","ⱸ":"e","ę":"e","ᶒ":"e","ɇ":"e","ẽ":"e","ḛ":"e","ꝫ":"et","ḟ":"f","ƒ":"f","ᵮ":"f","ᶂ":"f","ǵ":"g","ğ":"g","ǧ":"g","ģ":"g","ĝ":"g","ġ":"g","ɠ":"g","ḡ":"g","ᶃ":"g","ǥ":"g","ḫ":"h","ȟ":"h","ḩ":"h","ĥ":"h","ⱨ":"h","ḧ":"h","ḣ":"h","ḥ":"h","ɦ":"h","ẖ":"h","ħ":"h","ƕ":"hv","í":"i","ĭ":"i","ǐ":"i","î":"i","ï":"i","ḯ":"i","ị":"i","ȉ":"i","ì":"i","ỉ":"i","ȋ":"i","ī":"i","į":"i","ᶖ":"i","ɨ":"i","ĩ":"i","ḭ":"i","ꝺ":"d","ꝼ":"f","ᵹ":"g","ꞃ":"r","ꞅ":"s","ꞇ":"t","ꝭ":"is","ǰ":"j","ĵ":"j","ʝ":"j","ɉ":"j","ḱ":"k","ǩ":"k","ķ":"k","ⱪ":"k","ꝃ":"k","ḳ":"k","ƙ":"k","ḵ":"k","ᶄ":"k","ꝁ":"k","ꝅ":"k","ĺ":"l","ƚ":"l","ɬ":"l","ľ":"l","ļ":"l","ḽ":"l","ȴ":"l","ḷ":"l","ḹ":"l","ⱡ":"l","ꝉ":"l","ḻ":"l","ŀ":"l","ɫ":"l","ᶅ":"l","ɭ":"l","ł":"l","lj":"lj","ſ":"s","ẜ":"s","ẛ":"s","ẝ":"s","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ᵯ":"m","ᶆ":"m","ń":"n","ň":"n","ņ":"n","ṋ":"n","ȵ":"n","ṅ":"n","ṇ":"n","ǹ":"n","ɲ":"n","ṉ":"n","ƞ":"n","ᵰ":"n","ᶇ":"n","ɳ":"n","ñ":"n","nj":"nj","ó":"o","ŏ":"o","ǒ":"o","ô":"o","ố":"o","ộ":"o","ồ":"o","ổ":"o","ỗ":"o","ö":"o","ȫ":"o","ȯ":"o","ȱ":"o","ọ":"o","ő":"o","ȍ":"o","ò":"o","ỏ":"o","ơ":"o","ớ":"o","ợ":"o","ờ":"o","ở":"o","ỡ":"o","ȏ":"o","ꝋ":"o","ꝍ":"o","ⱺ":"o","ō":"o","ṓ":"o","ṑ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","õ":"o","ṍ":"o","ṏ":"o","ȭ":"o","ƣ":"oi","ꝏ":"oo","ɛ":"e","ᶓ":"e","ɔ":"o","ᶗ":"o","ȣ":"ou","ṕ":"p","ṗ":"p","ꝓ":"p","ƥ":"p","ᵱ":"p","ᶈ":"p","ꝕ":"p","ᵽ":"p","ꝑ":"p","ꝙ":"q","ʠ":"q","ɋ":"q","ꝗ":"q","ŕ":"r","ř":"r","ŗ":"r","ṙ":"r","ṛ":"r","ṝ":"r","ȑ":"r","ɾ":"r","ᵳ":"r","ȓ":"r","ṟ":"r","ɼ":"r","ᵲ":"r","ᶉ":"r","ɍ":"r","ɽ":"r","ↄ":"c","ꜿ":"c","ɘ":"e","ɿ":"r","ś":"s","ṥ":"s","š":"s","ṧ":"s","ş":"s","ŝ":"s","ș":"s","ṡ":"s","ṣ":"s","ṩ":"s","ʂ":"s","ᵴ":"s","ᶊ":"s","ȿ":"s","ɡ":"g","ß":"ss","ᴑ":"o","ᴓ":"o","ᴝ":"u","ť":"t","ţ":"t","ṱ":"t","ț":"t","ȶ":"t","ẗ":"t","ⱦ":"t","ṫ":"t","ṭ":"t","ƭ":"t","ṯ":"t","ᵵ":"t","ƫ":"t","ʈ":"t","ŧ":"t","ᵺ":"th","ɐ":"a","ᴂ":"ae","ǝ":"e","ᵷ":"g","ɥ":"h","ʮ":"h","ʯ":"h","ᴉ":"i","ʞ":"k","ꞁ":"l","ɯ":"m","ɰ":"m","ᴔ":"oe","ɹ":"r","ɻ":"r","ɺ":"r","ⱹ":"r","ʇ":"t","ʌ":"v","ʍ":"w","ʎ":"y","ꜩ":"tz","ú":"u","ŭ":"u","ǔ":"u","û":"u","ṷ":"u","ü":"u","ǘ":"u","ǚ":"u","ǜ":"u","ǖ":"u","ṳ":"u","ụ":"u","ű":"u","ȕ":"u","ù":"u","ủ":"u","ư":"u","ứ":"u","ự":"u","ừ":"u","ử":"u","ữ":"u","ȗ":"u","ū":"u","ṻ":"u","ų":"u","ᶙ":"u","ů":"u","ũ":"u","ṹ":"u","ṵ":"u","ᵫ":"ue","ꝸ":"um","ⱴ":"v","ꝟ":"v","ṿ":"v","ʋ":"v","ᶌ":"v","ⱱ":"v","ṽ":"v","ꝡ":"vy","ẃ":"w","ŵ":"w","ẅ":"w","ẇ":"w","ẉ":"w","ẁ":"w","ⱳ":"w","ẘ":"w","ẍ":"x","ẋ":"x","ᶍ":"x","ý":"y","ŷ":"y","ÿ":"y","ẏ":"y","ỵ":"y","ỳ":"y","ƴ":"y","ỷ":"y","ỿ":"y","ȳ":"y","ẙ":"y","ɏ":"y","ỹ":"y","ź":"z","ž":"z","ẑ":"z","ʑ":"z","ⱬ":"z","ż":"z","ẓ":"z","ȥ":"z","ẕ":"z","ᵶ":"z","ᶎ":"z","ʐ":"z","ƶ":"z","ɀ":"z","ff":"ff","ffi":"ffi","ffl":"ffl","fi":"fi","fl":"fl","ij":"ij","œ":"oe","st":"st","ₐ":"a","ₑ":"e","ᵢ":"i","ⱼ":"j","ₒ":"o","ᵣ":"r","ᵤ":"u","ᵥ":"v","ₓ":"x"}; |
40796 }) |
41626 }) |
40797 |
41627 |
40798 return new this.constructor(s); |
41628 return new this.constructor(s); |
40799 }, |
41629 }, |
40800 |
41630 |
40801 endsWith: function(suffix) { |
41631 endsWith: function() { |
40802 var l = this.s.length - suffix.length; |
41632 var suffixes = Array.prototype.slice.call(arguments, 0); |
40803 return l >= 0 && this.s.indexOf(suffix, l) === l; |
41633 for (var i = 0; i < suffixes.length; ++i) { |
41634 var l = this.s.length - suffixes[i].length; |
|
41635 if (l >= 0 && this.s.indexOf(suffixes[i], l) === l) return true; |
|
41636 } |
|
41637 return false; |
|
40804 }, |
41638 }, |
40805 |
41639 |
40806 escapeHTML: function() { //from underscore.string |
41640 escapeHTML: function() { //from underscore.string |
40807 return new this.constructor(this.s.replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; })); |
41641 return new this.constructor(this.s.replace(/[&<>"']/g, function(m){ return '&' + reversedEscapeChars[m] + ';'; })); |
40808 }, |
41642 }, |
41007 if (sl.charAt(0) === '-') |
41841 if (sl.charAt(0) === '-') |
41008 sl = sl.substr(1); |
41842 sl = sl.substr(1); |
41009 return new this.constructor(sl); |
41843 return new this.constructor(sl); |
41010 }, |
41844 }, |
41011 |
41845 |
41012 startsWith: function(prefix) { |
41846 startsWith: function() { |
41013 return this.s.lastIndexOf(prefix, 0) === 0; |
41847 var prefixes = Array.prototype.slice.call(arguments, 0); |
41848 for (var i = 0; i < prefixes.length; ++i) { |
|
41849 if (this.s.lastIndexOf(prefixes[i], 0) === 0) return true; |
|
41850 } |
|
41851 return false; |
|
41014 }, |
41852 }, |
41015 |
41853 |
41016 stripPunctuation: function() { |
41854 stripPunctuation: function() { |
41017 //return new this.constructor(this.s.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g,"")); |
41855 //return new this.constructor(this.s.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()]/g,"")); |
41018 return new this.constructor(this.s.replace(/[^\w\s]|_/g, "").replace(/\s+/g, " ")); |
41856 return new this.constructor(this.s.replace(/[^\w\s]|_/g, "").replace(/\s+/g, " ")); |
41036 var r = new RegExp(open + '(.+?)' + close, 'g') |
41874 var r = new RegExp(open + '(.+?)' + close, 'g') |
41037 //, r = /\{\{(.+?)\}\}/g |
41875 //, r = /\{\{(.+?)\}\}/g |
41038 var matches = s.match(r) || []; |
41876 var matches = s.match(r) || []; |
41039 |
41877 |
41040 matches.forEach(function(match) { |
41878 matches.forEach(function(match) { |
41041 var key = match.substring(opening.length, match.length - closing.length);//chop {{ and }} |
41879 var key = match.substring(opening.length, match.length - closing.length).trim();//chop {{ and }} |
41042 if (typeof values[key] != 'undefined') |
41880 var value = typeof values[key] == 'undefined' ? '' : values[key]; |
41043 s = s.replace(match, values[key]); |
41881 s = s.replace(match, value); |
41044 }); |
41882 }); |
41045 return new this.constructor(s); |
41883 return new this.constructor(s); |
41046 }, |
41884 }, |
41047 |
41885 |
41048 times: function(n) { |
41886 times: function(n) { |
41186 }, |
42024 }, |
41187 |
42025 |
41188 //#modified from https://github.com/epeli/underscore.string |
42026 //#modified from https://github.com/epeli/underscore.string |
41189 underscore: function() { |
42027 underscore: function() { |
41190 var s = this.trim().s.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase(); |
42028 var s = this.trim().s.replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase(); |
41191 if ((new S(this.s.charAt(0))).isUpper()) { |
|
41192 s = '_' + s; |
|
41193 } |
|
41194 return new this.constructor(s); |
42029 return new this.constructor(s); |
41195 }, |
42030 }, |
41196 |
42031 |
41197 unescapeHTML: function() { //from underscore.string |
42032 unescapeHTML: function() { //from underscore.string |
41198 return new this.constructor(this.s.replace(/\&([^;]+);/g, function(entity, entityCode){ |
42033 return new this.constructor(this.s.replace(/\&([^;]+);/g, function(entity, entityCode){ |