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