Upgrade to ExtJS 4.0.7 - Released 10/19/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  * Provides useful information about the current browser features.
17  * Don't instantiate directly, but use the {@link Ext#features} property instead.
18  */
19 Ext.define('Ext.env.FeatureDetector', {
20
21     statics: {
22         defaultTests: {
23             /**
24              * @property {Boolean}
25              * True if canvas element supported.
26              */
27             Canvas: function() {
28                 var element = this.getTestElement('canvas');
29                 return !!(element && element.getContext && element.getContext('2d'));
30             },
31             /**
32              * @property {Boolean}
33              * True if SVG supported.
34              */
35             SVG: function() {
36                 var doc = Ext.global.document;
37
38                 return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
39             },
40             /**
41              * @property {Boolean}
42              * True if VML supported.
43              */
44             VML: function() {
45                 var element = this.getTestElement(),
46                     ret = false;
47
48                 element.innerHTML = "<!--[if vml]><br><br><![endif]-->";
49                 ret = (element.childNodes.length === 2);
50                 element.innerHTML = "";
51
52                 return ret;
53             },
54             /**
55              * @property {Boolean}
56              * True if we're in Sencha Touch environment.
57              */
58             Touch: function() {
59                 return ('ontouchstart' in Ext.global) && !(Ext.platform && Ext.platform.name.match(/Windows|MacOSX|Linux/));
60             },
61             /**
62              * @property {Boolean}
63              * True if orientation API supported.
64              */
65             Orientation: function() {
66                 return ('orientation' in Ext.global);
67             },
68             /**
69              * @property {Boolean}
70              * True if geolocation API supported.
71              */
72             Geolocation: function() {
73                 return !!Ext.global.navigator.geolocation;
74             },
75             /**
76              * @property {Boolean}
77              * True if openDatabase API supported.
78              */
79             SqlDatabase: function() {
80                 return !!Ext.global.openDatabase;
81             },
82             /**
83              * @property {Boolean}
84              * True if WebSocket API supported.
85              */
86             Websockets: function() {
87                 return 'WebSocket' in Ext.global;
88             },
89             /**
90              * @property {Boolean}
91              * True if history.pushState supported.
92              */
93             History: function() {
94                 return !!(Ext.global.history && Ext.global.history.pushState);
95             },
96             /**
97              * @property {Boolean}
98              * True if CSS transforms supported.
99              */
100             CSSTransforms: function() {
101                 return this.isStyleSupported('transform');
102             },
103             /**
104              * @property {Boolean}
105              * True if CSS 3D transforms supported.
106              */
107             CSS3DTransforms: function() {
108                 return this.has('csstransforms') && this.isStyleSupported('perspective');
109             },
110             /**
111              * @property {Boolean}
112              * True if CSS animations supported.
113              */
114             CSSAnimations: function() {
115                 return this.isStyleSupported('animationName');
116             },
117             /**
118              * @property {Boolean}
119              * True if CSS transitions supported.
120              */
121             CSSTransitions: function() {
122                 return this.isStyleSupported('transitionProperty');
123             },
124             /**
125              * @property {Boolean}
126              * True if audio element supported.
127              */
128             Audio: function() {
129                 return !!this.getTestElement('audio').canPlayType;
130             },
131             /**
132              * @property {Boolean}
133              * True if video element supported.
134              */
135             Video: function() {
136                 return !!this.getTestElement('video').canPlayType;
137             }
138         },
139
140         stylePrefixes: ['Webkit', 'Moz', 'O', 'ms']
141     },
142
143     constructor: function() {
144         this.tests = {};
145
146         this.testElements = {};
147
148         this.registerTests(this.self.defaultTests, true);
149
150         return this;
151     },
152
153     has: function(name) {
154         if (!this.hasTest(name)) {
155             return false;
156         }
157         else if (this.has.hasOwnProperty(name)) {
158             return this.has[name];
159         }
160         else {
161             return this.getTestResult(name);
162         }
163     },
164
165     getTestResult: function(name) {
166         return !!this.getTest(name).call(this);
167     },
168
169     getTestElement: function(tag) {
170         if (!tag) {
171             tag = 'div';
172         }
173
174         if (!this.testElements[tag]) {
175             this.testElements[tag] = Ext.global.document.createElement(tag);
176         }
177
178         return this.testElements[tag];
179     },
180
181     registerTest: function(name, fn, isDefault) {
182         //<debug>
183         if (this.hasTest(name)) {
184             Ext.Error.raise({
185                 sourceClass: "Ext.env.FeatureDetector",
186                 sourceMethod: "registerTest",
187                 msg: "Test name " + name + " has already been registered"
188             });
189         }
190         //<debug>
191
192         this.tests[name] = fn;
193
194         if (isDefault) {
195             this.has[name] = this.getTestResult(name);
196         }
197
198         return this;
199     },
200
201     registerTests: function(tests, isDefault) {
202         Ext.Object.each(tests, function(name, fn) {
203             this.registerTest(name, fn, isDefault);
204         }, this);
205
206         return this;
207     },
208
209     hasTest: function(name) {
210         return this.tests.hasOwnProperty(name);
211     },
212
213     getTest: function(name) {
214         //<debug>
215         if (!this.hasTest(name)) {
216             Ext.Error.raise({
217                 sourceClass: "Ext.env.FeatureDetector",
218                 sourceMethod: "getTest",
219                 msg: "Test name " + name + " does not exist"
220             });
221         }
222         //<debug>
223
224         return this.tests[name];
225     },
226
227     getTests: function() {
228         return this.tests;
229     },
230
231     isStyleSupported: function(name, tag) {
232         var elementStyle = this.getTestElement(tag).style,
233             cName = Ext.String.capitalize(name),
234             i = this.self.stylePrefixes.length;
235
236         if (elementStyle[name] !== undefined) {
237             return true;
238         }
239
240         while (i--) {
241             if (elementStyle[this.self.stylePrefixes[i] + cName] !== undefined) {
242                 return true;
243             }
244         }
245
246         return false;
247     },
248
249     isEventSupported: function(name, tag) {
250         var element = this.getTestElement(tag),
251             eventName = 'on' + name,
252             isSupported = false;
253
254         // When using `setAttribute`, IE skips "unload", WebKit skips
255         // "unload" and "resize", whereas `in` "catches" those
256         isSupported = (eventName in element);
257
258         if (!isSupported) {
259             if (element.setAttribute && element.removeAttribute) {
260                 element.setAttribute(eventName, '');
261                 isSupported = typeof element[eventName] === 'function';
262
263                 // If property was created, "remove it" (by setting value to `undefined`)
264                 if (typeof element[eventName] !== 'undefined') {
265                     element[eventName] = undefined;
266                 }
267
268                 element.removeAttribute(eventName);
269             }
270         }
271
272         return isSupported;
273     }
274
275 }, function() {
276
277     /**
278      * @property {Ext.env.FeatureDetector} features
279      * @member Ext
280      * Global convenient instance of {@link Ext.env.FeatureDetector}.
281      */
282     Ext.features = new Ext.env.FeatureDetector();
283
284 });
285