/**
 * @aside guide environment_package
 *
 * A class to detect if the current browser supports various features.
 *
 * Please refer to the documentation of {@link Ext.feature.has} on how to use it.
 *
 *     if (Ext.feature.has.Canvas) {
 *         // do some cool things with canvas here
 *     }
 */
Ext.define('Ext.env.Feature', {

    requires: ['Ext.env.Browser', 'Ext.env.OS'],

    constructor: function() {
        this.testElements = {};

        this.has = function(name) {
            return !!this.has[name];
        };

        return this;
    },

    getTestElement: function(tag, createNew) {
        if (tag === undefined) {
            tag = 'div';
        }
        else if (typeof tag !== 'string') {
            return tag;
        }

        if (createNew) {
            return document.createElement(tag);
        }

        if (!this.testElements[tag]) {
            this.testElements[tag] = document.createElement(tag);
        }

        return this.testElements[tag];
    },

    isStyleSupported: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style,
            cName = Ext.String.capitalize(name);

        if (typeof elementStyle[name] !== 'undefined'
            || typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
            return true;
        }

        return false;
    },

    isEventSupported: function(name, tag) {
        if (tag === undefined) {
            tag = window;
        }

        var element = this.getTestElement(tag),
            eventName = 'on' + name.toLowerCase(),
            isSupported = (eventName in element);

        if (!isSupported) {
            if (element.setAttribute && element.removeAttribute) {
                element.setAttribute(eventName, '');
                isSupported = typeof element[eventName] === 'function';

                if (typeof element[eventName] !== 'undefined') {
                    element[eventName] = undefined;
                }

                element.removeAttribute(eventName);
            }
        }

        return isSupported;
    },

    getSupportedPropertyName: function(object, name) {
        var vendorName = Ext.browser.getVendorProperyName(name);

        if (vendorName in object) {
            return vendorName;
        }
        else if (name in object) {
            return name;
        }

        return null;
    },

    registerTest: Ext.Function.flexSetter(function(name, fn) {
        this.has[name] = fn.call(this);

        return this;
    })

}, function() {

    Ext.feature = new this;

    var has = Ext.feature.has;

    /**
     * @class Ext.feature.has
     * A simple class to verify if a browser feature exists or not on the current device.
     *
     *     if (Ext.feature.has.Canvas) {
     *         // do some cool things with canvas here
     *     }
     *
     * See the list of properties below too see which features are available for detection.
     */

    Ext.feature.registerTest({
        /**
         * @member Ext.feature.has
         * @property {Boolean} Canvas
         * True if the current device supports Canvas.
         */
        Canvas: function() {
            var element = this.getTestElement('canvas');
            return !!(element && element.getContext && element.getContext('2d'));
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Svg
         * True if the current device supports SVG.
         */
        Svg: function() {
            var doc = document;

            return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Vml
         * True if the current device supports VML.
         */
        Vml: function() {
            var element = this.getTestElement(),
                ret = false;

            element.innerHTML = "<!--[if vml]><br><![endif]-->";
            ret = (element.childNodes.length === 1);
            element.innerHTML = "";

            return ret;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Touch
         * True if the current device supports touch events (`touchstart`).
         */
        Touch: function() {
            return this.isEventSupported('touchstart') && !(Ext.os && Ext.os.name.match(/Windows|MacOS|Linux/));
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Orientation
         * True if the current device supports different orientations.
         */
        Orientation: function() {
            return ('orientation' in window) && this.isEventSupported('orientationchange');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} OrientationChange
         * True if the current device supports the `orientationchange` event.
         */
        OrientationChange: function() {
            return this.isEventSupported('orientationchange');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} DeviceMotion
         * True if the current device supports the `devicemotion` event.
         */
        DeviceMotion: function() {
            return this.isEventSupported('devicemotion');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Geolocation
         * True if the current device supports Geolocation.
         */
        Geolocation: function() {
            return 'geolocation' in window.navigator;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} SqlDatabase
         * True if the current device supports SQL Databases.
         */
        SqlDatabase: function() {
            return 'openDatabase' in window;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} WebSockets
         * True if the current device supports WebSockets.
         */
        WebSockets: function() {
            return 'WebSocket' in window;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Range
         * True if the current device supports [DOM document fragments.][1]
         *
         * [1]: https://developer.mozilla.org/en/DOM/range
         */
        Range: function() {
            return !!document.createRange;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CreateContextualFragment
         * True if the current device supports HTML fragment parsing using [range.createContextualFragment()][1].
         *
         * [1]: https://developer.mozilla.org/en/DOM/range.createContextualFragment
         */
        CreateContextualFragment: function() {
            var range = !!document.createRange ? document.createRange() : false;
            return range && !!range.createContextualFragment;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} History
         * True if the current device supports history management with [history.pushState()][1].
         *
         * [1]: https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history#The_pushState().C2.A0method
         */
        History: function() {
            return ('history' in window && 'pushState' in window.history);
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssTransforms
         * True if the current device supports CSS Transform animations.
         */
        CssTransforms: function() {
            return this.isStyleSupported('transform');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Css3dTransforms
         * True if the current device supports CSS 3D Transform animations.
         */
        Css3dTransforms: function() {
            // See https://sencha.jira.com/browse/TOUCH-1544
            return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.os.is.Android2;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssAnimations
         * True if the current device supports CSS Animations.
         */
        CssAnimations: function() {
            return this.isStyleSupported('animationName');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} CssTransitions
         * True if the current device supports CSS Transitions.
         */
        CssTransitions: function() {
            return this.isStyleSupported('transitionProperty');
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Audio
         * True if the current device supports the `<audio>` tag.
         */
        Audio: function() {
            return !!this.getTestElement('audio').canPlayType;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} Video
         * True if the current device supports the `<video>` tag.
         */
        Video: function() {
            return !!this.getTestElement('video').canPlayType;
        },

        /**
         * @member Ext.feature.has
         * @property {Boolean} ClassList
         * True if document environment supports the HTML5 classList API.
         */
        ClassList: function() {
            return "classList" in this.getTestElement();
        }
    });

    //<deprecated product=touch since=2.0>
    /**
     * @class Ext.supports
     * Determines information about features are supported in the current environment.
     * @deprecated 2.0.0
     * Please use the {@link Ext.env.Browser}, {@link Ext.env.OS} and {@link Ext.feature.has} classes.
     */

    /**
     * @member Ext.supports
     * @property Transitions
     * @inheritdoc Ext.feature.has#CssTransitions
     * @deprecated 2.0.0 Please use {@link Ext.feature.has#CssTransitions} instead
     */
    Ext.deprecatePropertyValue(has, 'Transitions', has.CssTransitions,
                          "Ext.supports.Transitions is deprecated, please use Ext.feature.has.CssTransitions instead");

    /**
     * @member Ext.supports
     * @property SVG
     * @inheritdoc Ext.feature.has#Svg
     * @deprecated 2.0.0 Please use {@link Ext.feature.has#Svg} instead
     */
    Ext.deprecatePropertyValue(has, 'SVG', has.Svg,
                          "Ext.supports.SVG is deprecated, please use Ext.feature.has.Svg instead");

    /**
     * @member Ext.supports
     * @property VML
     * @inheritdoc Ext.feature.has#Vml
     * @deprecated 2.0.0 Please use {@link Ext.feature.has#Vml} instead
     */
    Ext.deprecatePropertyValue(has, 'VML', has.Vml,
                          "Ext.supports.VML is deprecated, please use Ext.feature.has.Vml instead");

    /**
     * @member Ext.supports
     * @property AudioTag
     * @inheritdoc Ext.feature.has#Audio
     * @deprecated 2.0.0 Please use {@link Ext.feature.has#Audio} instead
     */
    Ext.deprecatePropertyValue(has, 'AudioTag', has.Audio,
                          "Ext.supports.AudioTag is deprecated, please use Ext.feature.has.Audio instead");

    /**
     * @member Ext.supports
     * @property GeoLocation
     * @inheritdoc Ext.feature.has#Geolocation
     * @deprecated 2.0.0 Please use {@link Ext.feature.has#Geolocation} instead
     */
    Ext.deprecatePropertyValue(has, 'GeoLocation', has.Geolocation,
                          "Ext.supports.GeoLocation is deprecated, please use Ext.feature.has.Geolocation instead");
    var name;

    if (!Ext.supports) {
        Ext.supports = {};
    }

    for (name in has) {
        if (has.hasOwnProperty(name)) {
            Ext.deprecatePropertyValue(Ext.supports, name, has[name], "Ext.supports." + name + " is deprecated, please use Ext.feature.has." + name + " instead");
        }
    }
    //</deprecated>
});