//@tag dom,core

/**
 * Provides information about browser.
 *
 * Should not be manually instantiated unless for unit-testing.
 * Access the global instance stored in {@link Ext.browser} instead.
 * @private
 */
Ext.define('Ext.env.Feature', {

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

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

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

        if (!Ext.theme) {
            Ext.theme = {
                name: 'Default'
            };
        }

        Ext.theme.is = {};
        Ext.theme.is[Ext.theme.name] = true;

        Ext.onDocumentReady(function() {
            this.registerTest({
                ProperHBoxStretching: function() {
                    // IE10 currently has a bug in their flexbox row layout. We feature detect the issue here.
                    var bodyElement = document.createElement('div'),
                        innerElement = bodyElement.appendChild(document.createElement('div')),
                        contentElement = innerElement.appendChild(document.createElement('div')),
                        innerWidth;

                    bodyElement.setAttribute('style', 'width: 100px; height: 100px; position: relative;');
                    innerElement.setAttribute('style', 'position: absolute; display: -ms-flexbox; display: -webkit-flex; display: -moz-flexbox; display: flex; -ms-flex-direction: row; -webkit-flex-direction: row; -moz-flex-direction: row; flex-direction: row; min-width: 100%;');
                    contentElement.setAttribute('style', 'width: 200px; height: 50px;');
                    document.body.appendChild(bodyElement);
                    innerWidth = innerElement.offsetWidth;
                    document.body.removeChild(bodyElement);

                    return (innerWidth > 100);
                }
            });
        }, 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;
    },

    isStyleSupportedWithoutPrefix: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style;

        if (typeof elementStyle[name] !== '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() {

    /**
     * @class Ext.feature
     * @extend Ext.env.Feature
     * @singleton
     *
     * 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 {@link #has} property/method for details of the features that can be detected.
     *
     * @aside guide environment_package
     */
    Ext.feature = new this;

    var has = Ext.feature.has;

    /**
     * @method has
     * @member Ext.feature
     * Verifies if a browser feature exists or not on the current device.
     *
     * A "hybrid" property, can be either accessed as a method call, i.e:
     *
     *     if (Ext.feature.has('Canvas')) {
     *         // ...
     *     }
     *
     * or as an object with boolean properties, i.e:
     *
     *     if (Ext.feature.has.Canvas) {
     *         // ...
     *     }
     *
     * Possible properties/parameter values:
     *
     * - Canvas
     * - Svg
     * - Vml
     * - Touch - supports touch events (`touchstart`).
     * - Orientation - supports different orientations.
     * - OrientationChange - supports the `orientationchange` event.
     * - DeviceMotion - supports the `devicemotion` event.
     * - Geolocation
     * - SqlDatabase
     * - WebSockets
     * - Range - supports [DOM document fragments.][1]
     * - CreateContextualFragment - supports HTML fragment parsing using [range.createContextualFragment()][2].
     * - History - supports history management with [history.pushState()][3].
     * - CssTransforms
     * - Css3dTransforms
     * - CssAnimations
     * - CssTransitions
     * - Audio - supports the `<audio>` tag.
     * - Video - supports the `<video>` tag.
     * - ClassList - supports the HTML5 classList API.
     * - LocalStorage - LocalStorage is supported and can be written to.
     * - NumericInputPlaceHolder - Supports placeholders on numeric input fields
     * - XHR2 - Supports XMLHttpRequest 
     * - XHRUploadProgress - Supports XMLHttpRequest upload progress info
     *
     * [1]: https://developer.mozilla.org/en/DOM/range
     * [2]: https://developer.mozilla.org/en/DOM/range.createContextualFragment
     * [3]: https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history#The_pushState().C2.A0method
     *
     * @param {String} value The feature name to check.
     * @return {Boolean}
     */
    Ext.feature.registerTest({
        Canvas: function() {
            var element = this.getTestElement('canvas');
            return !!(element && element.getContext && element.getContext('2d'));
        },

        Svg: function() {
            var doc = document;

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

        Vml: function() {
            var element = this.getTestElement(),
                ret = false;

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

            return ret;
        },

        Touch: function() {
            return Ext.browser.is.Ripple || (this.isEventSupported('touchstart') && !(Ext.os && Ext.os.name.match(/Windows|MacOS|Linux/) && !Ext.os.is.BlackBerry6));
        },

        Pointer: function() {
            return !!window.navigator.msPointerEnabled;
        },

        Orientation: function() {
            return ('orientation' in window) && this.isEventSupported('orientationchange');
        },

        OrientationChange: function() {
            return this.isEventSupported('orientationchange');
        },

        DeviceMotion: function() {
            return this.isEventSupported('devicemotion');
        },

        Geolocation: function() {
            return 'geolocation' in window.navigator;
        },

        SqlDatabase: function() {
            return 'openDatabase' in window;
        },

        WebSockets: function() {
            return 'WebSocket' in window;
        },

        Range: function() {
            return !!document.createRange;
        },

        CreateContextualFragment: function() {
            var range = !!document.createRange ? document.createRange() : false;
            return range && !!range.createContextualFragment;
        },

        History: function() {
            return ('history' in window && 'pushState' in window.history);
        },

        CssTransforms: function() {
            return this.isStyleSupported('transform');
        },

        CssTransformNoPrefix: function() {
            // This extra check is needed to get around a browser bug where both 'transform' and '-webkit-transform' are present
            // but the device really only uses '-webkit-transform'. This is seen on the HTC One for example.
            // https://sencha.jira.com/browse/TOUCH-5029
            if(!Ext.browser.is.AndroidStock) {
                return this.isStyleSupportedWithoutPrefix('transform')
            } else {
                return this.isStyleSupportedWithoutPrefix('transform') && !this.isStyleSupportedWithoutPrefix('-webkit-transform');
            }
        },

        Css3dTransforms: function() {
            // See https://sencha.jira.com/browse/TOUCH-1544
            return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.browser.is.AndroidStock2;
        },

        CssAnimations: function() {
            return this.isStyleSupported('animationName');
        },

        CssTransitions: function() {
            return this.isStyleSupported('transitionProperty');
        },

        Audio: function() {
            return !!this.getTestElement('audio').canPlayType;
        },

        Video: function() {
            return !!this.getTestElement('video').canPlayType;
        },

        ClassList: function() {
            return "classList" in this.getTestElement();
        },

        LocalStorage : function() {
            var supported = false;

            try {
                if ('localStorage' in window && window['localStorage'] !== null) {
                    //this should throw an error in private browsing mode in iOS
                    localStorage.setItem('sencha-localstorage-test', 'test success');
                    //clean up if setItem worked
                    localStorage.removeItem('sencha-localstorage-test');
                    supported = true;
                }
            } catch ( e ) {}

            return supported;
        },

        XHR2 : function() {
          return window.ProgressEvent && window.FormData && window.XMLHttpRequest && ('withCredentials' in new XMLHttpRequest);
        },

        XHRUploadProgress : function() {
            if(window.XMLHttpRequest && !Ext.browser.is.AndroidStock) {
                var xhr = new XMLHttpRequest();
                return xhr && ('upload' in xhr) && ('onprogress' in xhr.upload);
            }
            return false;
        },

        NumericInputPlaceHolder: function() {
            return !(Ext.browser.is.AndroidStock4 && Ext.os.version.getMinor() < 2);
        }
    });

    //<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.browser}, {@link Ext.os} and {@link Ext.feature} classes.
     */

    /**
     * @member Ext.supports
     * @property Transitions
     * True if current device supports CSS transitions.
     * @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
     * True if current device supports 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
     * True if current device supports 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
     * True if current device supports `<audio>` tag.
     * @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
     * True if current device supports 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>
});
