Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / core / src / env / FeatureDetector.js
diff --git a/src/core/src/env/FeatureDetector.js b/src/core/src/env/FeatureDetector.js
new file mode 100644 (file)
index 0000000..b373dc2
--- /dev/null
@@ -0,0 +1,208 @@
+/**
+ * @class Ext.env.FeatureDetector
+ */
+Ext.define('Ext.env.FeatureDetector', {
+
+    statics: {
+        defaultTests: {
+            Canvas: function() {
+                var element = this.getTestElement('canvas');
+                return !!(element && element.getContext && element.getContext('2d'));
+            },
+            SVG: function() {
+                var doc = Ext.global.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><br><![endif]-->";
+                ret = (element.childNodes.length === 2);
+                element.innerHTML = "";
+
+                return ret;
+            },
+            Touch: function() {
+                return ('ontouchstart' in Ext.global) && !(Ext.platform && Ext.platform.name.match(/Windows|MacOSX|Linux/));
+            },
+            Orientation: function() {
+                return ('orientation' in Ext.global);
+            },
+            Geolocation: function() {
+                return !!Ext.global.navigator.geolocation;
+            },
+            SqlDatabase: function() {
+                return !!Ext.global.openDatabase;
+            },
+            Websockets: function() {
+                return 'WebSocket' in Ext.global;
+            },
+            History: function() {
+                return !!(Ext.global.history && Ext.global.history.pushState);
+            },
+            CSSTransforms: function() {
+                return this.isStyleSupported('transform');
+            },
+            CSS3DTransforms: function() {
+                return this.has('csstransforms') && this.isStyleSupported('perspective');
+            },
+            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;
+            }
+        },
+
+        stylePrefixes: ['Webkit', 'Moz', 'O', 'ms']
+    },
+
+    constructor: function() {
+        this.tests = {};
+
+        this.testElements = {};
+
+        this.registerTests(this.self.defaultTests, true);
+
+        return this;
+    },
+
+    has: function(name) {
+        if (!this.hasTest(name)) {
+            return false;
+        }
+        else if (this.has.hasOwnProperty(name)) {
+            return this.has[name];
+        }
+        else {
+            return this.getTestResult(name);
+        }
+    },
+
+    getTestResult: function(name) {
+        return !!this.getTest(name).call(this);
+    },
+
+    getTestElement: function(tag) {
+        if (!tag) {
+            tag = 'div';
+        }
+
+        if (!this.testElements[tag]) {
+            this.testElements[tag] = Ext.global.document.createElement(tag);
+        }
+
+        return this.testElements[tag];
+    },
+
+    registerTest: function(name, fn, isDefault) {
+        //<debug>
+        if (this.hasTest(name)) {
+            Ext.Error.raise({
+                sourceClass: "Ext.env.FeatureDetector",
+                sourceMethod: "registerTest",
+                msg: "Test name " + name + " has already been registered"
+            });
+        }
+        //<debug>
+
+        this.tests[name] = fn;
+
+        if (isDefault) {
+            this.has[name] = this.getTestResult(name);
+        }
+
+        return this;
+    },
+
+    registerTests: function(tests, isDefault) {
+        Ext.Object.each(tests, function(name, fn) {
+            this.registerTest(name, fn, isDefault);
+        }, this);
+
+        return this;
+    },
+
+    hasTest: function(name) {
+        return this.tests.hasOwnProperty(name);
+    },
+
+    getTest: function(name) {
+        //<debug>
+        if (!this.hasTest(name)) {
+            Ext.Error.raise({
+                sourceClass: "Ext.env.FeatureDetector",
+                sourceMethod: "getTest",
+                msg: "Test name " + name + " does not exist"
+            });
+        }
+        //<debug>
+
+        return this.tests[name];
+    },
+
+    getTests: function() {
+        return this.tests;
+    },
+
+    isStyleSupported: function(name, tag) {
+        var elementStyle = this.getTestElement(tag).style,
+            cName = Ext.String.capitalize(name),
+            i = this.self.stylePrefixes.length;
+
+        if (elementStyle[name] !== undefined) {
+            return true;
+        }
+
+        while (i--) {
+            if (elementStyle[this.self.stylePrefixes[i] + cName] !== undefined) {
+                return true;
+            }
+        }
+
+        return false;
+    },
+
+    isEventSupported: function(name, tag) {
+        var element = this.getTestElement(tag),
+            eventName = 'on' + name,
+            isSupported = false;
+
+        // When using `setAttribute`, IE skips "unload", WebKit skips
+        // "unload" and "resize", whereas `in` "catches" those
+        isSupported = (eventName in element);
+
+        if (!isSupported) {
+            if (element.setAttribute && element.removeAttribute) {
+                element.setAttribute(eventName, '');
+                isSupported = typeof element[eventName] === 'function';
+
+                // If property was created, "remove it" (by setting value to `undefined`)
+                if (typeof element[eventName] !== 'undefined') {
+                    element[eventName] = undefined;
+                }
+
+                element.removeAttribute(eventName);
+            }
+        }
+
+        return isSupported;
+    }
+
+}, function() {
+
+/*
+ * Global convenient instance of {@link Ext.env.FeatureDetector Ext.env.FeatureDetector}
+ * @member Ext features
+ */
+Ext.features = new Ext.env.FeatureDetector();
+
+});