3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
17 * Modified version of Douglas Crockford"s json.js that doesn"t
18 * mess with the Object prototype
19 * http://www.json.org/js.html
22 Ext.JSON = new(function() {
23 var useHasOwn = !! {}.hasOwnProperty,
24 isNative = function() {
28 if (useNative === null) {
29 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
36 return n < 10 ? "0" + n : n;
38 doDecode = function(json) {
39 return eval("(" + json + ')');
41 doEncode = function(o) {
42 if (!Ext.isDefined(o) || o === null) {
44 } else if (Ext.isArray(o)) {
45 return encodeArray(o);
46 } else if (Ext.isDate(o)) {
47 return Ext.JSON.encodeDate(o);
48 } else if (Ext.isString(o)) {
49 return encodeString(o);
50 } else if (typeof o == "number") {
51 //don't use isNumber here, since finite checks happen inside isNumber
52 return isFinite(o) ? String(o) : "null";
53 } else if (Ext.isBoolean(o)) {
55 } else if (Ext.isObject(o)) {
56 return encodeObject(o);
57 } else if (typeof o === "function") {
70 '\x0b': '\\u000b' //ie doesn't handle \v
72 charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
73 encodeString = function(s) {
74 return '"' + s.replace(charToReplace, function(a) {
76 return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
79 encodeArray = function(o) {
81 // Note empty string in case there are no serializable members.
84 for (i = 0; i < len; i += 1) {
85 a.push(doEncode(o[i]), ',');
87 // Overwrite trailing comma (or empty string)
88 a[a.length - 1] = ']';
91 encodeObject = function(o) {
93 // Note empty string in case there are no serializable members.
96 if (!useHasOwn || o.hasOwnProperty(i)) {
97 a.push(doEncode(i), ":", doEncode(o[i]), ',');
100 // Overwrite trailing comma (or empty string)
101 a[a.length - 1] = '}';
106 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
107 * <b>The returned value includes enclosing double quotation marks.</b></p>
108 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
109 * <p>To override this:</p><pre><code>
110 Ext.JSON.encodeDate = function(d) {
111 return d.format('"Y-m-d"');
114 * @param {Date} d The Date to encode
115 * @return {String} The string literal to use in a JSON string.
117 this.encodeDate = function(o) {
118 return '"' + o.getFullYear() + "-"
119 + pad(o.getMonth() + 1) + "-"
120 + pad(o.getDate()) + "T"
121 + pad(o.getHours()) + ":"
122 + pad(o.getMinutes()) + ":"
123 + pad(o.getSeconds()) + '"';
127 * Encodes an Object, Array or other value
128 * @param {Mixed} o The variable to encode
129 * @return {String} The JSON string
131 this.encode = function() {
135 // setup encoding function on first access
136 ec = isNative() ? JSON.stringify : doEncode;
144 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
145 * @param {String} json The JSON string
146 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
147 * @return {Object} The resulting object
149 this.decode = function() {
151 return function(json, safe) {
153 // setup decoding function on first access
154 dc = isNative() ? JSON.parse : doDecode;
163 sourceClass: "Ext.JSON",
164 sourceMethod: "decode",
165 msg: "You're trying to decode an invalid JSON String: " + json
173 * Shorthand for {@link Ext.JSON#encode}
174 * @param {Mixed} o The variable to encode
175 * @return {String} The JSON string
179 Ext.encode = Ext.JSON.encode;
181 * Shorthand for {@link Ext.JSON#decode}
182 * @param {String} json The JSON string
183 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
184 * @return {Object} The resulting object
188 Ext.decode = Ext.JSON.decode;
194 The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
195 Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
196 as direct properties of the Ext namespace.
198 Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
199 For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
201 Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
202 This ensures all scripts have been loaded, preventing dependency issues. For example
204 Ext.onReady(function(){
206 renderTo: document.body,
211 For more information about how to use the Ext classes, see
213 - <a href="http://www.sencha.com/learn/">The Learning Center</a>
214 - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
215 - <a href="http://www.sencha.com/forum/">The forums</a>
221 userAgent: navigator.userAgent.toLowerCase(),
224 BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
225 isStrict: document.compatMode == "CSS1Compat",
226 windowId: 'ext-window',
227 documentId: 'ext-document',
230 * True when the document is fully initialized and ready for action
236 * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true)
239 enableGarbageCollector: true,
242 * True to automatically purge event listeners during garbageCollection (defaults to true).
245 enableListenerCollection: true,
248 * Generates unique ids. If the element already has an id, it is unchanged
249 * @param {Mixed} el (optional) The element to generate an id for
250 * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
251 * @return {String} The generated Id.
253 id: function(el, prefix) {
256 el = Ext.getDom(el, true) || {};
257 if (el === document) {
258 el.id = me.documentId;
260 else if (el === window) {
264 if (me.isSandboxed) {
265 if (!me.uniqueGlobalNamespace) {
266 me.getUniqueGlobalNamespace();
268 sandboxPrefix = me.uniqueGlobalNamespace + '-';
270 el.id = sandboxPrefix + (prefix || "ext-gen") + (++Ext.idSeed);
276 * Returns the current document body as an {@link Ext.core.Element}.
277 * @return Ext.core.Element The document body
279 getBody: function() {
280 return Ext.get(document.body || false);
284 * Returns the current document head as an {@link Ext.core.Element}.
285 * @return Ext.core.Element The document head
288 getHead: function() {
292 if (head == undefined) {
293 head = Ext.get(document.getElementsByTagName("head")[0]);
301 * Returns the current HTML document object as an {@link Ext.core.Element}.
302 * @return Ext.core.Element The document
305 return Ext.get(document);
309 * This is shorthand reference to {@link Ext.ComponentManager#get}.
310 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
311 * @param {String} id The component {@link Ext.Component#id id}
312 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
315 getCmp: function(id) {
316 return Ext.ComponentManager.get(id);
320 * Returns the current orientation of the mobile device
321 * @return {String} Either 'portrait' or 'landscape'
323 getOrientation: function() {
324 return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
328 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
329 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
330 * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of
331 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
332 * passed into this function in a single call as separate arguments.
333 * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy
334 * @param {Mixed} arg2 (optional)
335 * @param {Mixed} etc... (optional)
337 destroy: function() {
338 var ln = arguments.length,
341 for (i = 0; i < ln; i++) {
344 if (Ext.isArray(arg)) {
345 this.destroy.apply(this, arg);
347 else if (Ext.isFunction(arg.destroy)) {
358 * Execute a callback function in a particular scope. If no function is passed the call is ignored.
360 * For example, these lines are equivalent:
362 * Ext.callback(myFunc, this, [arg1, arg2]);
363 * Ext.isFunction(myFunc) && myFunc.apply(this, [arg1, arg2]);
365 * @param {Function} callback The callback to execute
366 * @param {Object} scope (optional) The scope to execute in
367 * @param {Array} args (optional) The arguments to pass to the function
368 * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
370 callback: function(callback, scope, args, delay){
371 if(Ext.isFunction(callback)){
373 scope = scope || window;
375 Ext.defer(callback, delay, scope, args);
377 callback.apply(scope, args);
383 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
384 * @param {String} value The string to encode
385 * @return {String} The encoded text
387 htmlEncode : function(value) {
388 return Ext.String.htmlEncode(value);
392 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
393 * @param {String} value The string to decode
394 * @return {String} The decoded text
396 htmlDecode : function(value) {
397 return Ext.String.htmlDecode(value);
401 * Appends content to the query string of a URL, handling logic for whether to place
402 * a question mark or ampersand.
403 * @param {String} url The URL to append to.
404 * @param {String} s The content to append to the URL.
405 * @return (String) The resulting URL
407 urlAppend : function(url, s) {
408 if (!Ext.isEmpty(s)) {
409 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
416 Ext.ns = Ext.namespace;
419 window.undefined = window.undefined;
423 * Ext core utilities and functions.
427 var check = function(regex){
428 return regex.test(Ext.userAgent);
430 docMode = document.documentMode,
431 isOpera = check(/opera/),
432 isOpera10_5 = isOpera && check(/version\/10\.5/),
433 isChrome = check(/\bchrome\b/),
434 isWebKit = check(/webkit/),
435 isSafari = !isChrome && check(/safari/),
436 isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
437 isSafari3 = isSafari && check(/version\/3/),
438 isSafari4 = isSafari && check(/version\/4/),
439 isIE = !isOpera && check(/msie/),
440 isIE7 = isIE && (check(/msie 7/) || docMode == 7),
441 isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
442 isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
443 isIE6 = isIE && check(/msie 6/),
444 isGecko = !isWebKit && check(/gecko/),
445 isGecko3 = isGecko && check(/rv:1\.9/),
446 isGecko4 = isGecko && check(/rv:2\.0/),
447 isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
448 isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
449 isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
450 isWindows = check(/windows|win32/),
451 isMac = check(/macintosh|mac os x/),
452 isLinux = check(/linux/),
453 scrollbarSize = null,
454 webKitVersion = isWebKit && (/webkit\/(\d+\.\d+)/.exec(Ext.userAgent));
456 // remove css image flicker
458 document.execCommand("BackgroundImageCache", false, true);
461 Ext.setVersion('extjs', '4.0.2');
464 * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
465 * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
468 SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',
471 * True if the {@link Ext.fx.Anim} Class is available
477 * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
478 * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
479 * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
482 scopeResetCSS : Ext.buildSettings.scopeResetCSS,
485 * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
486 * Currently not optimized for performance.
489 enableNestedListenerRemoval : false,
492 * Indicates whether to use native browser parsing for JSON methods.
493 * This option is ignored if the browser does not support native JSON methods.
494 * <b>Note: Native JSON methods will not work with objects that have functions.
495 * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
498 USE_NATIVE_JSON : false,
501 * Return the dom node for the passed String (id), dom node, or Ext.core.Element.
502 * Optional 'strict' flag is needed for IE since it can return 'name' and
503 * 'id' elements by using getElementById.
504 * Here are some examples:
506 // gets dom node based on id
507 var elDom = Ext.getDom('elId');
508 // gets dom node based on the dom node
509 var elDom1 = Ext.getDom(elDom);
511 // If we don't know if we are working with an
512 // Ext.core.Element or a dom node use Ext.getDom
514 var dom = Ext.getDom(el);
515 // do something with the dom node
518 * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
519 * when this method is called to be successful.
521 * @return HTMLElement
523 getDom : function(el, strict) {
524 if (!el || !document) {
530 if (typeof el == 'string') {
531 var e = document.getElementById(el);
532 // IE returns elements with the 'name' and 'id' attribute.
533 // we do a strict check to return the element with only the id attribute
534 if (e && isIE && strict) {
535 if (el == e.getAttribute('id')) {
549 * Removes a DOM node from the document.
550 * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
551 * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
552 * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
553 * will be ignored if passed in.</p>
554 * @param {HTMLElement} node The node to remove
557 removeNode : isIE6 || isIE7 ? function() {
560 if(n && n.tagName != 'BODY'){
561 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
562 d = d || document.createElement('div');
565 delete Ext.cache[n.id];
569 if (n && n.parentNode && n.tagName != 'BODY') {
570 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
571 n.parentNode.removeChild(n);
572 delete Ext.cache[n.id];
577 * True if the detected browser is Opera.
583 * True if the detected browser is Opera 10.5x.
586 isOpera10_5 : isOpera10_5,
589 * True if the detected browser uses WebKit.
595 * True if the detected browser is Chrome.
601 * True if the detected browser is Safari.
607 * True if the detected browser is Safari 3.x.
610 isSafari3 : isSafari3,
613 * True if the detected browser is Safari 4.x.
616 isSafari4 : isSafari4,
619 * True if the detected browser is Safari 2.x.
622 isSafari2 : isSafari2,
625 * True if the detected browser is Internet Explorer.
631 * True if the detected browser is Internet Explorer 6.x.
637 * True if the detected browser is Internet Explorer 7.x.
643 * True if the detected browser is Internet Explorer 8.x.
649 * True if the detected browser is Internet Explorer 9.x.
655 * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
661 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
667 * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
673 * True if the detected browser uses FireFox 3.0
679 * True if the detected browser uses FireFox 3.5
685 * True if the detected browser uses FireFox 3.6
691 * True if the detected platform is Linux.
697 * True if the detected platform is Windows.
700 isWindows : isWindows,
703 * True if the detected platform is Mac OS.
709 * The current version of WebKit (-1 if the browser does not use WebKit).
712 webKitVersion: webKitVersion ? parseFloat(webKitVersion[1]) : -1,
715 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
716 * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
717 * For other browsers it uses an inline data URL.
720 BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
723 * <p>Utility method for returning a default value if the passed value is empty.</p>
724 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
727 * <li>an empty array</li>
728 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
730 * @param {Mixed} value The value to test
731 * @param {Mixed} defaultValue The value to return if the original value is empty
732 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
733 * @return {Mixed} value, if non-empty, else defaultValue
734 * @deprecated 4.0.0 Use {@link Ext#valueFrom} instead
736 value : function(v, defaultValue, allowBlank){
737 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
741 * Escapes the passed string for use in a regular expression
742 * @param {String} str
744 * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
746 escapeRe : function(s) {
747 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
751 * Applies event listeners to elements by selectors when the document is ready.
752 * The event name is specified with an <tt>@</tt> suffix.
755 // add a listener for click on all anchors in element with id foo
756 '#foo a@click' : function(e, t){
760 // add the same listener to multiple selectors (separated by comma BEFORE the @)
761 '#foo a, #bar span.some-class@mouseover' : function(){
766 * @param {Object} obj The list of behaviors to apply
768 addBehaviors : function(o){
770 Ext.onReady(function(){
774 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
779 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
782 cache[s] = Ext.select(s);
784 cache[s].on(parts[1], o[b]);
792 * Returns the size of the browser scrollbars. This can differ depending on
793 * operating system settings, such as the theme or font size.
794 * @param {Boolean} force (optional) true to force a recalculation of the value.
795 * @return {Object} An object containing the width of a vertical scrollbar and the
796 * height of a horizontal scrollbar.
798 getScrollbarSize: function (force) {
803 if(force === true || scrollbarSize === null){
805 // When IE9 positions an element offscreen via offsets, the offsetWidth is
806 // inaccurately reported. For IE9 only, we render on screen before removing.
807 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets',
808 // Append our div, do our calculation and then remove it
809 div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
810 child = div.child('div', true),
811 w1 = child.offsetWidth;
813 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
815 var w2 = child.offsetWidth, width = w1 - w2;
818 // We assume width == height for now. TODO: is this always true?
819 scrollbarSize = { width: width, height: width };
822 return scrollbarSize;
826 * Utility method for getting the width of the browser's vertical scrollbar. This
827 * can differ depending on operating system settings, such as the theme or font size.
829 * This method is deprected in favor of {@link #getScrollbarSize}.
831 * @param {Boolean} force (optional) true to force a recalculation of the value.
832 * @return {Number} The width of a vertical scrollbar.
835 getScrollBarWidth: function(force){
836 var size = Ext.getScrollbarSize(force);
837 return size.width + 2; // legacy fudge factor
841 * Copies a set of named properties fom the source object to the destination object.
845 * ImageComponent = Ext.extend(Ext.Component, {
846 * initComponent: function() {
847 * this.autoEl = { tag: 'img' };
848 * MyComponent.superclass.initComponent.apply(this, arguments);
849 * this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
853 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
855 * @param {Object} dest The destination object.
856 * @param {Object} source The source object.
857 * @param {Array/String} names Either an Array of property names, or a comma-delimited list
858 * of property names to copy.
859 * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
860 * @return {Object} The modified object.
862 copyTo : function(dest, source, names, usePrototypeKeys){
863 if(typeof names == 'string'){
864 names = names.split(/[,;\s]/);
866 Ext.each(names, function(name){
867 if(usePrototypeKeys || source.hasOwnProperty(name)){
868 dest[name] = source[name];
875 * Attempts to destroy and then remove a set of named properties of the passed object.
876 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
877 * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
878 * @param {Mixed} etc... More property names to destroy and remove.
880 destroyMembers : function(o){
881 for (var i = 1, a = arguments, len = a.length; i < len; i++) {
882 Ext.destroy(o[a[i]]);
888 * Logs a message. If a console is present it will be used. On Opera, the method
889 * "opera.postError" is called. In other cases, the message is logged to an array
890 * "Ext.log.out". An attached debugger can watch this array and view the log. The
891 * log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 100).
893 * If additional parameters are passed, they are joined and appended to the message.
895 * This method does nothing in a release build.
897 * @param {String|Object} message The message to log or an options object with any
898 * of the following properties:
900 * - `msg`: The message to log (required).
901 * - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
902 * - `dump`: An object to dump to the log as part of the message.
903 * - `stack`: True to include a stack trace in the log.
906 log : function (message) {
909 con = Ext.global.console,
916 if (!Ext.isString(message)) {
918 message = options.msg || '';
919 level = options.level || level;
921 stack = options.stack;
923 if (dump && !(con && con.dir)) {
926 // Cannot use Ext.encode since it can recurse endlessly (if we're lucky)
927 // ...and the data could be prettier!
928 Ext.Object.each(dump, function (name, value) {
929 if (typeof(value) === "function") {
933 if (!Ext.isDefined(value) || value === null ||
935 Ext.isString(value) || (typeof(value) == "number") ||
936 Ext.isBoolean(value)) {
937 member = Ext.encode(value);
938 } else if (Ext.isArray(value)) {
940 } else if (Ext.isObject(value)) {
943 member = 'undefined';
945 members.push(Ext.encode(name) + ': ' + member);
948 if (members.length) {
949 message += ' \nData: {\n ' + members.join(',\n ') + '\n}';
955 if (arguments.length > 1) {
956 message += Array.prototype.slice.call(arguments, 1).join('');
959 // Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
960 // an early test may fail either direction if Firebug is toggled.
962 if (con) { // if (Firebug-like console)
973 if (stack && con.trace) {
974 // Firebug's console.error() includes a trace already...
975 if (!con.firebug || level != 'error') {
980 // w/o console, all messages are equal, so munge the level into the message:
981 if (level != 'log') {
982 message = level.toUpperCase() + ': ' + message;
986 opera.postError(message);
988 var out = log.out || (log.out = []),
989 max = log.max || (log.max = 100);
991 if (out.length >= max) {
992 // this formula allows out.max to change (via debugger), where the
993 // more obvious "max/4" would not quite be the same
994 Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4)); // keep newest 75%
1001 // Mostly informational, but the Ext.Error notifier uses them:
1002 var counters = log.counters ||
1003 (log.counters = { error: 0, warn: 0, info: 0, log: 0 });
1010 * Partitions the set into two sets: a true set and a false set.
1015 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
1021 return val.className == "class1"
1024 // true are those paragraph elements with a className of "class1",
1025 // false set are those that do not have that className.
1027 * @param {Array|NodeList} arr The array to partition
1028 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
1029 * itself must be able to be evaluated for its truthfulness.
1030 * @return {Array} [array of truish values, array of falsy values]
1031 * @deprecated 4.0.0 Will be removed in the next major version
1033 partition : function(arr, truth){
1035 Ext.each(arr, function(v, i, a) {
1036 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
1042 * Invokes a method on each item in an Array.
1045 Ext.invoke(Ext.query("p"), "getAttribute", "id");
1046 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
1048 * @param {Array|NodeList} arr The Array of items to invoke the method on.
1049 * @param {String} methodName The method name to invoke.
1050 * @param {...*} args Arguments to send into the method invocation.
1051 * @return {Array} The results of invoking the method on each item in the array.
1052 * @deprecated 4.0.0 Will be removed in the next major version
1054 invoke : function(arr, methodName){
1056 args = Array.prototype.slice.call(arguments, 2);
1057 Ext.each(arr, function(v,i) {
1058 if (v && typeof v[methodName] == 'function') {
1059 ret.push(v[methodName].apply(v, args));
1061 ret.push(undefined);
1068 * <p>Zips N sets together.</p>
1071 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
1078 return "$" + a + "" + b + "." + c
1080 ); // ["$+12.43", "$-10.15", "$+22.96"]
1082 * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
1083 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
1084 * @return {Array} The zipped set.
1085 * @deprecated 4.0.0 Will be removed in the next major version
1088 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
1091 len = Ext.max(Ext.pluck(arrs, "length")),
1094 for (var i = 0; i < len; i++) {
1097 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
1099 for (var j = 0, aLen = arrs.length; j < aLen; j++){
1100 ret[i].push( arrs[j][i] );
1108 * Turns an array into a sentence, joined by a specified connector - e.g.:
1109 * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
1110 * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
1111 * @param {Array} items The array to create a sentence from
1112 * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
1113 * @return {String} The sentence string
1114 * @deprecated 4.0.0 Will be removed in the next major version
1116 toSentence: function(items, connector) {
1117 var length = items.length;
1122 var head = items.slice(0, length - 1),
1123 tail = items[length - 1];
1125 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
1130 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
1131 * you may want to set this to true.
1139 * Loads Ext.app.Application class and starts it up with given configuration after the page is ready.
1141 * See Ext.app.Application for details.
1143 * @param {Object} config
1145 Ext.application = function(config) {
1146 Ext.require('Ext.app.Application');
1148 Ext.onReady(function() {
1149 Ext.create('Ext.app.Application', config);
1154 * @class Ext.util.Format
1156 This class is a centralized place for formatting functions inside the library. It includes
1157 functions to format various different types of data, such as text, dates and numeric values.
1160 This class contains several options for localization. These can be set once the library has loaded,
1161 all calls to the functions from that point will use the locale settings that were specified.
1168 This class also uses the default date format defined here: {@link Ext.Date#defaultFormat}.
1170 __Using with renderers__
1171 There are two helper functions that return a new function that can be used in conjunction with
1176 renderer: Ext.util.Format.dateRenderer('Y-m-d')
1179 renderer: Ext.util.Format.numberRenderer('0.000')
1182 Functions that only take a single argument can also be passed directly:
1185 renderer: Ext.util.Format.usMoney
1187 dataIndex: 'productCode',
1188 renderer: Ext.util.Format.uppercase
1191 __Using with XTemplates__
1192 XTemplates can also directly use Ext.util.Format functions:
1195 'Date: {startDate:date("Y-m-d")}',
1196 'Cost: {cost:usMoney}'
1205 Ext.util.Format = {};
1206 var UtilFormat = Ext.util.Format,
1207 stripTagsRE = /<\/?[^>]+>/gi,
1208 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
1211 // A RegExp to remove from a number format string, all characters except digits and '.'
1212 formatCleanRe = /[^\d\.]/g,
1214 // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
1215 // Created on first use. The local decimal separator character must be initialized for this to be created.
1218 Ext.apply(UtilFormat, {
1221 * @property thousandSeparator
1222 * <p>The character that the {@link #number} function uses as a thousand separator.</p>
1223 * <p>This defaults to <code>,</code>, but may be overridden in a locale file.</p>
1225 thousandSeparator: ',',
1229 * @property decimalSeparator
1230 * <p>The character that the {@link #number} function uses as a decimal point.</p>
1231 * <p>This defaults to <code>.</code>, but may be overridden in a locale file.</p>
1233 decimalSeparator: '.',
1237 * @property currencyPrecision
1238 * <p>The number of decimal places that the {@link #currency} function displays.</p>
1239 * <p>This defaults to <code>2</code>, but may be overridden in a locale file.</p>
1241 currencyPrecision: 2,
1245 * @property currencySign
1246 * <p>The currency sign that the {@link #currency} function displays.</p>
1247 * <p>This defaults to <code>$</code>, but may be overridden in a locale file.</p>
1253 * @property currencyAtEnd
1254 * <p>This may be set to <code>true</code> to make the {@link #currency} function
1255 * append the currency sign to the formatted value.</p>
1256 * <p>This defaults to <code>false</code>, but may be overridden in a locale file.</p>
1258 currencyAtEnd: false,
1261 * Checks a reference and converts it to empty string if it is undefined
1262 * @param {Mixed} value Reference to check
1263 * @return {Mixed} Empty string if converted, otherwise the original value
1265 undef : function(value) {
1266 return value !== undefined ? value : "";
1270 * Checks a reference and converts it to the default value if it's empty
1271 * @param {Mixed} value Reference to check
1272 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
1275 defaultValue : function(value, defaultValue) {
1276 return value !== undefined && value !== '' ? value : defaultValue;
1280 * Returns a substring from within an original string
1281 * @param {String} value The original text
1282 * @param {Number} start The start index of the substring
1283 * @param {Number} length The length of the substring
1284 * @return {String} The substring
1286 substr : function(value, start, length) {
1287 return String(value).substr(start, length);
1291 * Converts a string to all lower case letters
1292 * @param {String} value The text to convert
1293 * @return {String} The converted text
1295 lowercase : function(value) {
1296 return String(value).toLowerCase();
1300 * Converts a string to all upper case letters
1301 * @param {String} value The text to convert
1302 * @return {String} The converted text
1304 uppercase : function(value) {
1305 return String(value).toUpperCase();
1309 * Format a number as US currency
1310 * @param {Number/String} value The numeric value to format
1311 * @return {String} The formatted currency string
1313 usMoney : function(v) {
1314 return UtilFormat.currency(v, '$', 2);
1318 * Format a number as a currency
1319 * @param {Number/String} value The numeric value to format
1320 * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
1321 * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
1322 * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
1323 * @return {String} The formatted currency string
1325 currency: function(v, currencySign, decimals, end) {
1326 var negativeSign = '',
1334 decimals = decimals || UtilFormat.currencyPrecision;
1335 format += format + (decimals > 0 ? '.' : '');
1336 for (; i < decimals; i++) {
1339 v = UtilFormat.number(v, format);
1340 if ((end || UtilFormat.currencyAtEnd) === true) {
1341 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
1343 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
1348 * Formats the passed date using the specified format pattern.
1349 * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
1350 * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
1351 * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
1352 * @return {String} The formatted date string.
1354 date: function(v, format) {
1358 if (!Ext.isDate(v)) {
1359 v = new Date(Date.parse(v));
1361 return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
1365 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
1366 * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
1367 * @return {Function} The date formatting function
1369 dateRenderer : function(format) {
1370 return function(v) {
1371 return UtilFormat.date(v, format);
1376 * Strips all HTML tags
1377 * @param {Mixed} value The text from which to strip tags
1378 * @return {String} The stripped text
1380 stripTags : function(v) {
1381 return !v ? v : String(v).replace(stripTagsRE, "");
1385 * Strips all script tags
1386 * @param {Mixed} value The text from which to strip script tags
1387 * @return {String} The stripped text
1389 stripScripts : function(v) {
1390 return !v ? v : String(v).replace(stripScriptsRe, "");
1394 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
1395 * @param {Number/String} size The numeric value to format
1396 * @return {String} The formatted file size
1398 fileSize : function(size) {
1400 return size + " bytes";
1401 } else if (size < 1048576) {
1402 return (Math.round(((size*10) / 1024))/10) + " KB";
1404 return (Math.round(((size*10) / 1048576))/10) + " MB";
1409 * It does simple math for use in a template, for example:<pre><code>
1410 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
1412 * @return {Function} A function that operates on the passed value.
1418 return function(v, a){
1420 fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
1427 * Rounds the passed number to the required decimal precision.
1428 * @param {Number/String} value The numeric value to round.
1429 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
1430 * @return {Number} The rounded value.
1432 round : function(value, precision) {
1433 var result = Number(value);
1434 if (typeof precision == 'number') {
1435 precision = Math.pow(10, precision);
1436 result = Math.round(value * precision) / precision;
1442 * <p>Formats the passed number according to the passed format string.</p>
1443 * <p>The number of digits after the decimal separator character specifies the number of
1444 * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
1445 * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
1446 * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
1447 * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
1448 * <p><b>New to Ext4</b></p>
1449 * <p>Locale-specific characters are always used in the formatted output when inserting
1450 * thousand and decimal separators.</p>
1451 * <p>The format string must specify separator characters according to US/UK conventions ("," as the
1452 * thousand separator, and "." as the decimal separator)</p>
1453 * <p>To allow specification of format strings according to local conventions for separator characters, add
1454 * the string <code>/i</code> to the end of the format string.</p>
1455 * <div style="margin-left:40px">examples (123456.789):
1456 * <div style="margin-left:10px">
1457 * 0 - (123456) show only digits, no precision<br>
1458 * 0.00 - (123456.78) show only digits, 2 precision<br>
1459 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
1460 * 0,000 - (123,456) show comma and digits, no precision<br>
1461 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
1462 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
1463 * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
1464 * For example: 0.000,00/i
1466 * @param {Number} v The number to format.
1467 * @param {String} format The way you would like to format this text.
1468 * @return {String} The formatted number.
1470 number: function(v, formatString) {
1471 if (!formatString) {
1474 v = Ext.Number.from(v, NaN);
1478 var comma = UtilFormat.thousandSeparator,
1479 dec = UtilFormat.decimalSeparator,
1487 // The "/i" suffix allows caller to use a locale-specific formatting string.
1488 // Clean the format string by removing all but numerals and the decimal separator.
1489 // Then split the format string into pre and post decimal segments according to *what* the
1490 // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
1491 if (formatString.substr(formatString.length - 2) == '/i') {
1492 if (!I18NFormatCleanRe) {
1493 I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
1495 formatString = formatString.substr(0, formatString.length - 2);
1497 hasComma = formatString.indexOf(comma) != -1;
1498 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
1500 hasComma = formatString.indexOf(',') != -1;
1501 psplit = formatString.replace(formatCleanRe, '').split('.');
1504 if (1 < psplit.length) {
1505 v = v.toFixed(psplit[1].length);
1506 } else if(2 < psplit.length) {
1509 sourceClass: "Ext.util.Format",
1510 sourceMethod: "number",
1512 formatString: formatString,
1513 msg: "Invalid number format, should have no more than 1 decimal"
1520 var fnum = v.toString();
1522 psplit = fnum.split('.');
1525 var cnum = psplit[0],
1528 m = Math.floor(j / 3),
1529 n = cnum.length % 3 || 3,
1532 for (i = 0; i < j; i += n) {
1537 parr[parr.length] = cnum.substr(i, n);
1540 fnum = parr.join(comma);
1542 fnum += dec + psplit[1];
1546 fnum = psplit[0] + dec + psplit[1];
1552 * Edge case. If we have a very small negative number it will get rounded to 0,
1553 * however the initial check at the top will still report as negative. Replace
1554 * everything but 1-9 and check if the string is empty to determine a 0 value.
1556 neg = fnum.replace(/[^1-9]/g, '') !== '';
1559 return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
1563 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
1564 * @param {String} format Any valid number format string for {@link #number}
1565 * @return {Function} The number formatting function
1567 numberRenderer : function(format) {
1568 return function(v) {
1569 return UtilFormat.number(v, format);
1574 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
1575 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
1576 * if the value is 0 or greater than 1.
1577 * @param {Number} value The value to compare against
1578 * @param {String} singular The singular form of the word
1579 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
1581 plural : function(v, s, p) {
1582 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
1586 * Converts newline characters to the HTML tag <br/>
1587 * @param {String} The string value to format.
1588 * @return {String} The string with embedded <br/> tags in place of newlines.
1590 nl2br : function(v) {
1591 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
1595 * Capitalize the given string. See {@link Ext.String#capitalize}.
1598 capitalize: Ext.String.capitalize,
1601 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
1602 * See {@link Ext.String#ellipsis}.
1605 ellipsis: Ext.String.ellipsis,
1608 * Formats to a string. See {@link Ext.String#format}
1611 format: Ext.String.format,
1614 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
1615 * See {@link Ext.String#htmlDecode}.
1618 htmlDecode: Ext.String.htmlDecode,
1621 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
1622 * See {@link Ext.String#htmlEncode}.
1625 htmlEncode: Ext.String.htmlEncode,
1628 * Adds left padding to a string. See {@link Ext.String#leftPad}
1631 leftPad: Ext.String.leftPad,
1634 * Trims any whitespace from either side of a string. See {@link Ext.String#trim}.
1637 trim : Ext.String.trim,
1640 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
1641 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
1642 * @param {Number|String} v The encoded margins
1643 * @return {Object} An object with margin sizes for top, right, bottom and left
1645 parseBox : function(box) {
1646 if (Ext.isNumber(box)) {
1647 box = box.toString();
1649 var parts = box.split(' '),
1653 parts[1] = parts[2] = parts[3] = parts[0];
1656 parts[2] = parts[0];
1657 parts[3] = parts[1];
1660 parts[3] = parts[1];
1664 top :parseInt(parts[0], 10) || 0,
1665 right :parseInt(parts[1], 10) || 0,
1666 bottom:parseInt(parts[2], 10) || 0,
1667 left :parseInt(parts[3], 10) || 0
1672 * Escapes the passed string for use in a regular expression
1673 * @param {String} str
1676 escapeRegex : function(s) {
1677 return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
1683 * @class Ext.util.TaskRunner
1684 * Provides the ability to execute one or more arbitrary tasks in a multithreaded
1685 * manner. Generally, you can use the singleton {@link Ext.TaskManager} instead, but
1686 * if needed, you can create separate instances of TaskRunner. Any number of
1687 * separate tasks can be started at any time and will run independently of each
1688 * other. Example usage:
1690 // Start a simple clock task that updates a div once per second
1691 var updateClock = function(){
1692 Ext.fly('clock').update(new Date().format('g:i:s A'));
1696 interval: 1000 //1 second
1698 var runner = new Ext.util.TaskRunner();
1701 // equivalent using TaskManager
1702 Ext.TaskManager.start({
1708 * <p>See the {@link #start} method for details about how to configure a task object.</p>
1709 * Also see {@link Ext.util.DelayedTask}.
1712 * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance
1717 Ext.util.TaskRunner = function(interval) {
1718 interval = interval || 10;
1725 stopThread = function() {
1732 startThread = function() {
1735 id = setInterval(runTasks, interval);
1740 removeTask = function(t) {
1741 removeQueue.push(t);
1743 t.onStop.apply(t.scope || t);
1748 runTasks = function() {
1749 var rqLen = removeQueue.length,
1750 now = new Date().getTime(),
1754 for (i = 0; i < rqLen; i++) {
1755 Ext.Array.remove(tasks, removeQueue[i]);
1758 if (tasks.length < 1) {
1768 for (; i < len; ++i) {
1770 itime = now - t.taskRunTime;
1771 if (t.interval <= itime) {
1772 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
1773 t.taskRunTime = now;
1774 if (rt === false || t.taskRunCount === t.repeat) {
1779 if (t.duration && t.duration <= (now - t.taskStartTime)) {
1786 * Starts a new task.
1788 * @param {Object} task <p>A config object that supports the following properties:<ul>
1789 * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
1790 * function will be called at each interval and passed the <code>args</code> argument if specified, and the
1791 * current invocation count if not.</p>
1792 * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
1793 * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
1794 * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
1795 * should be invoked.</div></li>
1796 * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
1797 * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
1798 * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
1799 * <code>run</code> function. Defaults to the task config object.</div></li>
1800 * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
1801 * the task before stopping automatically (defaults to indefinite).</div></li>
1802 * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
1803 * stopping automatically (defaults to indefinite).</div></li>
1805 * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
1806 * that calculations based on the repeat count can be performed.</p>
1807 * @return {Object} The task
1809 this.start = function(task) {
1811 task.taskStartTime = new Date().getTime();
1812 task.taskRunTime = 0;
1813 task.taskRunCount = 0;
1819 * Stops an existing running task.
1821 * @param {Object} task The task to stop
1822 * @return {Object} The task
1824 this.stop = function(task) {
1830 * Stops all tasks that are currently running.
1833 this.stopAll = function() {
1835 for (var i = 0, len = tasks.length; i < len; i++) {
1836 if (tasks[i].onStop) {
1846 * @class Ext.TaskManager
1847 * @extends Ext.util.TaskRunner
1848 * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks. See
1849 * {@link Ext.util.TaskRunner} for supported methods and task config properties.
1851 // Start a simple clock task that updates a div once per second
1854 Ext.fly('clock').update(new Date().format('g:i:s A'));
1856 interval: 1000 //1 second
1858 Ext.TaskManager.start(task);
1860 * <p>See the {@link #start} method for details about how to configure a task object.</p>
1863 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
1867 * Determines information about the current platform the application is running on.
1872 init : function(navigator) {
1873 var platforms = this.platforms,
1874 ln = platforms.length,
1877 navigator = navigator || window.navigator;
1879 for (i = 0; i < ln; i++) {
1880 platform = platforms[i];
1881 this[platform.identity] = platform.regex.test(navigator[platform.property]);
1885 * @property Desktop True if the browser is running on a desktop machine
1888 this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
1890 * @property Tablet True if the browser is running on a tablet (iPad)
1892 this.Tablet = this.iPad;
1894 * @property Phone True if the browser is running on a phone.
1897 this.Phone = !this.Desktop && !this.Tablet;
1899 * @property iOS True if the browser is running on iOS
1902 this.iOS = this.iPhone || this.iPad || this.iPod;
1905 * @property Standalone Detects when application has been saved to homescreen.
1908 this.Standalone = !!window.navigator.standalone;
1912 * @property iPhone True when the browser is running on a iPhone
1916 property: 'platform',
1922 * @property iPod True when the browser is running on a iPod
1926 property: 'platform',
1932 * @property iPad True when the browser is running on a iPad
1936 property: 'userAgent',
1942 * @property Blackberry True when the browser is running on a Blackberry
1946 property: 'userAgent',
1947 regex: /Blackberry/i,
1948 identity: 'Blackberry'
1952 * @property Android True when the browser is running on an Android device
1956 property: 'userAgent',
1962 * @property Mac True when the browser is running on a Mac
1966 property: 'platform',
1972 * @property Windows True when the browser is running on Windows
1976 property: 'platform',
1982 * @property Linux True when the browser is running on Linux
1986 property: 'platform',
1995 * @class Ext.supports
1997 * Determines information about features are supported in the current environment
2004 div = doc.createElement('div'),
2010 '<div style="height:30px;width:50px;">',
2011 '<div style="height:20px;width:20px;"></div>',
2013 '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
2014 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
2016 '<div style="float:left; background-color:transparent;"></div>'
2019 doc.body.appendChild(div);
2021 for (i = 0; i < ln; i++) {
2023 this[test.identity] = test.fn.call(this, doc, div);
2026 doc.body.removeChild(div);
2030 * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
2033 CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
2036 * @property ClassList True if document environment supports the HTML5 classList API.
2039 ClassList: !!document.documentElement.classList,
2042 * @property OrientationChange True if the device supports orientation change
2045 OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
2048 * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
2051 DeviceMotion: ('ondevicemotion' in window),
2054 * @property Touch True if the device supports touch
2057 // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
2058 // and Safari 4.0 (they all have 'ontouchstart' in the window object).
2059 Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
2063 * @property Transitions True if the device supports CSS3 Transitions
2067 identity: 'Transitions',
2068 fn: function(doc, div) {
2076 TE = 'TransitionEnd',
2077 transitionEndName = [
2079 'transitionend', //Moz bucks the prefixing convention
2088 for (; i < ln; i++) {
2089 if (div.getStyle(prefix[i] + "TransitionProperty")) {
2090 Ext.supports.CSS3Prefix = prefix[i];
2091 Ext.supports.CSS3TransitionEnd = transitionEndName[i];
2101 * @property RightMargin True if the device supports right margin.
2102 * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
2106 identity: 'RightMargin',
2107 fn: function(doc, div) {
2108 var view = doc.defaultView;
2109 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
2114 * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
2115 * selection when their display style is changed. Essentially, if a text input
2116 * has focus and its display style is changed, the I-beam disappears.
2118 * This bug is encountered due to the work around in place for the {@link #RightMargin}
2119 * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
2120 * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
2121 * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
2124 identity: 'DisplayChangeInputSelectionBug',
2126 var webKitVersion = Ext.webKitVersion;
2127 // WebKit but older than Safari 5 or Chrome 6:
2128 return 0 < webKitVersion && webKitVersion < 533;
2133 * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
2134 * selection when their display style is changed. Essentially, if a text area has
2135 * focus and its display style is changed, the I-beam disappears.
2137 * This bug is encountered due to the work around in place for the {@link #RightMargin}
2138 * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
2139 * be fixed in Chrome 11.
2142 identity: 'DisplayChangeTextAreaSelectionBug',
2144 var webKitVersion = Ext.webKitVersion;
2149 (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
2150 AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
2152 (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
2153 AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
2158 (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
2159 AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
2162 return 0 < webKitVersion && webKitVersion < 534.24;
2167 * @property TransparentColor True if the device supports transparent color
2171 identity: 'TransparentColor',
2172 fn: function(doc, div, view) {
2173 view = doc.defaultView;
2174 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
2179 * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
2183 identity: 'ComputedStyle',
2184 fn: function(doc, div, view) {
2185 view = doc.defaultView;
2186 return view && view.getComputedStyle;
2191 * @property SVG True if the device supports SVG
2197 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
2202 * @property Canvas True if the device supports Canvas
2208 return !!doc.createElement('canvas').getContext;
2213 * @property VML True if the device supports VML
2219 var d = doc.createElement("div");
2220 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
2221 return (d.childNodes.length == 2);
2226 * @property Float True if the device supports CSS float
2231 fn: function(doc, div) {
2232 return !!div.lastChild.style.cssFloat;
2237 * @property AudioTag True if the device supports the HTML5 audio tag
2241 identity: 'AudioTag',
2243 return !!doc.createElement('audio').canPlayType;
2248 * @property History True if the device supports HTML5 history
2252 identity: 'History',
2254 return !!(window.history && history.pushState);
2259 * @property CSS3DTransform True if the device supports CSS3DTransform
2263 identity: 'CSS3DTransform',
2265 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
2270 * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
2274 identity: 'CSS3LinearGradient',
2275 fn: function(doc, div) {
2276 var property = 'background-image:',
2277 webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
2278 w3c = 'linear-gradient(left top, black, white)',
2279 moz = '-moz-' + w3c,
2280 options = [property + webkit, property + w3c, property + moz];
2282 div.style.cssText = options.join(';');
2284 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
2289 * @property CSS3BorderRadius True if the device supports CSS3 border radius
2293 identity: 'CSS3BorderRadius',
2294 fn: function(doc, div) {
2295 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
2298 for (i = 0; i < domPrefixes.length; i++) {
2299 if (document.body.style[domPrefixes[i]] !== undefined) {
2308 * @property GeoLocation True if the device supports GeoLocation
2312 identity: 'GeoLocation',
2314 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
2318 * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
2322 identity: 'MouseEnterLeave',
2323 fn: function(doc, div){
2324 return ('onmouseenter' in div && 'onmouseleave' in div);
2328 * @property MouseWheel True if the browser supports the mousewheel event
2332 identity: 'MouseWheel',
2333 fn: function(doc, div) {
2334 return ('onmousewheel' in div);
2338 * @property Opacity True if the browser supports normal css opacity
2342 identity: 'Opacity',
2343 fn: function(doc, div){
2344 // Not a strict equal comparison in case opacity can be converted to a number.
2345 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
2348 div.firstChild.style.cssText = 'opacity:0.73';
2349 return div.firstChild.style.opacity == '0.73';
2353 * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
2357 identity: 'Placeholder',
2359 return 'placeholder' in doc.createElement('input');
2364 * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
2365 * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
2369 identity: 'Direct2DBug',
2371 return Ext.isString(document.body.style.msTransformOrigin);
2375 * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
2379 identity: 'BoundingClientRect',
2380 fn: function(doc, div) {
2381 return Ext.isFunction(div.getBoundingClientRect);
2385 identity: 'IncludePaddingInWidthCalculation',
2386 fn: function(doc, div){
2387 var el = Ext.get(div.childNodes[1].firstChild);
2388 return el.getWidth() == 210;
2392 identity: 'IncludePaddingInHeightCalculation',
2393 fn: function(doc, div){
2394 var el = Ext.get(div.childNodes[1].firstChild);
2395 return el.getHeight() == 210;
2400 * @property ArraySort True if the Array sort native method isn't bugged.
2404 identity: 'ArraySort',
2406 var a = [1,2,3,4,5].sort(function(){ return 0; });
2407 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
2411 * @property Range True if browser support document.createRange native method.
2417 return !!document.createRange;
2421 * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
2425 identity: 'CreateContextualFragment',
2427 var range = Ext.supports.Range ? document.createRange() : false;
2429 return range && !!range.createContextualFragment;
2434 * @property WindowOnError True if browser supports window.onerror.
2438 identity: 'WindowOnError',
2440 // sadly, we cannot feature detect this...
2441 return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+