UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

472 lines (407 loc) • 13 kB
"use strict"; var $ = require("../core/renderer"), windowUtils = require("./utils/window"), navigator = windowUtils.getNavigator(), window = windowUtils.getWindow(), extend = require("./utils/extend").extend, isPlainObject = require("./utils/type").isPlainObject, each = require("./utils/iterator").each, Class = require("./class"), errors = require("./errors"), Callbacks = require("./utils/callbacks"), resizeCallbacks = require("./utils/resize_callbacks"), EventsMixin = require("./events_mixin"), SessionStorage = require("./utils/storage").sessionStorage, viewPort = require("./utils/view_port"), Config = require("./config"); var KNOWN_UA_TABLE = { "iPhone": "iPhone", "iPhone5": "iPhone", "iPhone6": "iPhone", "iPhone6plus": "iPhone", "iPad": "iPad", "iPadMini": "iPad Mini", "androidPhone": "Android Mobile", "androidTablet": "Android", "win8": "MSAppHost", "win8Phone": "Windows Phone 8.0", "msSurface": "Windows ARM Tablet PC", "desktop": "desktop", "win10Phone": "Windows Phone 10.0", "win10": "MSAppHost/3.0" }; /** * @name device * @section commonObjectStructures * @publicName Device * @type object * @namespace DevExpress * @module core/devices * @export default */ var DEFAULT_DEVICE = { /** * @name device.devicetype * @publicName deviceType * @type string * @acceptValues 'phone'|'tablet'|'desktop' */ deviceType: "desktop", /** * @name device.platform * @publicName platform * @type string * @acceptValues 'android'|'ios'|'win'|'generic' */ platform: "generic", /** * @name device.version * @publicName version * @type Array<number> */ version: [], /** * @name device.phone * @publicName phone * @type boolean */ phone: false, /** * @name device.tablet * @publicName tablet * @type boolean */ tablet: false, /** * @name device.android * @publicName android * @type boolean */ android: false, /** * @name device.ios * @publicName ios * @type boolean */ ios: false, /** * @name device.win * @publicName win * @type boolean */ win: false, /** * @name device.generic * @publicName generic * @type boolean */ generic: true, /** * @name device.grade * @publicName grade * @type string * @acceptValues 'A'|'B'|'C' */ grade: "A", // TODO: For internal use (draft, do not document these options!) mac: false }; var uaParsers = { win: function win(userAgent) { var isPhone = /windows phone/i.test(userAgent) || userAgent.match(/WPDesktop/), isTablet = !isPhone && /Windows(.*)arm(.*)Tablet PC/i.test(userAgent), isDesktop = !isPhone && !isTablet && /msapphost/i.test(userAgent); if (!(isPhone || isTablet || isDesktop)) { return; } var matches = userAgent.match(/windows phone (\d+).(\d+)/i) || userAgent.match(/windows nt (\d+).(\d+)/i), version = []; if (matches) { version.push(parseInt(matches[1], 10), parseInt(matches[2], 10)); } else { matches = userAgent.match(/msapphost(\/(\d+).(\d+))?/i); matches && version.push(parseInt(matches[2], 10) === 3 ? 10 : 8); } return { deviceType: isPhone ? "phone" : isTablet ? "tablet" : "desktop", platform: "win", version: version, grade: "A" }; }, ios: function ios(userAgent) { if (!/ip(hone|od|ad)/i.test(userAgent)) { return; } var isPhone = /ip(hone|od)/i.test(userAgent), matches = userAgent.match(/os (\d+)_(\d+)_?(\d+)?/i), version = matches ? [parseInt(matches[1], 10), parseInt(matches[2], 10), parseInt(matches[3] || 0, 10)] : [], isIPhone4 = window.screen.height === 960 / 2, grade = isIPhone4 ? "B" : "A"; return { deviceType: isPhone ? "phone" : "tablet", platform: "ios", version: version, grade: grade }; }, android: function android(userAgent) { if (!/android|htc_|silk/i.test(userAgent)) { return; } var isPhone = /mobile/i.test(userAgent), matches = userAgent.match(/android (\d+)\.(\d+)\.?(\d+)?/i), version = matches ? [parseInt(matches[1], 10), parseInt(matches[2], 10), parseInt(matches[3] || 0, 10)] : [], worseThan4_4 = version.length > 1 && (version[0] < 4 || version[0] === 4 && version[1] < 4), grade = worseThan4_4 ? "B" : "A"; return { deviceType: isPhone ? "phone" : "tablet", platform: "android", version: version, grade: grade }; } }; /** * @name DevicesObject * @section Utils * @publicName devices * @inherits EventsMixin * @namespace DevExpress */ var Devices = Class.inherit({ /** * @name DevicesObjectevents.orientationChanged * @publicName orientationChanged * @type classEventType * @type_function_param1 e:object * @type_function_param1_field1 orientation:String */ /** * @name DevicesObjectMethods.ctor * @publicName ctor(options) * @param1 options:object * @param1_field1 window:Window * @hidden */ ctor: function ctor(options) { this._window = options && options.window || window; this._realDevice = this._getDevice(); this._currentDevice = undefined; this._currentOrientation = undefined; this.changed = Callbacks(); if (windowUtils.hasWindow()) { this._recalculateOrientation(); resizeCallbacks.add(this._recalculateOrientation.bind(this)); } }, /** * @name DevicesObjectmethods.current * @publicName current() * @return Device */ /** * @name DevicesObjectmethods.current * @publicName current(deviceName) * @param1 deviceName:string|Device */ current: function current(deviceOrName) { if (deviceOrName) { this._currentDevice = this._getDevice(deviceOrName); this._forced = true; this.changed.fire(); return; } if (!this._currentDevice) { deviceOrName = undefined; try { deviceOrName = this._getDeviceOrNameFromWindowScope(); } catch (e) { deviceOrName = this._getDeviceNameFromSessionStorage(); } finally { if (!deviceOrName) { deviceOrName = this._getDeviceNameFromSessionStorage(); } if (deviceOrName) { this._forced = true; } } this._currentDevice = this._getDevice(deviceOrName); } return this._currentDevice; }, /** * @name DevicesObjectmethods.real * @publicName real() * @return Device */ real: function real() { ///#DEBUG var forceDevice = arguments[0]; if (isPlainObject(forceDevice)) { extend(this._realDevice, forceDevice); return; } ///#ENDDEBUG return extend({}, this._realDevice); }, /** * @name DevicesObjectmethods.orientation * @publicName orientation() * @return String */ orientation: function orientation() { return this._currentOrientation; }, isForced: function isForced() { return this._forced; }, isRippleEmulator: function isRippleEmulator() { return !!this._window.tinyHippos; }, _getCssClasses: function _getCssClasses(device) { var result = []; var realDevice = this._realDevice; device = device || this.current(); // TODO: use real device here? if (device.deviceType) { result.push("dx-device-" + device.deviceType); if (device.deviceType !== "desktop") { result.push("dx-device-mobile"); } } result.push("dx-device-" + realDevice.platform); if (realDevice.version && realDevice.version.length) { result.push("dx-device-" + realDevice.platform + "-" + realDevice.version[0]); } if (devices.isSimulator()) { result.push("dx-simulator"); } if (Config().rtlEnabled) { result.push("dx-rtl"); } return result; }, attachCssClasses: function attachCssClasses(element, device) { this._deviceClasses = this._getCssClasses(device).join(" "); $(element).addClass(this._deviceClasses); }, detachCssClasses: function detachCssClasses(element) { $(element).removeClass(this._deviceClasses); }, isSimulator: function isSimulator() { // NOTE: error may happen due to same-origin policy try { return this._isSimulator || windowUtils.hasWindow() && this._window.top !== this._window.self && this._window.top["dx-force-device"] || this.isRippleEmulator(); } catch (e) { return false; } }, forceSimulator: function forceSimulator() { this._isSimulator = true; }, _getDevice: function _getDevice(deviceName) { if (deviceName === "genericPhone") { deviceName = { deviceType: "phone", platform: "generic", generic: true }; } if (isPlainObject(deviceName)) { return this._fromConfig(deviceName); } else { var ua; if (deviceName) { ua = KNOWN_UA_TABLE[deviceName]; if (!ua) { throw errors.Error("E0005"); } } else { ua = navigator.userAgent; } return this._fromUA(ua); } }, _getDeviceOrNameFromWindowScope: function _getDeviceOrNameFromWindowScope() { var result; if (windowUtils.hasWindow() && (this._window.top["dx-force-device-object"] || this._window.top["dx-force-device"])) { result = this._window.top["dx-force-device-object"] || this._window.top["dx-force-device"]; } return result; }, _getDeviceNameFromSessionStorage: function _getDeviceNameFromSessionStorage() { var sessionStorage = SessionStorage(); if (!sessionStorage) { return; } var deviceOrName = sessionStorage.getItem("dx-force-device"); try { return JSON.parse(deviceOrName); } catch (ex) { return deviceOrName; } }, _fromConfig: function _fromConfig(config) { var result = extend({}, DEFAULT_DEVICE, this._currentDevice, config), shortcuts = { phone: result.deviceType === "phone", tablet: result.deviceType === "tablet", android: result.platform === "android", ios: result.platform === "ios", win: result.platform === "win", generic: result.platform === "generic" }; return extend(result, shortcuts); }, _fromUA: function _fromUA(ua) { var config; each(uaParsers, function (platform, parser) { config = parser(ua); return !config; }); if (config) { return this._fromConfig(config); } var isMac = /(mac os)/.test(ua.toLowerCase()), deviceWithOS = DEFAULT_DEVICE; deviceWithOS.mac = isMac; return deviceWithOS; }, _changeOrientation: function _changeOrientation() { var $window = $(this._window), orientation = $window.height() > $window.width() ? "portrait" : "landscape"; if (this._currentOrientation === orientation) { return; } this._currentOrientation = orientation; this.fireEvent("orientationChanged", [{ orientation: orientation }]); }, _recalculateOrientation: function _recalculateOrientation() { var windowWidth = $(this._window).width(); if (this._currentWidth === windowWidth) { return; } this._currentWidth = windowWidth; this._changeOrientation(); } }).include(EventsMixin); var devices = new Devices(); viewPort.changeCallback.add(function (viewPort, prevViewport) { devices.detachCssClasses(prevViewport); devices.attachCssClasses(viewPort); }); // TODO: remove with win8 theme if (!devices.isForced() && devices.current().platform === "win") { devices.current({ version: [10] }); } /** * @name devices * @publicName devices * @type DevicesObject * @namespace DevExpress * @hidden */ module.exports = devices;