/** * Provides useful information about the current browser features. * Don't instantiate directly, but use the {@link Ext#features} property instead. */ Ext.define('Ext.env.FeatureDetector', { statics: { defaultTests: { /** * @property {Boolean} * True if canvas element supported. */ Canvas: function() { var element = this.getTestElement('canvas'); return !!(element && element.getContext && element.getContext('2d')); }, /** * @property {Boolean} * True if SVG supported. */ SVG: function() { var doc = Ext.global.document; return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect); }, /** * @property {Boolean} * True if VML supported. */ VML: function() { var element = this.getTestElement(), ret = false; element.innerHTML = "<!--[if vml]><br><br><![endif]-->"; ret = (element.childNodes.length === 2); element.innerHTML = ""; return ret; }, /** * @property {Boolean} * True if we're in Sencha Touch environment. */ Touch: function() { return ('ontouchstart' in Ext.global) && !(Ext.platform && Ext.platform.name.match(/Windows|MacOSX|Linux/)); }, /** * @property {Boolean} * True if orientation API supported. */ Orientation: function() { return ('orientation' in Ext.global); }, /** * @property {Boolean} * True if geolocation API supported. */ Geolocation: function() { return !!Ext.global.navigator.geolocation; }, /** * @property {Boolean} * True if openDatabase API supported. */ SqlDatabase: function() { return !!Ext.global.openDatabase; }, /** * @property {Boolean} * True if WebSocket API supported. */ Websockets: function() { return 'WebSocket' in Ext.global; }, /** * @property {Boolean} * True if history.pushState supported. */ History: function() { return !!(Ext.global.history && Ext.global.history.pushState); }, /** * @property {Boolean} * True if CSS transforms supported. */ CSSTransforms: function() { return this.isStyleSupported('transform'); }, /** * @property {Boolean} * True if CSS 3D transforms supported. */ CSS3DTransforms: function() { return this.has('csstransforms') && this.isStyleSupported('perspective'); }, /** * @property {Boolean} * True if CSS animations supported. */ CSSAnimations: function() { return this.isStyleSupported('animationName'); }, /** * @property {Boolean} * True if CSS transitions supported. */ CSSTransitions: function() { return this.isStyleSupported('transitionProperty'); }, /** * @property {Boolean} * True if audio element supported. */ Audio: function() { return !!this.getTestElement('audio').canPlayType; }, /** * @property {Boolean} * True if video element supported. */ 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() { /** * @property {Ext.env.FeatureDetector} features * @member Ext * Global convenient instance of {@link Ext.env.FeatureDetector}. */ Ext.features = new Ext.env.FeatureDetector(); });