419 lines
14 KiB
JavaScript
419 lines
14 KiB
JavaScript
//@tag dom,core
|
|
|
|
/**
|
|
* Provides information about browser.
|
|
*
|
|
* Should not be manually instantiated unless for unit-testing.
|
|
* Access the global instance stored in {@link Ext.browser} instead.
|
|
* @private
|
|
*/
|
|
Ext.define('Ext.env.Feature', {
|
|
|
|
requires: ['Ext.env.Browser', 'Ext.env.OS'],
|
|
|
|
constructor: function() {
|
|
this.testElements = {};
|
|
|
|
this.has = function(name) {
|
|
return !!this.has[name];
|
|
};
|
|
|
|
if (!Ext.theme) {
|
|
Ext.theme = {
|
|
name: 'Default'
|
|
};
|
|
}
|
|
|
|
Ext.theme.is = {};
|
|
Ext.theme.is[Ext.theme.name] = true;
|
|
|
|
Ext.onDocumentReady(function() {
|
|
this.registerTest({
|
|
ProperHBoxStretching: function() {
|
|
// IE10 currently has a bug in their flexbox row layout. We feature detect the issue here.
|
|
var bodyElement = document.createElement('div'),
|
|
innerElement = bodyElement.appendChild(document.createElement('div')),
|
|
contentElement = innerElement.appendChild(document.createElement('div')),
|
|
innerWidth;
|
|
|
|
bodyElement.setAttribute('style', 'width: 100px; height: 100px; position: relative;');
|
|
innerElement.setAttribute('style', 'position: absolute; display: -ms-flexbox; display: -webkit-flex; display: -moz-flexbox; display: flex; -ms-flex-direction: row; -webkit-flex-direction: row; -moz-flex-direction: row; flex-direction: row; min-width: 100%;');
|
|
contentElement.setAttribute('style', 'width: 200px; height: 50px;');
|
|
document.body.appendChild(bodyElement);
|
|
innerWidth = innerElement.offsetWidth;
|
|
document.body.removeChild(bodyElement);
|
|
|
|
return (innerWidth > 100);
|
|
}
|
|
});
|
|
}, this);
|
|
},
|
|
|
|
getTestElement: function(tag, createNew) {
|
|
if (tag === undefined) {
|
|
tag = 'div';
|
|
}
|
|
else if (typeof tag !== 'string') {
|
|
return tag;
|
|
}
|
|
|
|
if (createNew) {
|
|
return document.createElement(tag);
|
|
}
|
|
|
|
if (!this.testElements[tag]) {
|
|
this.testElements[tag] = document.createElement(tag);
|
|
}
|
|
|
|
return this.testElements[tag];
|
|
},
|
|
|
|
isStyleSupported: function(name, tag) {
|
|
var elementStyle = this.getTestElement(tag).style,
|
|
cName = Ext.String.capitalize(name);
|
|
|
|
if (typeof elementStyle[name] !== 'undefined'
|
|
|| typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
isStyleSupportedWithoutPrefix: function(name, tag) {
|
|
var elementStyle = this.getTestElement(tag).style;
|
|
|
|
if (typeof elementStyle[name] !== 'undefined') {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
isEventSupported: function(name, tag) {
|
|
if (tag === undefined) {
|
|
tag = window;
|
|
}
|
|
|
|
var element = this.getTestElement(tag),
|
|
eventName = 'on' + name.toLowerCase(),
|
|
isSupported = (eventName in element);
|
|
|
|
if (!isSupported) {
|
|
if (element.setAttribute && element.removeAttribute) {
|
|
element.setAttribute(eventName, '');
|
|
isSupported = typeof element[eventName] === 'function';
|
|
|
|
if (typeof element[eventName] !== 'undefined') {
|
|
element[eventName] = undefined;
|
|
}
|
|
|
|
element.removeAttribute(eventName);
|
|
}
|
|
}
|
|
|
|
return isSupported;
|
|
},
|
|
|
|
getSupportedPropertyName: function(object, name) {
|
|
var vendorName = Ext.browser.getVendorProperyName(name);
|
|
|
|
if (vendorName in object) {
|
|
return vendorName;
|
|
}
|
|
else if (name in object) {
|
|
return name;
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
registerTest: Ext.Function.flexSetter(function(name, fn) {
|
|
this.has[name] = fn.call(this);
|
|
|
|
return this;
|
|
})
|
|
|
|
}, function() {
|
|
|
|
/**
|
|
* @class Ext.feature
|
|
* @extend Ext.env.Feature
|
|
* @singleton
|
|
*
|
|
* A simple class to verify if a browser feature exists or not on the current device.
|
|
*
|
|
* if (Ext.feature.has.Canvas) {
|
|
* // do some cool things with canvas here
|
|
* }
|
|
*
|
|
* See the {@link #has} property/method for details of the features that can be detected.
|
|
*
|
|
* For more information, see the [Environment Detect Guide](../../../core_concepts/environment_detection.html)
|
|
*/
|
|
Ext.feature = new this;
|
|
|
|
var has = Ext.feature.has;
|
|
|
|
/**
|
|
* @method has
|
|
* @member Ext.feature
|
|
* Verifies if a browser feature exists or not on the current device.
|
|
*
|
|
* A "hybrid" property, can be either accessed as a method call, i.e:
|
|
*
|
|
* if (Ext.feature.has('Canvas')) {
|
|
* // ...
|
|
* }
|
|
*
|
|
* or as an object with boolean properties, i.e:
|
|
*
|
|
* if (Ext.feature.has.Canvas) {
|
|
* // ...
|
|
* }
|
|
*
|
|
* Possible properties/parameter values:
|
|
*
|
|
* - Canvas
|
|
* - Svg
|
|
* - Vml
|
|
* - Touch - supports touch events (`touchstart`).
|
|
* - Orientation - supports different orientations.
|
|
* - OrientationChange - supports the `orientationchange` event.
|
|
* - DeviceMotion - supports the `devicemotion` event.
|
|
* - Geolocation
|
|
* - SqlDatabase
|
|
* - WebSockets
|
|
* - Range - supports [DOM document fragments.][1]
|
|
* - CreateContextualFragment - supports HTML fragment parsing using [range.createContextualFragment()][2].
|
|
* - History - supports history management with [history.pushState()][3].
|
|
* - CssTransforms
|
|
* - Css3dTransforms
|
|
* - CssAnimations
|
|
* - CssTransitions
|
|
* - Audio - supports the `<audio>` tag.
|
|
* - Video - supports the `<video>` tag.
|
|
* - ClassList - supports the HTML5 classList API.
|
|
* - LocalStorage - LocalStorage is supported and can be written to.
|
|
* - NumericInputPlaceHolder - Supports placeholders on numeric input fields
|
|
* - XHR2 - Supports XMLHttpRequest
|
|
* - XHRUploadProgress - Supports XMLHttpRequest upload progress info
|
|
*
|
|
* [1]: https://developer.mozilla.org/en/DOM/range
|
|
* [2]: https://developer.mozilla.org/en/DOM/range.createContextualFragment
|
|
* [3]: https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history#The_pushState().C2.A0method
|
|
*
|
|
* @param {String} value The feature name to check.
|
|
* @return {Boolean}
|
|
*/
|
|
Ext.feature.registerTest({
|
|
Canvas: function() {
|
|
var element = this.getTestElement('canvas');
|
|
return !!(element && element.getContext && element.getContext('2d'));
|
|
},
|
|
|
|
Svg: function() {
|
|
var doc = 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><![endif]-->";
|
|
ret = (element.childNodes.length === 1);
|
|
element.innerHTML = "";
|
|
|
|
return ret;
|
|
},
|
|
|
|
Touch: function() {
|
|
return Ext.browser.is.Ripple || (this.isEventSupported('touchstart') && !(Ext.os && Ext.os.name.match(/Windows|MacOS|Linux/) && !Ext.os.is.BlackBerry6));
|
|
},
|
|
|
|
Pointer: function() {
|
|
return !!window.navigator.msPointerEnabled;
|
|
},
|
|
|
|
Orientation: function() {
|
|
return 'orientation' in window;
|
|
},
|
|
|
|
OrientationChange: function() {
|
|
return this.isEventSupported('orientationchange');
|
|
},
|
|
|
|
DeviceMotion: function() {
|
|
return this.isEventSupported('devicemotion');
|
|
},
|
|
|
|
Geolocation: function() {
|
|
return 'geolocation' in window.navigator;
|
|
},
|
|
|
|
SqlDatabase: function() {
|
|
return 'openDatabase' in window;
|
|
},
|
|
|
|
WebSockets: function() {
|
|
return 'WebSocket' in window;
|
|
},
|
|
|
|
Range: function() {
|
|
return !!document.createRange;
|
|
},
|
|
|
|
CreateContextualFragment: function() {
|
|
var range = !!document.createRange ? document.createRange() : false;
|
|
return range && !!range.createContextualFragment;
|
|
},
|
|
|
|
History: function() {
|
|
return ('history' in window && 'pushState' in window.history);
|
|
},
|
|
|
|
CssTransforms: function() {
|
|
return this.isStyleSupported('transform');
|
|
},
|
|
|
|
CssTransformNoPrefix: function() {
|
|
// This extra check is needed to get around a browser bug where both 'transform' and '-webkit-transform' are present
|
|
// but the device really only uses '-webkit-transform'. This is seen on the HTC One for example.
|
|
// https://sencha.jira.com/browse/TOUCH-5029
|
|
if(!Ext.browser.is.AndroidStock) {
|
|
return this.isStyleSupportedWithoutPrefix('transform')
|
|
} else {
|
|
return this.isStyleSupportedWithoutPrefix('transform') && !this.isStyleSupportedWithoutPrefix('-webkit-transform');
|
|
}
|
|
},
|
|
|
|
Css3dTransforms: function() {
|
|
// See https://sencha.jira.com/browse/TOUCH-1544
|
|
return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.browser.is.AndroidStock2;
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
ClassList: function() {
|
|
return "classList" in this.getTestElement();
|
|
},
|
|
|
|
LocalStorage : function() {
|
|
var supported = false;
|
|
|
|
try {
|
|
if ('localStorage' in window && window['localStorage'] !== null) {
|
|
//this should throw an error in private browsing mode in iOS
|
|
localStorage.setItem('sencha-localstorage-test', 'test success');
|
|
//clean up if setItem worked
|
|
localStorage.removeItem('sencha-localstorage-test');
|
|
supported = true;
|
|
}
|
|
} catch ( e ) {}
|
|
|
|
return supported;
|
|
},
|
|
|
|
MatchMedia: function() {
|
|
return "matchMedia" in window;
|
|
},
|
|
|
|
XHR2 : function() {
|
|
return window.ProgressEvent && window.FormData && window.XMLHttpRequest && ('withCredentials' in new XMLHttpRequest);
|
|
},
|
|
|
|
XHRUploadProgress : function() {
|
|
if(window.XMLHttpRequest && !Ext.browser.is.AndroidStock) {
|
|
var xhr = new XMLHttpRequest();
|
|
return xhr && ('upload' in xhr) && ('onprogress' in xhr.upload);
|
|
}
|
|
return false;
|
|
},
|
|
|
|
NumericInputPlaceHolder: function() {
|
|
return !(Ext.browser.is.AndroidStock4 && Ext.os.version.getMinor() < 2);
|
|
}
|
|
});
|
|
|
|
//<deprecated product=touch since=2.0>
|
|
/**
|
|
* @class Ext.supports
|
|
* Determines information about features are supported in the current environment.
|
|
* @deprecated 2.0.0
|
|
* Please use the {@link Ext.browser}, {@link Ext.os} and {@link Ext.feature} classes.
|
|
*/
|
|
|
|
/**
|
|
* @member Ext.supports
|
|
* @property Transitions
|
|
* True if current device supports CSS transitions.
|
|
* @deprecated 2.0.0 Please use {@link Ext.feature#has}.CssTransitions instead
|
|
*/
|
|
Ext.deprecatePropertyValue(has, 'Transitions', has.CssTransitions,
|
|
"Ext.supports.Transitions is deprecated, please use Ext.feature.has.CssTransitions instead");
|
|
|
|
/**
|
|
* @member Ext.supports
|
|
* @property SVG
|
|
* True if current device supports SVG.
|
|
* @deprecated 2.0.0 Please use {@link Ext.feature#has}.Svg instead
|
|
*/
|
|
Ext.deprecatePropertyValue(has, 'SVG', has.Svg,
|
|
"Ext.supports.SVG is deprecated, please use Ext.feature.has.Svg instead");
|
|
|
|
/**
|
|
* @member Ext.supports
|
|
* @property VML
|
|
* True if current device supports VML.
|
|
* @deprecated 2.0.0 Please use {@link Ext.feature#has}.Vml instead
|
|
*/
|
|
Ext.deprecatePropertyValue(has, 'VML', has.Vml,
|
|
"Ext.supports.VML is deprecated, please use Ext.feature.has.Vml instead");
|
|
|
|
/**
|
|
* @member Ext.supports
|
|
* @property AudioTag
|
|
* True if current device supports `<audio>` tag.
|
|
* @deprecated 2.0.0 Please use {@link Ext.feature#has}.Audio instead
|
|
*/
|
|
Ext.deprecatePropertyValue(has, 'AudioTag', has.Audio,
|
|
"Ext.supports.AudioTag is deprecated, please use Ext.feature.has.Audio instead");
|
|
|
|
/**
|
|
* @member Ext.supports
|
|
* @property GeoLocation
|
|
* True if current device supports geolocation.
|
|
* @deprecated 2.0.0 Please use {@link Ext.feature#has}.Geolocation instead
|
|
*/
|
|
Ext.deprecatePropertyValue(has, 'GeoLocation', has.Geolocation,
|
|
"Ext.supports.GeoLocation is deprecated, please use Ext.feature.has.Geolocation instead");
|
|
var name;
|
|
|
|
if (!Ext.supports) {
|
|
Ext.supports = {};
|
|
}
|
|
|
|
for (name in has) {
|
|
if (has.hasOwnProperty(name)) {
|
|
Ext.deprecatePropertyValue(Ext.supports, name, has[name], "Ext.supports." + name + " is deprecated, please use Ext.feature.has." + name + " instead");
|
|
}
|
|
}
|
|
//</deprecated>
|
|
});
|