2 Ext JS - JavaScript Library
3 Copyright (c) 2006-2011, Sencha Inc.
9 * Modified version of Douglas Crockford"s json.js that doesn"t
10 * mess with the Object prototype
11 * http://www.json.org/js.html
14 Ext.JSON = new(function() {
15 var useHasOwn = !! {}.hasOwnProperty,
16 isNative = function() {
20 if (useNative === null) {
21 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
28 return n < 10 ? "0" + n : n;
30 doDecode = function(json) {
31 return eval("(" + json + ')');
33 doEncode = function(o) {
34 if (!Ext.isDefined(o) || o === null) {
36 } else if (Ext.isArray(o)) {
37 return encodeArray(o);
38 } else if (Ext.isDate(o)) {
39 return Ext.JSON.encodeDate(o);
40 } else if (Ext.isString(o)) {
41 return encodeString(o);
42 } else if (typeof o == "number") {
43 //don't use isNumber here, since finite checks happen inside isNumber
44 return isFinite(o) ? String(o) : "null";
45 } else if (Ext.isBoolean(o)) {
47 } else if (Ext.isObject(o)) {
48 return encodeObject(o);
49 } else if (typeof o === "function") {
62 '\x0b': '\\u000b' //ie doesn't handle \v
64 charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
65 encodeString = function(s) {
66 return '"' + s.replace(charToReplace, function(a) {
68 return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
71 encodeArray = function(o) {
73 // Note empty string in case there are no serializable members.
76 for (i = 0; i < len; i += 1) {
77 a.push(doEncode(o[i]), ',');
79 // Overwrite trailing comma (or empty string)
80 a[a.length - 1] = ']';
83 encodeObject = function(o) {
85 // Note empty string in case there are no serializable members.
88 if (!useHasOwn || o.hasOwnProperty(i)) {
89 a.push(doEncode(i), ":", doEncode(o[i]), ',');
92 // Overwrite trailing comma (or empty string)
93 a[a.length - 1] = '}';
98 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
99 * <b>The returned value includes enclosing double quotation marks.</b></p>
100 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
101 * <p>To override this:</p><pre><code>
102 Ext.JSON.encodeDate = function(d) {
103 return d.format('"Y-m-d"');
106 * @param {Date} d The Date to encode
107 * @return {String} The string literal to use in a JSON string.
109 this.encodeDate = function(o) {
110 return '"' + o.getFullYear() + "-"
111 + pad(o.getMonth() + 1) + "-"
112 + pad(o.getDate()) + "T"
113 + pad(o.getHours()) + ":"
114 + pad(o.getMinutes()) + ":"
115 + pad(o.getSeconds()) + '"';
119 * Encodes an Object, Array or other value
120 * @param {Mixed} o The variable to encode
121 * @return {String} The JSON string
123 this.encode = function() {
127 // setup encoding function on first access
128 ec = isNative() ? JSON.stringify : doEncode;
136 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
137 * @param {String} json The JSON string
138 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
139 * @return {Object} The resulting object
141 this.decode = function() {
143 return function(json, safe) {
145 // setup decoding function on first access
146 dc = isNative() ? JSON.parse : doDecode;
155 sourceClass: "Ext.JSON",
156 sourceMethod: "decode",
157 msg: "You're trying to decode and invalid JSON String: " + json
165 * Shorthand for {@link Ext.JSON#encode}
166 * @param {Mixed} o The variable to encode
167 * @return {String} The JSON string
171 Ext.encode = Ext.JSON.encode;
173 * Shorthand for {@link Ext.JSON#decode}
174 * @param {String} json The JSON string
175 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
176 * @return {Object} The resulting object
180 Ext.decode = Ext.JSON.decode;
186 The Ext namespace (global object) encapsulates all classes, singletons, and utility methods provided by Sencha's libraries.</p>
187 Most user interface Components are at a lower level of nesting in the namespace, but many common utility functions are provided
188 as direct properties of the Ext namespace.
190 Also many frequently used methods from other classes are provided as shortcuts within the Ext namespace.
191 For example {@link Ext#getCmp Ext.getCmp} aliases {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
193 Many applications are initiated with {@link Ext#onReady Ext.onReady} which is called once the DOM is ready.
194 This ensures all scripts have been loaded, preventing dependency issues. For example
196 Ext.onReady(function(){
198 renderTo: document.body,
203 For more information about how to use the Ext classes, see
205 * <a href="http://www.sencha.com/learn/">The Learning Center</a>
206 * <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
207 * <a href="http://www.sencha.com/forum/">The forums</a>
213 userAgent: navigator.userAgent.toLowerCase(),
216 BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
217 isStrict: document.compatMode == "CSS1Compat",
218 windowId: 'ext-window',
219 documentId: 'ext-document',
222 * True when the document is fully initialized and ready for action
228 * True to automatically uncache orphaned Ext.core.Elements periodically (defaults to true)
231 enableGarbageCollector: true,
234 * True to automatically purge event listeners during garbageCollection (defaults to true).
237 enableListenerCollection: true,
240 * Generates unique ids. If the element already has an id, it is unchanged
241 * @param {Mixed} el (optional) The element to generate an id for
242 * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
243 * @return {String} The generated Id.
245 id: function(el, prefix) {
246 el = Ext.getDom(el, true) || {};
247 if (el === document) {
248 el.id = this.documentId;
250 else if (el === window) {
251 el.id = this.windowId;
254 el.id = (prefix || "ext-gen") + (++Ext.idSeed);
260 * Returns the current document body as an {@link Ext.core.Element}.
261 * @return Ext.core.Element The document body
263 getBody: function() {
264 return Ext.get(document.body || false);
268 * Returns the current document head as an {@link Ext.core.Element}.
269 * @return Ext.core.Element The document head
271 getHead: function() {
275 if (head == undefined) {
276 head = Ext.get(document.getElementsByTagName("head")[0]);
284 * Returns the current HTML document object as an {@link Ext.core.Element}.
285 * @return Ext.core.Element The document
288 return Ext.get(document);
292 * This is shorthand reference to {@link Ext.ComponentManager#get}.
293 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
294 * @param {String} id The component {@link Ext.Component#id id}
295 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
298 getCmp: function(id) {
299 return Ext.ComponentManager.get(id);
303 * Returns the current orientation of the mobile device
304 * @return {String} Either 'portrait' or 'landscape'
306 getOrientation: function() {
307 return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
311 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
312 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
313 * intended for arguments of type {@link Ext.core.Element} and {@link Ext.Component}, but any subclass of
314 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
315 * passed into this function in a single call as separate arguments.
316 * @param {Mixed} arg1 An {@link Ext.core.Element}, {@link Ext.Component}, or an Array of either of these to destroy
317 * @param {Mixed} arg2 (optional)
318 * @param {Mixed} etc... (optional)
320 destroy: function() {
321 var ln = arguments.length,
324 for (i = 0; i < ln; i++) {
327 if (Ext.isArray(arg)) {
328 this.destroy.apply(this, arg);
330 else if (Ext.isFunction(arg.destroy)) {
341 * Execute a callback function in a particular scope. If no function is passed the call is ignored.
342 * @param {Function} callback The callback to execute
343 * @param {Object} scope (optional) The scope to execute in
344 * @param {Array} args (optional) The arguments to pass to the function
345 * @param {Number} delay (optional) Pass a number to delay the call by a number of milliseconds.
347 callback: function(callback, scope, args, delay){
348 if(Ext.isFunction(callback)){
350 scope = scope || window;
352 Ext.defer(callback, delay, scope, args);
354 callback.apply(scope, args);
360 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
361 * @param {String} value The string to encode
362 * @return {String} The encoded text
364 htmlEncode : function(value) {
365 return Ext.String.htmlEncode(value);
369 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
370 * @param {String} value The string to decode
371 * @return {String} The decoded text
373 htmlDecode : function(value) {
374 return Ext.String.htmlDecode(value);
378 * Appends content to the query string of a URL, handling logic for whether to place
379 * a question mark or ampersand.
380 * @param {String} url The URL to append to.
381 * @param {String} s The content to append to the URL.
382 * @return (String) The resulting URL
384 urlAppend : function(url, s) {
385 if (!Ext.isEmpty(s)) {
386 return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
393 Ext.ns = Ext.namespace;
396 window.undefined = window.undefined;
399 * Ext core utilities and functions.
403 var check = function(regex){
404 return regex.test(Ext.userAgent);
406 docMode = document.documentMode,
407 isOpera = check(/opera/),
408 isOpera10_5 = isOpera && check(/version\/10\.5/),
409 isChrome = check(/\bchrome\b/),
410 isWebKit = check(/webkit/),
411 isSafari = !isChrome && check(/safari/),
412 isSafari2 = isSafari && check(/applewebkit\/4/), // unique to Safari 2
413 isSafari3 = isSafari && check(/version\/3/),
414 isSafari4 = isSafari && check(/version\/4/),
415 isIE = !isOpera && check(/msie/),
416 isIE7 = isIE && (check(/msie 7/) || docMode == 7),
417 isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
418 isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
419 isIE6 = isIE && check(/msie 6/),
420 isGecko = !isWebKit && check(/gecko/),
421 isGecko3 = isGecko && check(/rv:1\.9/),
422 isGecko4 = isGecko && check(/rv:2\.0/),
423 isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
424 isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
425 isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
426 isWindows = check(/windows|win32/),
427 isMac = check(/macintosh|mac os x/),
428 isLinux = check(/linux/),
431 // remove css image flicker
433 document.execCommand("BackgroundImageCache", false, true);
436 Ext.setVersion('extjs', '4.0.0');
439 * URL to a blank file used by Ext when in secure mode for iframe src and onReady src to prevent
440 * the IE insecure content warning (<tt>'about:blank'</tt>, except for IE in secure mode, which is <tt>'javascript:""'</tt>).
443 SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',
446 * True if the {@link Ext.fx.Anim} Class is available
452 * True to scope the reset CSS to be just applied to Ext components. Note that this wraps root containers
453 * with an additional element. Also remember that when you turn on this option, you have to use ext-all-scoped {
454 * unless you use the bootstrap.js to load your javascript, in which case it will be handled for you.
457 scopeResetCSS : Ext.buildSettings.scopeResetCSS,
460 * EXPERIMENTAL - True to cascade listener removal to child elements when an element is removed.
461 * Currently not optimized for performance.
464 enableNestedListenerRemoval : false,
467 * Indicates whether to use native browser parsing for JSON methods.
468 * This option is ignored if the browser does not support native JSON methods.
469 * <b>Note: Native JSON methods will not work with objects that have functions.
470 * Also, property names must be quoted, otherwise the data will not parse.</b> (Defaults to false)
473 USE_NATIVE_JSON : false,
476 * Return the dom node for the passed String (id), dom node, or Ext.core.Element.
477 * Optional 'strict' flag is needed for IE since it can return 'name' and
478 * 'id' elements by using getElementById.
479 * Here are some examples:
481 // gets dom node based on id
482 var elDom = Ext.getDom('elId');
483 // gets dom node based on the dom node
484 var elDom1 = Ext.getDom(elDom);
486 // If we don't know if we are working with an
487 // Ext.core.Element or a dom node use Ext.getDom
489 var dom = Ext.getDom(el);
490 // do something with the dom node
493 * <b>Note</b>: the dom node to be found actually needs to exist (be rendered, etc)
494 * when this method is called to be successful.
496 * @return HTMLElement
498 getDom : function(el, strict) {
499 if (!el || !document) {
505 if (typeof el == 'string') {
506 var e = document.getElementById(el);
507 // IE returns elements with the 'name' and 'id' attribute.
508 // we do a strict check to return the element with only the id attribute
509 if (e && isIE && strict) {
510 if (el == e.getAttribute('id')) {
524 * Removes a DOM node from the document.
525 * <p>Removes this element from the document, removes all DOM event listeners, and deletes the cache reference.
526 * All DOM event listeners are removed from this element. If {@link Ext#enableNestedListenerRemoval Ext.enableNestedListenerRemoval} is
527 * <code>true</code>, then DOM event listeners are also removed from all child nodes. The body node
528 * will be ignored if passed in.</p>
529 * @param {HTMLElement} node The node to remove
531 removeNode : isIE6 || isIE7 ? function() {
534 if(n && n.tagName != 'BODY'){
535 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
536 d = d || document.createElement('div');
539 delete Ext.cache[n.id];
543 if (n && n.parentNode && n.tagName != 'BODY') {
544 (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
545 n.parentNode.removeChild(n);
546 delete Ext.cache[n.id];
551 * True if the detected browser is Opera.
557 * True if the detected browser is Opera 10.5x.
560 isOpera10_5 : isOpera10_5,
563 * True if the detected browser uses WebKit.
569 * True if the detected browser is Chrome.
575 * True if the detected browser is Safari.
581 * True if the detected browser is Safari 3.x.
584 isSafari3 : isSafari3,
587 * True if the detected browser is Safari 4.x.
590 isSafari4 : isSafari4,
593 * True if the detected browser is Safari 2.x.
596 isSafari2 : isSafari2,
599 * True if the detected browser is Internet Explorer.
605 * True if the detected browser is Internet Explorer 6.x.
611 * True if the detected browser is Internet Explorer 7.x.
617 * True if the detected browser is Internet Explorer 8.x.
623 * True if the detected browser is Internet Explorer 9.x.
629 * True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
635 * True if the detected browser uses a Gecko 1.9+ layout engine (e.g. Firefox 3.x).
641 * True if the detected browser uses a Gecko 2.0+ layout engine (e.g. Firefox 4.x).
647 * True if the detected browser uses FireFox 3.0
653 * True if the detected browser uses FireFox 3.5
659 * True if the detected browser uses FireFox 3.6
665 * True if the detected platform is Linux.
671 * True if the detected platform is Windows.
674 isWindows : isWindows,
677 * True if the detected platform is Mac OS.
683 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
684 * In older versions of IE, this defaults to "http://sencha.com/s.gif" and you should change this to a URL on your server.
685 * For other browsers it uses an inline data URL.
688 BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
691 * <p>Utility method for returning a default value if the passed value is empty.</p>
692 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
695 * <li>an empty array</li>
696 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
698 * @param {Mixed} value The value to test
699 * @param {Mixed} defaultValue The value to return if the original value is empty
700 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
701 * @return {Mixed} value, if non-empty, else defaultValue
702 * @deprecated 4.0.0 Use {Ext#valueFrom} instead
704 value : function(v, defaultValue, allowBlank){
705 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
709 * Escapes the passed string for use in a regular expression
710 * @param {String} str
712 * @deprecated 4.0.0 Use {@link Ext.String#escapeRegex} instead
714 escapeRe : function(s) {
715 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
719 * Applies event listeners to elements by selectors when the document is ready.
720 * The event name is specified with an <tt>@</tt> suffix.
723 // add a listener for click on all anchors in element with id foo
724 '#foo a@click' : function(e, t){
728 // add the same listener to multiple selectors (separated by comma BEFORE the @)
729 '#foo a, #bar span.some-class@mouseover' : function(){
734 * @param {Object} obj The list of behaviors to apply
736 addBehaviors : function(o){
738 Ext.onReady(function(){
742 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
747 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
750 cache[s] = Ext.select(s);
752 cache[s].on(parts[1], o[b]);
760 * Utility method for getting the width of the browser scrollbar. This can differ depending on
761 * operating system settings, such as the theme or font size.
762 * @param {Boolean} force (optional) true to force a recalculation of the value.
763 * @return {Number} The width of the scrollbar.
765 getScrollBarWidth: function(force){
770 if(force === true || scrollWidth === null){
772 // When IE9 positions an element offscreen via offsets, the offsetWidth is
773 // inaccurately reported. For IE9 only, we render on screen before removing.
774 var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets';
775 // Append our div, do our calculation and then remove it
776 var div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
777 child = div.child('div', true);
778 var w1 = child.offsetWidth;
779 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
780 var w2 = child.offsetWidth;
782 // Need to add 2 to ensure we leave enough space
783 scrollWidth = w1 - w2 + 2;
789 * Copies a set of named properties fom the source object to the destination object.
790 * <p>example:<pre><code>
791 ImageComponent = Ext.extend(Ext.Component, {
792 initComponent: function() {
793 this.autoEl = { tag: 'img' };
794 MyComponent.superclass.initComponent.apply(this, arguments);
795 this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
799 * Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
800 * @param {Object} dest The destination object.
801 * @param {Object} source The source object.
802 * @param {Array/String} names Either an Array of property names, or a comma-delimited list
803 * of property names to copy.
804 * @param {Boolean} usePrototypeKeys (Optional) Defaults to false. Pass true to copy keys off of the prototype as well as the instance.
805 * @return {Object} The modified object.
807 copyTo : function(dest, source, names, usePrototypeKeys){
808 if(typeof names == 'string'){
809 names = names.split(/[,;\s]/);
811 Ext.each(names, function(name){
812 if(usePrototypeKeys || source.hasOwnProperty(name)){
813 dest[name] = source[name];
820 * Attempts to destroy and then remove a set of named properties of the passed object.
821 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
822 * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
823 * @param {Mixed} etc... More property names to destroy and remove.
825 destroyMembers : function(o, arg1, arg2, etc){
826 for (var i = 1, a = arguments, len = a.length; i < len; i++) {
827 Ext.destroy(o[a[i]]);
833 * Partitions the set into two sets: a true set and a false set.
838 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
844 return val.className == "class1"
847 // true are those paragraph elements with a className of "class1",
848 // false set are those that do not have that className.
850 * @param {Array|NodeList} arr The array to partition
851 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
852 * itself must be able to be evaluated for its truthfulness.
853 * @return {Array} [true<Array>,false<Array>]
854 * @deprecated 4.0.0 Will be removed in the next major version
856 partition : function(arr, truth){
858 Ext.each(arr, function(v, i, a) {
859 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
865 * Invokes a method on each item in an Array.
868 Ext.invoke(Ext.query("p"), "getAttribute", "id");
869 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
871 * @param {Array|NodeList} arr The Array of items to invoke the method on.
872 * @param {String} methodName The method name to invoke.
873 * @param {...*} args Arguments to send into the method invocation.
874 * @return {Array} The results of invoking the method on each item in the array.
875 * @deprecated 4.0.0 Will be removed in the next major version
877 invoke : function(arr, methodName){
879 args = Array.prototype.slice.call(arguments, 2);
880 Ext.each(arr, function(v,i) {
881 if (v && typeof v[methodName] == 'function') {
882 ret.push(v[methodName].apply(v, args));
891 * <p>Zips N sets together.</p>
894 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
901 return "$" + a + "" + b + "." + c
903 ); // ["$+12.43", "$-10.15", "$+22.96"]
905 * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
906 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
907 * @return {Array} The zipped set.
908 * @deprecated 4.0.0 Will be removed in the next major version
911 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
914 len = Ext.max(Ext.pluck(arrs, "length")),
917 for (var i = 0; i < len; i++) {
920 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
922 for (var j = 0, aLen = arrs.length; j < aLen; j++){
923 ret[i].push( arrs[j][i] );
931 * Turns an array into a sentence, joined by a specified connector - e.g.:
932 * Ext.toSentence(['Adama', 'Tigh', 'Roslin']); //'Adama, Tigh and Roslin'
933 * Ext.toSentence(['Adama', 'Tigh', 'Roslin'], 'or'); //'Adama, Tigh or Roslin'
934 * @param {Array} items The array to create a sentence from
935 * @param {String} connector The string to use to connect the last two words. Usually 'and' or 'or' - defaults to 'and'.
936 * @return {String} The sentence string
937 * @deprecated 4.0.0 Will be removed in the next major version
939 toSentence: function(items, connector) {
940 var length = items.length;
945 var head = items.slice(0, length - 1),
946 tail = items[length - 1];
948 return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
953 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
954 * you may want to set this to true.
964 * @param {Object} config
966 Ext.application = function(config) {
967 Ext.require('Ext.app.Application');
969 Ext.onReady(function() {
970 Ext.create('Ext.app.Application', config);
975 * @class Ext.util.Format
977 This class is a centralized place for formatting functions inside the library. It includes
978 functions to format various different types of data, such as text, dates and numeric values.
981 This class contains several options for localization. These can be set once the library has loaded,
982 all calls to the functions from that point will use the locale settings that were specified.
989 This class also uses the default date format defined here: {@link Ext.date#defaultFormat}.
991 __Using with renderers__
992 There are two helper functions that return a new function that can be used in conjunction with
997 renderer: Ext.util.Format.dateRenderer('Y-m-d')
1000 renderer: Ext.util.Format.numberRenderer('0.000')
1003 Functions that only take a single argument can also be passed directly:
1006 renderer: Ext.util.Format.usMoney
1008 dataIndex: 'productCode',
1009 renderer: Ext.util.Format.uppercase
1012 __Using with XTemplates__
1013 XTemplates can also directly use Ext.util.Format functions:
1016 'Date: {startDate:date("Y-m-d")}',
1017 'Cost: {cost:usMoney}'
1026 Ext.util.Format = {};
1027 var UtilFormat = Ext.util.Format,
1028 stripTagsRE = /<\/?[^>]+>/gi,
1029 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
1032 // A RegExp to remove from a number format string, all characters except digits and '.'
1033 formatCleanRe = /[^\d\.]/g,
1035 // A RegExp to remove from a number format string, all characters except digits and the local decimal separator.
1036 // Created on first use. The local decimal separator character must be initialized for this to be created.
1039 Ext.apply(UtilFormat, {
1042 * @property thousandSeparator
1043 * <p>The character that the {@link #number} function uses as a thousand separator.</p>
1044 * <p>This defaults to <code>,</code>, but may be overridden in a locale file.</p>
1046 thousandSeparator: ',',
1050 * @property decimalSeparator
1051 * <p>The character that the {@link #number} function uses as a decimal point.</p>
1052 * <p>This defaults to <code>.</code>, but may be overridden in a locale file.</p>
1054 decimalSeparator: '.',
1058 * @property currencyPrecision
1059 * <p>The number of decimal places that the {@link #currency} function displays.</p>
1060 * <p>This defaults to <code>2</code>, but may be overridden in a locale file.</p>
1062 currencyPrecision: 2,
1066 * @property currencySign
1067 * <p>The currency sign that the {@link #currency} function displays.</p>
1068 * <p>This defaults to <code>$</code>, but may be overridden in a locale file.</p>
1074 * @property currencyAtEnd
1075 * <p>This may be set to <code>true</code> to make the {@link #currency} function
1076 * append the currency sign to the formatted value.</p>
1077 * <p>This defaults to <code>false</code>, but may be overridden in a locale file.</p>
1079 currencyAtEnd: false,
1082 * Checks a reference and converts it to empty string if it is undefined
1083 * @param {Mixed} value Reference to check
1084 * @return {Mixed} Empty string if converted, otherwise the original value
1086 undef : function(value) {
1087 return value !== undefined ? value : "";
1091 * Checks a reference and converts it to the default value if it's empty
1092 * @param {Mixed} value Reference to check
1093 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
1096 defaultValue : function(value, defaultValue) {
1097 return value !== undefined && value !== '' ? value : defaultValue;
1101 * Returns a substring from within an original string
1102 * @param {String} value The original text
1103 * @param {Number} start The start index of the substring
1104 * @param {Number} length The length of the substring
1105 * @return {String} The substring
1107 substr : function(value, start, length) {
1108 return String(value).substr(start, length);
1112 * Converts a string to all lower case letters
1113 * @param {String} value The text to convert
1114 * @return {String} The converted text
1116 lowercase : function(value) {
1117 return String(value).toLowerCase();
1121 * Converts a string to all upper case letters
1122 * @param {String} value The text to convert
1123 * @return {String} The converted text
1125 uppercase : function(value) {
1126 return String(value).toUpperCase();
1130 * Format a number as US currency
1131 * @param {Number/String} value The numeric value to format
1132 * @return {String} The formatted currency string
1134 usMoney : function(v) {
1135 return UtilFormat.currency(v, '$', 2);
1139 * Format a number as a currency
1140 * @param {Number/String} value The numeric value to format
1141 * @param {String} sign The currency sign to use (defaults to {@link #currencySign})
1142 * @param {Number} decimals The number of decimals to use for the currency (defaults to {@link #currencyPrecision})
1143 * @param {Boolean} end True if the currency sign should be at the end of the string (defaults to {@link #currencyAtEnd})
1144 * @return {String} The formatted currency string
1146 currency: function(v, currencySign, decimals, end) {
1147 var negativeSign = '',
1155 decimals = decimals || UtilFormat.currencyPrecision;
1156 format += format + (decimals > 0 ? '.' : '');
1157 for (; i < decimals; i++) {
1160 v = UtilFormat.number(v, format);
1161 if ((end || UtilFormat.currencyAtEnd) === true) {
1162 return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
1164 return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
1169 * Formats the passed date using the specified format pattern.
1170 * @param {String/Date} value The value to format. If a string is passed, it is converted to a Date by the Javascript
1171 * Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method.
1172 * @param {String} format (Optional) Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
1173 * @return {String} The formatted date string.
1175 date: function(v, format) {
1179 if (!Ext.isDate(v)) {
1180 v = new Date(Date.parse(v));
1182 return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
1186 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
1187 * @param {String} format Any valid date format string. Defaults to {@link Ext.Date#defaultFormat}.
1188 * @return {Function} The date formatting function
1190 dateRenderer : function(format) {
1191 return function(v) {
1192 return UtilFormat.date(v, format);
1197 * Strips all HTML tags
1198 * @param {Mixed} value The text from which to strip tags
1199 * @return {String} The stripped text
1201 stripTags : function(v) {
1202 return !v ? v : String(v).replace(stripTagsRE, "");
1206 * Strips all script tags
1207 * @param {Mixed} value The text from which to strip script tags
1208 * @return {String} The stripped text
1210 stripScripts : function(v) {
1211 return !v ? v : String(v).replace(stripScriptsRe, "");
1215 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
1216 * @param {Number/String} size The numeric value to format
1217 * @return {String} The formatted file size
1219 fileSize : function(size) {
1221 return size + " bytes";
1222 } else if (size < 1048576) {
1223 return (Math.round(((size*10) / 1024))/10) + " KB";
1225 return (Math.round(((size*10) / 1048576))/10) + " MB";
1230 * It does simple math for use in a template, for example:<pre><code>
1231 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
1233 * @return {Function} A function that operates on the passed value.
1238 return function(v, a){
1240 fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
1247 * Rounds the passed number to the required decimal precision.
1248 * @param {Number/String} value The numeric value to round.
1249 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
1250 * @return {Number} The rounded value.
1252 round : function(value, precision) {
1253 var result = Number(value);
1254 if (typeof precision == 'number') {
1255 precision = Math.pow(10, precision);
1256 result = Math.round(value * precision) / precision;
1262 * <p>Formats the passed number according to the passed format string.</p>
1263 * <p>The number of digits after the decimal separator character specifies the number of
1264 * decimal places in the resulting string. The <u>local-specific</u> decimal character is used in the result.</p>
1265 * <p>The <i>presence</i> of a thousand separator character in the format string specifies that
1266 * the <u>locale-specific</u> thousand separator (if any) is inserted separating thousand groups.</p>
1267 * <p>By default, "," is expected as the thousand separator, and "." is expected as the decimal separator.</p>
1268 * <p><b>New to Ext4</b></p>
1269 * <p>Locale-specific characters are always used in the formatted output when inserting
1270 * thousand and decimal separators.</p>
1271 * <p>The format string must specify separator characters according to US/UK conventions ("," as the
1272 * thousand separator, and "." as the decimal separator)</p>
1273 * <p>To allow specification of format strings according to local conventions for separator characters, add
1274 * the string <code>/i</code> to the end of the format string.</p>
1275 * <div style="margin-left:40px">examples (123456.789):
1276 * <div style="margin-left:10px">
1277 * 0 - (123456) show only digits, no precision<br>
1278 * 0.00 - (123456.78) show only digits, 2 precision<br>
1279 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
1280 * 0,000 - (123,456) show comma and digits, no precision<br>
1281 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
1282 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
1283 * To allow specification of the formatting string using UK/US grouping characters (,) and decimal (.) for international numbers, add /i to the end.
1284 * For example: 0.000,00/i
1286 * @param {Number} v The number to format.
1287 * @param {String} format The way you would like to format this text.
1288 * @return {String} The formatted number.
1291 function(v, formatString) {
1292 if (!formatString) {
1295 v = Ext.Number.from(v, NaN);
1299 var comma = UtilFormat.thousandSeparator,
1300 dec = UtilFormat.decimalSeparator,
1308 // The "/i" suffix allows caller to use a locale-specific formatting string.
1309 // Clean the format string by removing all but numerals and the decimal separator.
1310 // Then split the format string into pre and post decimal segments according to *what* the
1311 // decimal separator is. If they are specifying "/i", they are using the local convention in the format string.
1312 if (formatString.substr(formatString.length - 2) == '/i') {
1313 if (!I18NFormatCleanRe) {
1314 I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
1316 formatString = formatString.substr(0, formatString.length - 2);
1318 hasComma = formatString.indexOf(comma) != -1;
1319 psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
1321 hasComma = formatString.indexOf(',') != -1;
1322 psplit = formatString.replace(formatCleanRe, '').split('.');
1325 if (1 < psplit.length) {
1326 v = v.toFixed(psplit[1].length);
1327 } else if(2 < psplit.length) {
1330 sourceClass: "Ext.util.Format",
1331 sourceMethod: "number",
1333 formatString: formatString,
1334 msg: "Invalid number format, should have no more than 1 decimal"
1341 var fnum = v.toString();
1343 psplit = fnum.split('.');
1346 var cnum = psplit[0],
1349 m = Math.floor(j / 3),
1350 n = cnum.length % 3 || 3,
1353 for (i = 0; i < j; i += n) {
1358 parr[parr.length] = cnum.substr(i, n);
1361 fnum = parr.join(comma);
1363 fnum += dec + psplit[1];
1367 fnum = psplit[0] + dec + psplit[1];
1371 return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
1375 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
1376 * @param {String} format Any valid number format string for {@link #number}
1377 * @return {Function} The number formatting function
1379 numberRenderer : function(format) {
1380 return function(v) {
1381 return UtilFormat.number(v, format);
1386 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
1387 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
1388 * if the value is 0 or greater than 1.
1389 * @param {Number} value The value to compare against
1390 * @param {String} singular The singular form of the word
1391 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
1393 plural : function(v, s, p) {
1394 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
1398 * Converts newline characters to the HTML tag <br/>
1399 * @param {String} The string value to format.
1400 * @return {String} The string with embedded <br/> tags in place of newlines.
1402 nl2br : function(v) {
1403 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
1407 * Capitalize the given string. See {@link Ext.String#capitalize}.
1409 capitalize: Ext.String.capitalize,
1412 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
1413 * See {@link Ext.String#ellipsis}.
1415 ellipsis: Ext.String.ellipsis,
1418 * Formats to a string. See {@link Ext.String#format}
1420 format: Ext.String.format,
1423 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
1424 * See {@link Ext.string#htmlDecode}.
1426 htmlDecode: Ext.String.htmlDecode,
1429 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
1430 * See {@link Ext.String#htmlEncode}.
1432 htmlEncode: Ext.String.htmlEncode,
1435 * Adds left padding to a string. See {@link Ext.String#leftPad}
1437 leftPad: Ext.String.leftPad,
1440 * Trims any whitespace from either side of a string. See {@link Ext.String#trim}.
1442 trim : Ext.String.trim,
1445 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
1446 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
1447 * @param {Number|String} v The encoded margins
1448 * @return {Object} An object with margin sizes for top, right, bottom and left
1450 parseBox : function(box) {
1451 if (Ext.isNumber(box)) {
1452 box = box.toString();
1454 var parts = box.split(' '),
1458 parts[1] = parts[2] = parts[3] = parts[0];
1461 parts[2] = parts[0];
1462 parts[3] = parts[1];
1465 parts[3] = parts[1];
1469 top :parseInt(parts[0], 10) || 0,
1470 right :parseInt(parts[1], 10) || 0,
1471 bottom:parseInt(parts[2], 10) || 0,
1472 left :parseInt(parts[3], 10) || 0
1477 * Escapes the passed string for use in a regular expression
1478 * @param {String} str
1481 escapeRegex : function(s) {
1482 return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
1488 * @class Ext.util.TaskRunner
1489 * Provides the ability to execute one or more arbitrary tasks in a multithreaded
1490 * manner. Generally, you can use the singleton {@link Ext.TaskManager} instead, but
1491 * if needed, you can create separate instances of TaskRunner. Any number of
1492 * separate tasks can be started at any time and will run independently of each
1493 * other. Example usage:
1495 // Start a simple clock task that updates a div once per second
1496 var updateClock = function(){
1497 Ext.fly('clock').update(new Date().format('g:i:s A'));
1501 interval: 1000 //1 second
1503 var runner = new Ext.util.TaskRunner();
1506 // equivalent using TaskManager
1507 Ext.TaskManager.start({
1513 * <p>See the {@link #start} method for details about how to configure a task object.</p>
1514 * Also see {@link Ext.util.DelayedTask}.
1517 * @param {Number} interval (optional) The minimum precision in milliseconds supported by this TaskRunner instance
1522 Ext.util.TaskRunner = function(interval) {
1523 interval = interval || 10;
1530 stopThread = function() {
1537 startThread = function() {
1540 id = setInterval(runTasks, interval);
1545 removeTask = function(t) {
1546 removeQueue.push(t);
1548 t.onStop.apply(t.scope || t);
1553 runTasks = function() {
1554 var rqLen = removeQueue.length,
1555 now = new Date().getTime(),
1559 for (i = 0; i < rqLen; i++) {
1560 Ext.Array.remove(tasks, removeQueue[i]);
1563 if (tasks.length < 1) {
1573 for (; i < len; ++i) {
1575 itime = now - t.taskRunTime;
1576 if (t.interval <= itime) {
1577 rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
1578 t.taskRunTime = now;
1579 if (rt === false || t.taskRunCount === t.repeat) {
1584 if (t.duration && t.duration <= (now - t.taskStartTime)) {
1591 * Starts a new task.
1593 * @param {Object} task <p>A config object that supports the following properties:<ul>
1594 * <li><code>run</code> : Function<div class="sub-desc"><p>The function to execute each time the task is invoked. The
1595 * function will be called at each interval and passed the <code>args</code> argument if specified, and the
1596 * current invocation count if not.</p>
1597 * <p>If a particular scope (<code>this</code> reference) is required, be sure to specify it using the <code>scope</code> argument.</p>
1598 * <p>Return <code>false</code> from this function to terminate the task.</p></div></li>
1599 * <li><code>interval</code> : Number<div class="sub-desc">The frequency in milliseconds with which the task
1600 * should be invoked.</div></li>
1601 * <li><code>args</code> : Array<div class="sub-desc">(optional) An array of arguments to be passed to the function
1602 * specified by <code>run</code>. If not specified, the current invocation count is passed.</div></li>
1603 * <li><code>scope</code> : Object<div class="sub-desc">(optional) The scope (<tt>this</tt> reference) in which to execute the
1604 * <code>run</code> function. Defaults to the task config object.</div></li>
1605 * <li><code>duration</code> : Number<div class="sub-desc">(optional) The length of time in milliseconds to invoke
1606 * the task before stopping automatically (defaults to indefinite).</div></li>
1607 * <li><code>repeat</code> : Number<div class="sub-desc">(optional) The number of times to invoke the task before
1608 * stopping automatically (defaults to indefinite).</div></li>
1610 * <p>Before each invocation, Ext injects the property <code>taskRunCount</code> into the task object so
1611 * that calculations based on the repeat count can be performed.</p>
1612 * @return {Object} The task
1614 this.start = function(task) {
1616 task.taskStartTime = new Date().getTime();
1617 task.taskRunTime = 0;
1618 task.taskRunCount = 0;
1624 * Stops an existing running task.
1626 * @param {Object} task The task to stop
1627 * @return {Object} The task
1629 this.stop = function(task) {
1635 * Stops all tasks that are currently running.
1638 this.stopAll = function() {
1640 for (var i = 0, len = tasks.length; i < len; i++) {
1641 if (tasks[i].onStop) {
1651 * @class Ext.TaskManager
1652 * @extends Ext.util.TaskRunner
1653 * A static {@link Ext.util.TaskRunner} instance that can be used to start and stop arbitrary tasks. See
1654 * {@link Ext.util.TaskRunner} for supported methods and task config properties.
1656 // Start a simple clock task that updates a div once per second
1659 Ext.fly('clock').update(new Date().format('g:i:s A'));
1661 interval: 1000 //1 second
1663 Ext.TaskManager.start(task);
1665 * <p>See the {@link #start} method for details about how to configure a task object.</p>
1668 Ext.TaskManager = Ext.create('Ext.util.TaskRunner');
1672 * Determines information about the current platform the application is running on.
1677 init : function(navigator) {
1678 var platforms = this.platforms,
1679 ln = platforms.length,
1682 navigator = navigator || window.navigator;
1684 for (i = 0; i < ln; i++) {
1685 platform = platforms[i];
1686 this[platform.identity] = platform.regex.test(navigator[platform.property]);
1690 * @property Desktop True if the browser is running on a desktop machine
1693 this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
1695 * @property Tablet True if the browser is running on a tablet (iPad)
1697 this.Tablet = this.iPad;
1699 * @property Phone True if the browser is running on a phone.
1702 this.Phone = !this.Desktop && !this.Tablet;
1704 * @property iOS True if the browser is running on iOS
1707 this.iOS = this.iPhone || this.iPad || this.iPod;
1710 * @property Standalone Detects when application has been saved to homescreen.
1713 this.Standalone = !!window.navigator.standalone;
1717 * @property iPhone True when the browser is running on a iPhone
1721 property: 'platform',
1727 * @property iPod True when the browser is running on a iPod
1731 property: 'platform',
1737 * @property iPad True when the browser is running on a iPad
1741 property: 'userAgent',
1747 * @property Blackberry True when the browser is running on a Blackberry
1751 property: 'userAgent',
1752 regex: /Blackberry/i,
1753 identity: 'Blackberry'
1757 * @property Android True when the browser is running on an Android device
1761 property: 'userAgent',
1767 * @property Mac True when the browser is running on a Mac
1771 property: 'platform',
1777 * @property Windows True when the browser is running on Windows
1781 property: 'platform',
1787 * @property Linux True when the browser is running on Linux
1791 property: 'platform',
1800 * @class Ext.supports
1802 * Determines information about features are supported in the current environment
1809 div = doc.createElement('div'),
1815 '<div style="height:30px;width:50px;">',
1816 '<div style="height:20px;width:20px;"></div>',
1818 '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
1819 '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
1821 '<div style="float:left; background-color:transparent;"></div>'
1824 doc.body.appendChild(div);
1826 for (i = 0; i < ln; i++) {
1828 this[test.identity] = test.fn.call(this, doc, div);
1831 doc.body.removeChild(div);
1835 * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
1838 CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),
1841 * @property ClassList True if document environment supports the HTML5 classList API.
1844 ClassList: !!document.documentElement.classList,
1847 * @property OrientationChange True if the device supports orientation change
1850 OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
1853 * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
1856 DeviceMotion: ('ondevicemotion' in window),
1859 * @property Touch True if the device supports touch
1862 // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
1863 // and Safari 4.0 (they all have 'ontouchstart' in the window object).
1864 Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),
1868 * @property Transitions True if the device supports CSS3 Transitions
1872 identity: 'Transitions',
1873 fn: function(doc, div) {
1881 TE = 'TransitionEnd',
1882 transitionEndName = [
1884 'transitionend', //Moz bucks the prefixing convention
1893 for (; i < ln; i++) {
1894 if (div.getStyle(prefix[i] + "TransitionProperty")) {
1895 Ext.supports.CSS3Prefix = prefix[i];
1896 Ext.supports.CSS3TransitionEnd = transitionEndName[i];
1906 * @property RightMargin True if the device supports right margin.
1907 * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
1911 identity: 'RightMargin',
1912 fn: function(doc, div, view) {
1913 view = doc.defaultView;
1914 return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
1919 * @property TransparentColor True if the device supports transparent color
1923 identity: 'TransparentColor',
1924 fn: function(doc, div, view) {
1925 view = doc.defaultView;
1926 return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
1931 * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
1935 identity: 'ComputedStyle',
1936 fn: function(doc, div, view) {
1937 view = doc.defaultView;
1938 return view && view.getComputedStyle;
1943 * @property SVG True if the device supports SVG
1949 return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
1954 * @property Canvas True if the device supports Canvas
1960 return !!doc.createElement('canvas').getContext;
1965 * @property VML True if the device supports VML
1971 var d = doc.createElement("div");
1972 d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
1973 return (d.childNodes.length == 2);
1978 * @property Float True if the device supports CSS float
1983 fn: function(doc, div) {
1984 return !!div.lastChild.style.cssFloat;
1989 * @property AudioTag True if the device supports the HTML5 audio tag
1993 identity: 'AudioTag',
1995 return !!doc.createElement('audio').canPlayType;
2000 * @property History True if the device supports HTML5 history
2004 identity: 'History',
2006 return !!(window.history && history.pushState);
2011 * @property CSS3DTransform True if the device supports CSS3DTransform
2015 identity: 'CSS3DTransform',
2017 return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
2022 * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
2026 identity: 'CSS3LinearGradient',
2027 fn: function(doc, div) {
2028 var property = 'background-image:',
2029 webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
2030 w3c = 'linear-gradient(left top, black, white)',
2031 moz = '-moz-' + w3c,
2032 options = [property + webkit, property + w3c, property + moz];
2034 div.style.cssText = options.join(';');
2036 return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
2041 * @property CSS3BorderRadius True if the device supports CSS3 border radius
2045 identity: 'CSS3BorderRadius',
2046 fn: function(doc, div) {
2047 var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
2050 for (i = 0; i < domPrefixes.length; i++) {
2051 if (document.body.style[domPrefixes[i]] !== undefined) {
2060 * @property GeoLocation True if the device supports GeoLocation
2064 identity: 'GeoLocation',
2066 return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
2070 * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
2074 identity: 'MouseEnterLeave',
2075 fn: function(doc, div){
2076 return ('onmouseenter' in div && 'onmouseleave' in div);
2080 * @property MouseWheel True if the browser supports the mousewheel event
2084 identity: 'MouseWheel',
2085 fn: function(doc, div) {
2086 return ('onmousewheel' in div);
2090 * @property Opacity True if the browser supports normal css opacity
2094 identity: 'Opacity',
2095 fn: function(doc, div){
2096 // Not a strict equal comparison in case opacity can be converted to a number.
2097 if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
2100 div.firstChild.style.cssText = 'opacity:0.73';
2101 return div.firstChild.style.opacity == '0.73';
2105 * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
2109 identity: 'Placeholder',
2111 return 'placeholder' in doc.createElement('input');
2116 * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
2117 * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
2121 identity: 'Direct2DBug',
2123 return Ext.isString(document.body.style.msTransformOrigin);
2127 * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
2131 identity: 'BoundingClientRect',
2132 fn: function(doc, div) {
2133 return Ext.isFunction(div.getBoundingClientRect);
2137 identity: 'IncludePaddingInWidthCalculation',
2138 fn: function(doc, div){
2139 var el = Ext.get(div.childNodes[1].firstChild);
2140 return el.getWidth() == 210;
2144 identity: 'IncludePaddingInHeightCalculation',
2145 fn: function(doc, div){
2146 var el = Ext.get(div.childNodes[1].firstChild);
2147 return el.getHeight() == 210;
2152 * @property ArraySort True if the Array sort native method isn't bugged.
2156 identity: 'ArraySort',
2158 var a = [1,2,3,4,5].sort(function(){ return 0; });
2159 return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
2163 * @property Range True if browser support document.createRange native method.
2169 return !!document.createRange;
2173 * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
2177 identity: 'CreateContextualFragment',
2179 var range = Ext.supports.Range ? document.createRange() : false;
2181 return range && !!range.createContextualFragment;