    /**
     * @class Ext.is
     *
     * Determines information about the current platform the application is running on.
     *
     * @singleton
     */
    Ext.is = {
        init : function(navigator) {
            var platforms = this.platforms,
                ln = platforms.length,
                i, platform;

            navigator = navigator || window.navigator;

            for (i = 0; i < ln; i++) {
                platform = platforms[i];
                this[platform.identity] = platform.regex.test(navigator[platform.property]);
            }

            /**
             * @property Desktop True if the browser is running on a desktop machine
             * @type {Boolean}
             */
            this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
            /**
             * @property Tablet True if the browser is running on a tablet (iPad)
             */
            this.Tablet = this.iPad;
            /**
             * @property Phone True if the browser is running on a phone.
             * @type {Boolean}
             */
            this.Phone = !this.Desktop && !this.Tablet;
            /**
             * @property iOS True if the browser is running on iOS
             * @type {Boolean}
             */
            this.iOS = this.iPhone || this.iPad || this.iPod;

            /**
             * @property Standalone Detects when application has been saved to homescreen.
             * @type {Boolean}
             */
            this.Standalone = !!window.navigator.standalone;
        },

        /**
         * @property iPhone True when the browser is running on a iPhone
         * @type {Boolean}
         */
        platforms: [{
            property: 'platform',
            regex: /iPhone/i,
            identity: 'iPhone'
        },

        /**
         * @property iPod True when the browser is running on a iPod
         * @type {Boolean}
         */
        {
            property: 'platform',
            regex: /iPod/i,
            identity: 'iPod'
        },

        /**
         * @property iPad True when the browser is running on a iPad
         * @type {Boolean}
         */
        {
            property: 'userAgent',
            regex: /iPad/i,
            identity: 'iPad'
        },

        /**
         * @property Blackberry True when the browser is running on a Blackberry
         * @type {Boolean}
         */
        {
            property: 'userAgent',
            regex: /Blackberry/i,
            identity: 'Blackberry'
        },

        /**
         * @property Android True when the browser is running on an Android device
         * @type {Boolean}
         */
        {
            property: 'userAgent',
            regex: /Android/i,
            identity: 'Android'
        },

        /**
         * @property Mac True when the browser is running on a Mac
         * @type {Boolean}
         */
        {
            property: 'platform',
            regex: /Mac/i,
            identity: 'Mac'
        },

        /**
         * @property Windows True when the browser is running on Windows
         * @type {Boolean}
         */
        {
            property: 'platform',
            regex: /Win/i,
            identity: 'Windows'
        },

        /**
         * @property Linux True when the browser is running on Linux
         * @type {Boolean}
         */
        {
            property: 'platform',
            regex: /Linux/i,
            identity: 'Linux'
        }]
    };

    Ext.is.init();

    /**
     * @class Ext.supports
     *
     * Determines information about features are supported in the current environment
     *
     * @singleton
     */
    (function(){

        // this is a local copy of certain logic from (Abstract)Element.getStyle
        // to break a dependancy between the supports mechanism and Element
        // use this instead of element references to check for styling info
        var getStyle = function(element, styleName){
            var view = element.ownerDocument.defaultView,
                style = (view ? view.getComputedStyle(element, null) : element.currentStyle) || element.style;
            return style[styleName];
        };

    Ext.supports = {
        /**
         * Runs feature detection routines and sets the various flags. This is called when
         * the scripts loads (very early) and again at {@link Ext#onReady}. Some detections
         * are flagged as `early` and run immediately. Others that require the document body
         * will not run until ready.
         *
         * Each test is run only once, so calling this method from an onReady function is safe
         * and ensures that all flags have been set.
         * @markdown
         * @private
         */
        init : function() {
            var me = this,
                doc = document,
                tests = me.tests,
                n = tests.length,
                div = n && Ext.isReady && doc.createElement('div'),
                test, notRun = [];

            if (div) {
                div.innerHTML = [
                    '<div style="height:30px;width:50px;">',
                        '<div style="height:20px;width:20px;"></div>',
                    '</div>',
                    '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
                        '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
                    '</div>',
                    '<div style="position: absolute; left: 10%; top: 10%;"></div>',
                    '<div style="float:left; background-color:transparent;"></div>'
                ].join('');

                doc.body.appendChild(div);
            }

            while (n--) {
                test = tests[n];
                if (div || test.early) {
                    me[test.identity] = test.fn.call(me, doc, div);
                } else {
                    notRun.push(test);
                }
            }

            if (div) {
                doc.body.removeChild(div);
            }

            me.tests = notRun;
        },

        /**
         * @property PointerEvents True if document environment supports the CSS3 pointer-events style.
         * @type {Boolean}
         */
        PointerEvents: 'pointerEvents' in document.documentElement.style,

        /**
         * @property CSS3BoxShadow True if document environment supports the CSS3 box-shadow style.
         * @type {Boolean}
         */
        CSS3BoxShadow: 'boxShadow' in document.documentElement.style || 'WebkitBoxShadow' in document.documentElement.style || 'MozBoxShadow' in document.documentElement.style,

        /**
         * @property ClassList True if document environment supports the HTML5 classList API.
         * @type {Boolean}
         */
        ClassList: !!document.documentElement.classList,

        /**
         * @property OrientationChange True if the device supports orientation change
         * @type {Boolean}
         */
        OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),

        /**
         * @property DeviceMotion True if the device supports device motion (acceleration and rotation rate)
         * @type {Boolean}
         */
        DeviceMotion: ('ondevicemotion' in window),

        /**
         * @property Touch True if the device supports touch
         * @type {Boolean}
         */
        // is.Desktop is needed due to the bug in Chrome 5.0.375, Safari 3.1.2
        // and Safari 4.0 (they all have 'ontouchstart' in the window object).
        Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),

        /**
         * @property TimeoutActualLateness True if the browser passes the "actualLateness" parameter to
         * setTimeout. See: https://developer.mozilla.org/en/DOM/window.setTimeout
         * @type {Boolean}
         */
        TimeoutActualLateness: (function(){
            setTimeout(function(){
                Ext.supports.TimeoutActualLateness = arguments.length !== 0;
            }, 0);
        }()),

        tests: [
            /**
             * @property Transitions True if the device supports CSS3 Transitions
             * @type {Boolean}
             */
            {
                identity: 'Transitions',
                fn: function(doc, div) {
                    var prefix = [
                            'webkit',
                            'Moz',
                            'o',
                            'ms',
                            'khtml'
                        ],
                        TE = 'TransitionEnd',
                        transitionEndName = [
                            prefix[0] + TE,
                            'transitionend', //Moz bucks the prefixing convention
                            prefix[2] + TE,
                            prefix[3] + TE,
                            prefix[4] + TE
                        ],
                        ln = prefix.length,
                        i = 0,
                        out = false;

                    for (; i < ln; i++) {
                        if (getStyle(div, prefix[i] + "TransitionProperty")) {
                            Ext.supports.CSS3Prefix = prefix[i];
                            Ext.supports.CSS3TransitionEnd = transitionEndName[i];
                            out = true;
                            break;
                        }
                    }
                    return out;
                }
            },

            /**
             * @property RightMargin True if the device supports right margin.
             * See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
             * @type {Boolean}
             */
            {
                identity: 'RightMargin',
                fn: function(doc, div) {
                    var view = doc.defaultView;
                    return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
                }
            },

            /**
             * @property DisplayChangeInputSelectionBug True if INPUT elements lose their
             * selection when their display style is changed. Essentially, if a text input
             * has focus and its display style is changed, the I-beam disappears.
             *
             * This bug is encountered due to the work around in place for the {@link #RightMargin}
             * bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
             * in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
             * version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
             */
            {
                identity: 'DisplayChangeInputSelectionBug',
                early: true,
                fn: function() {
                    var webKitVersion = Ext.webKitVersion;
                    // WebKit but older than Safari 5 or Chrome 6:
                    return 0 < webKitVersion && webKitVersion < 533;
                }
            },

            /**
             * @property DisplayChangeTextAreaSelectionBug True if TEXTAREA elements lose their
             * selection when their display style is changed. Essentially, if a text area has
             * focus and its display style is changed, the I-beam disappears.
             *
             * This bug is encountered due to the work around in place for the {@link #RightMargin}
             * bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
             * be fixed in Chrome 11.
             */
            {
                identity: 'DisplayChangeTextAreaSelectionBug',
                early: true,
                fn: function() {
                    var webKitVersion = Ext.webKitVersion;

                    /*
                    Has bug w/textarea:

                    (Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
                                AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
                                Safari/534.16
                    (Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
                                AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
                                Safari/533.21.1

                    No bug:

                    (Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
                                AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
                                Safari/534.24
                    */
                    return 0 < webKitVersion && webKitVersion < 534.24;
                }
            },

            /**
             * @property TransparentColor True if the device supports transparent color
             * @type {Boolean}
             */
            {
                identity: 'TransparentColor',
                fn: function(doc, div, view) {
                    view = doc.defaultView;
                    return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
                }
            },

            /**
             * @property ComputedStyle True if the browser supports document.defaultView.getComputedStyle()
             * @type {Boolean}
             */
            {
                identity: 'ComputedStyle',
                fn: function(doc, div, view) {
                    view = doc.defaultView;
                    return view && view.getComputedStyle;
                }
            },

            /**
             * @property Svg True if the device supports SVG
             * @type {Boolean}
             */
            {
                identity: 'Svg',
                fn: function(doc) {
                    return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
                }
            },

            /**
             * @property Canvas True if the device supports Canvas
             * @type {Boolean}
             */
            {
                identity: 'Canvas',
                fn: function(doc) {
                    return !!doc.createElement('canvas').getContext;
                }
            },

            /**
             * @property Vml True if the device supports VML
             * @type {Boolean}
             */
            {
                identity: 'Vml',
                fn: function(doc) {
                    var d = doc.createElement("div");
                    d.innerHTML = "<!--[if vml]><br/><br/><![endif]-->";
                    return (d.childNodes.length == 2);
                }
            },

            /**
             * @property Float True if the device supports CSS float
             * @type {Boolean}
             */
            {
                identity: 'Float',
                fn: function(doc, div) {
                    return !!div.lastChild.style.cssFloat;
                }
            },

            /**
             * @property AudioTag True if the device supports the HTML5 audio tag
             * @type {Boolean}
             */
            {
                identity: 'AudioTag',
                fn: function(doc) {
                    return !!doc.createElement('audio').canPlayType;
                }
            },

            /**
             * @property History True if the device supports HTML5 history
             * @type {Boolean}
             */
            {
                identity: 'History',
                fn: function() {
                    var history = window.history;
                    return !!(history && history.pushState);
                }
            },

            /**
             * @property CSS3DTransform True if the device supports CSS3DTransform
             * @type {Boolean}
             */
            {
                identity: 'CSS3DTransform',
                fn: function() {
                    return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
                }
            },

    		/**
             * @property CSS3LinearGradient True if the device supports CSS3 linear gradients
             * @type {Boolean}
             */
            {
                identity: 'CSS3LinearGradient',
                fn: function(doc, div) {
                    var property = 'background-image:',
                        webkit   = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
                        w3c      = 'linear-gradient(left top, black, white)',
                        moz      = '-moz-' + w3c,
                        opera    = '-o-' + w3c,
                        options  = [property + webkit, property + w3c, property + moz, property + opera];

                    div.style.cssText = options.join(';');

                    return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
                }
            },

            /**
             * @property CSS3BorderRadius True if the device supports CSS3 border radius
             * @type {Boolean}
             */
            {
                identity: 'CSS3BorderRadius',
                fn: function(doc, div) {
                    var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
                        pass = false,
                        i;
                    for (i = 0; i < domPrefixes.length; i++) {
                        if (document.body.style[domPrefixes[i]] !== undefined) {
                            return true;
                        }
                    }
                    return pass;
                }
            },

            /**
             * @property GeoLocation True if the device supports GeoLocation
             * @type {Boolean}
             */
            {
                identity: 'GeoLocation',
                fn: function() {
                    return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
                }
            },
            /**
             * @property MouseEnterLeave True if the browser supports mouseenter and mouseleave events
             * @type {Boolean}
             */
            {
                identity: 'MouseEnterLeave',
                fn: function(doc, div){
                    return ('onmouseenter' in div && 'onmouseleave' in div);
                }
            },
            /**
             * @property MouseWheel True if the browser supports the mousewheel event
             * @type {Boolean}
             */
            {
                identity: 'MouseWheel',
                fn: function(doc, div) {
                    return ('onmousewheel' in div);
                }
            },
            /**
             * @property Opacity True if the browser supports normal css opacity
             * @type {Boolean}
             */
            {
                identity: 'Opacity',
                fn: function(doc, div){
                    // Not a strict equal comparison in case opacity can be converted to a number.
                    if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
                        return false;
                    }
                    div.firstChild.style.cssText = 'opacity:0.73';
                    return div.firstChild.style.opacity == '0.73';
                }
            },
            /**
             * @property Placeholder True if the browser supports the HTML5 placeholder attribute on inputs
             * @type {Boolean}
             */
            {
                identity: 'Placeholder',
                fn: function(doc) {
                    return 'placeholder' in doc.createElement('input');
                }
            },

            /**
             * @property Direct2DBug True if when asking for an element's dimension via offsetWidth or offsetHeight,
             * getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
             * @type {Boolean}
             */
            {
                identity: 'Direct2DBug',
                fn: function() {
                    return Ext.isString(document.body.style.msTransformOrigin);
                }
            },
            /**
             * @property BoundingClientRect True if the browser supports the getBoundingClientRect method on elements
             * @type {Boolean}
             */
            {
                identity: 'BoundingClientRect',
                fn: function(doc, div) {
                    return Ext.isFunction(div.getBoundingClientRect);
                }
            },
            {
                identity: 'IncludePaddingInWidthCalculation',
                fn: function(doc, div){
                    return div.childNodes[1].firstChild.offsetWidth == 210;
                }
            },
            {
                identity: 'IncludePaddingInHeightCalculation',
                fn: function(doc, div){
                    return div.childNodes[1].firstChild.offsetHeight == 210;
                }
            },

            /**
             * @property ArraySort True if the Array sort native method isn't bugged.
             * @type {Boolean}
             */
            {
                identity: 'ArraySort',
                fn: function() {
                    var a = [1,2,3,4,5].sort(function(){ return 0; });
                    return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
                }
            },
            /**
             * @property Range True if browser support document.createRange native method.
             * @type {Boolean}
             */
            {
                identity: 'Range',
                fn: function() {
                    return !!document.createRange;
                }
            },
            /**
             * @property CreateContextualFragment True if browser support CreateContextualFragment range native methods.
             * @type {Boolean}
             */
            {
                identity: 'CreateContextualFragment',
                fn: function() {
                    var range = Ext.supports.Range ? document.createRange() : false;

                    return range && !!range.createContextualFragment;
                }
            },

            /**
             * @property WindowOnError True if browser supports window.onerror.
             * @type {Boolean}
             */
            {
                identity: 'WindowOnError',
                fn: function () {
                    // sadly, we cannot feature detect this...
                    return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; // Chrome 10+
                }
            },

            /**
             * @property TextAreaMaxLength True if the browser supports maxlength on textareas.
             * @type {Boolean}
             */
            {
                identity: 'TextAreaMaxLength',
                fn: function(){
                    var el = document.createElement('textarea');
                    return ('maxlength' in el);
                }
            },
            /**
             * @property GetPositionPercentage True if the browser will return the left/top/right/bottom
             * position as a percentage when explicitly set as a percentage value.
             * @type {Boolean}
             */
            // Related bug: https://bugzilla.mozilla.org/show_bug.cgi?id=707691#c7
            {
                identity: 'GetPositionPercentage',
                fn: function(doc, div){
                   return getStyle(div.childNodes[2], 'left') == '10%';
                }
            }
        ]
    };
    }());

    Ext.supports.init(); // run the "early" detections now