Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / core / src / env / FeatureDetector.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.env.FeatureDetector
17  */
18 Ext.define('Ext.env.FeatureDetector', {
19
20     statics: {
21         defaultTests: {
22             Canvas: function() {
23                 var element = this.getTestElement('canvas');
24                 return !!(element && element.getContext && element.getContext('2d'));
25             },
26             SVG: function() {
27                 var doc = Ext.global.document;
28
29                 return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
30             },
31             VML: function() {
32                 var element = this.getTestElement(),
33                     ret = false;
34
35                 element.innerHTML = "<!--[if vml]><br><br><![endif]-->";
36                 ret = (element.childNodes.length === 2);
37                 element.innerHTML = "";
38
39                 return ret;
40             },
41             Touch: function() {
42                 return ('ontouchstart' in Ext.global) && !(Ext.platform && Ext.platform.name.match(/Windows|MacOSX|Linux/));
43             },
44             Orientation: function() {
45                 return ('orientation' in Ext.global);
46             },
47             Geolocation: function() {
48                 return !!Ext.global.navigator.geolocation;
49             },
50             SqlDatabase: function() {
51                 return !!Ext.global.openDatabase;
52             },
53             Websockets: function() {
54                 return 'WebSocket' in Ext.global;
55             },
56             History: function() {
57                 return !!(Ext.global.history && Ext.global.history.pushState);
58             },
59             CSSTransforms: function() {
60                 return this.isStyleSupported('transform');
61             },
62             CSS3DTransforms: function() {
63                 return this.has('csstransforms') && this.isStyleSupported('perspective');
64             },
65             CSSAnimations: function() {
66                 return this.isStyleSupported('animationName');
67             },
68             CSSTransitions: function() {
69                 return this.isStyleSupported('transitionProperty');
70             },
71             Audio: function() {
72                 return !!this.getTestElement('audio').canPlayType;
73             },
74             Video: function() {
75                 return !!this.getTestElement('video').canPlayType;
76             }
77         },
78
79         stylePrefixes: ['Webkit', 'Moz', 'O', 'ms']
80     },
81
82     constructor: function() {
83         this.tests = {};
84
85         this.testElements = {};
86
87         this.registerTests(this.self.defaultTests, true);
88
89         return this;
90     },
91
92     has: function(name) {
93         if (!this.hasTest(name)) {
94             return false;
95         }
96         else if (this.has.hasOwnProperty(name)) {
97             return this.has[name];
98         }
99         else {
100             return this.getTestResult(name);
101         }
102     },
103
104     getTestResult: function(name) {
105         return !!this.getTest(name).call(this);
106     },
107
108     getTestElement: function(tag) {
109         if (!tag) {
110             tag = 'div';
111         }
112
113         if (!this.testElements[tag]) {
114             this.testElements[tag] = Ext.global.document.createElement(tag);
115         }
116
117         return this.testElements[tag];
118     },
119
120     registerTest: function(name, fn, isDefault) {
121         //<debug>
122         if (this.hasTest(name)) {
123             Ext.Error.raise({
124                 sourceClass: "Ext.env.FeatureDetector",
125                 sourceMethod: "registerTest",
126                 msg: "Test name " + name + " has already been registered"
127             });
128         }
129         //<debug>
130
131         this.tests[name] = fn;
132
133         if (isDefault) {
134             this.has[name] = this.getTestResult(name);
135         }
136
137         return this;
138     },
139
140     registerTests: function(tests, isDefault) {
141         Ext.Object.each(tests, function(name, fn) {
142             this.registerTest(name, fn, isDefault);
143         }, this);
144
145         return this;
146     },
147
148     hasTest: function(name) {
149         return this.tests.hasOwnProperty(name);
150     },
151
152     getTest: function(name) {
153         //<debug>
154         if (!this.hasTest(name)) {
155             Ext.Error.raise({
156                 sourceClass: "Ext.env.FeatureDetector",
157                 sourceMethod: "getTest",
158                 msg: "Test name " + name + " does not exist"
159             });
160         }
161         //<debug>
162
163         return this.tests[name];
164     },
165
166     getTests: function() {
167         return this.tests;
168     },
169
170     isStyleSupported: function(name, tag) {
171         var elementStyle = this.getTestElement(tag).style,
172             cName = Ext.String.capitalize(name),
173             i = this.self.stylePrefixes.length;
174
175         if (elementStyle[name] !== undefined) {
176             return true;
177         }
178
179         while (i--) {
180             if (elementStyle[this.self.stylePrefixes[i] + cName] !== undefined) {
181                 return true;
182             }
183         }
184
185         return false;
186     },
187
188     isEventSupported: function(name, tag) {
189         var element = this.getTestElement(tag),
190             eventName = 'on' + name,
191             isSupported = false;
192
193         // When using `setAttribute`, IE skips "unload", WebKit skips
194         // "unload" and "resize", whereas `in` "catches" those
195         isSupported = (eventName in element);
196
197         if (!isSupported) {
198             if (element.setAttribute && element.removeAttribute) {
199                 element.setAttribute(eventName, '');
200                 isSupported = typeof element[eventName] === 'function';
201
202                 // If property was created, "remove it" (by setting value to `undefined`)
203                 if (typeof element[eventName] !== 'undefined') {
204                     element[eventName] = undefined;
205                 }
206
207                 element.removeAttribute(eventName);
208             }
209         }
210
211         return isSupported;
212     }
213
214 }, function() {
215
216 /*
217  * Global convenient instance of {@link Ext.env.FeatureDetector Ext.env.FeatureDetector}
218  * @member Ext features
219  */
220 Ext.features = new Ext.env.FeatureDetector();
221
222 });
223