devices.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * DevExtreme (core/devices.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var $ = require("../core/renderer");
  11. var windowUtils = require("./utils/window");
  12. var navigator = windowUtils.getNavigator();
  13. var window = windowUtils.getWindow();
  14. var extend = require("./utils/extend").extend;
  15. var isPlainObject = require("./utils/type").isPlainObject;
  16. var each = require("./utils/iterator").each;
  17. var Class = require("./class");
  18. var errors = require("./errors");
  19. var Callbacks = require("./utils/callbacks");
  20. var resizeCallbacks = require("./utils/resize_callbacks");
  21. var EventsMixin = require("./events_mixin");
  22. var SessionStorage = require("./utils/storage").sessionStorage;
  23. var viewPort = require("./utils/view_port");
  24. var Config = require("./config");
  25. var KNOWN_UA_TABLE = {
  26. iPhone: "iPhone",
  27. iPhone5: "iPhone",
  28. iPhone6: "iPhone",
  29. iPhone6plus: "iPhone",
  30. iPad: "iPad",
  31. iPadMini: "iPad Mini",
  32. androidPhone: "Android Mobile",
  33. androidTablet: "Android",
  34. win8: "MSAppHost",
  35. win8Phone: "Windows Phone 8.0",
  36. msSurface: "Windows ARM Tablet PC",
  37. desktop: "desktop",
  38. win10Phone: "Windows Phone 10.0",
  39. win10: "MSAppHost/3.0"
  40. };
  41. var DEFAULT_DEVICE = {
  42. deviceType: "desktop",
  43. platform: "generic",
  44. version: [],
  45. phone: false,
  46. tablet: false,
  47. android: false,
  48. ios: false,
  49. win: false,
  50. generic: true,
  51. grade: "A",
  52. mac: false
  53. };
  54. var uaParsers = {
  55. win: function(userAgent) {
  56. var isPhone = /windows phone/i.test(userAgent) || userAgent.match(/WPDesktop/);
  57. var isTablet = !isPhone && /Windows(.*)arm(.*)Tablet PC/i.test(userAgent);
  58. var isDesktop = !isPhone && !isTablet && /msapphost/i.test(userAgent);
  59. if (!(isPhone || isTablet || isDesktop)) {
  60. return
  61. }
  62. var matches = userAgent.match(/windows phone (\d+).(\d+)/i) || userAgent.match(/windows nt (\d+).(\d+)/i);
  63. var version = [];
  64. if (matches) {
  65. version.push(parseInt(matches[1], 10), parseInt(matches[2], 10))
  66. } else {
  67. matches = userAgent.match(/msapphost(\/(\d+).(\d+))?/i);
  68. matches && version.push(3 === parseInt(matches[2], 10) ? 10 : 8)
  69. }
  70. return {
  71. deviceType: isPhone ? "phone" : isTablet ? "tablet" : "desktop",
  72. platform: "win",
  73. version: version,
  74. grade: "A"
  75. }
  76. },
  77. ios: function(userAgent) {
  78. if (!/ip(hone|od|ad)/i.test(userAgent)) {
  79. return
  80. }
  81. var isPhone = /ip(hone|od)/i.test(userAgent);
  82. var matches = userAgent.match(/os (\d+)_(\d+)_?(\d+)?/i);
  83. var version = matches ? [parseInt(matches[1], 10), parseInt(matches[2], 10), parseInt(matches[3] || 0, 10)] : [];
  84. var isIPhone4 = 480 === window.screen.height;
  85. var grade = isIPhone4 ? "B" : "A";
  86. return {
  87. deviceType: isPhone ? "phone" : "tablet",
  88. platform: "ios",
  89. version: version,
  90. grade: grade
  91. }
  92. },
  93. android: function(userAgent) {
  94. if (!/android|htc_|silk/i.test(userAgent)) {
  95. return
  96. }
  97. var isPhone = /mobile/i.test(userAgent);
  98. var matches = userAgent.match(/android (\d+)\.?(\d+)?\.?(\d+)?/i);
  99. var version = matches ? [parseInt(matches[1], 10), parseInt(matches[2] || 0, 10), parseInt(matches[3] || 0, 10)] : [];
  100. var worseThan4_4 = version.length > 1 && (version[0] < 4 || 4 === version[0] && version[1] < 4);
  101. var grade = worseThan4_4 ? "B" : "A";
  102. return {
  103. deviceType: isPhone ? "phone" : "tablet",
  104. platform: "android",
  105. version: version,
  106. grade: grade
  107. }
  108. }
  109. };
  110. var Devices = Class.inherit({
  111. ctor: function(options) {
  112. this._window = options && options.window || window;
  113. this._realDevice = this._getDevice();
  114. this._currentDevice = void 0;
  115. this._currentOrientation = void 0;
  116. this.changed = Callbacks();
  117. if (windowUtils.hasWindow()) {
  118. this._recalculateOrientation();
  119. resizeCallbacks.add(this._recalculateOrientation.bind(this))
  120. }
  121. },
  122. current: function(deviceOrName) {
  123. if (deviceOrName) {
  124. this._currentDevice = this._getDevice(deviceOrName);
  125. this._forced = true;
  126. this.changed.fire();
  127. return
  128. }
  129. if (!this._currentDevice) {
  130. deviceOrName = void 0;
  131. try {
  132. deviceOrName = this._getDeviceOrNameFromWindowScope()
  133. } catch (e) {
  134. deviceOrName = this._getDeviceNameFromSessionStorage()
  135. } finally {
  136. if (!deviceOrName) {
  137. deviceOrName = this._getDeviceNameFromSessionStorage()
  138. }
  139. if (deviceOrName) {
  140. this._forced = true
  141. }
  142. }
  143. this._currentDevice = this._getDevice(deviceOrName)
  144. }
  145. return this._currentDevice
  146. },
  147. real: function() {
  148. return extend({}, this._realDevice)
  149. },
  150. orientation: function() {
  151. return this._currentOrientation
  152. },
  153. isForced: function() {
  154. return this._forced
  155. },
  156. isRippleEmulator: function() {
  157. return !!this._window.tinyHippos
  158. },
  159. _getCssClasses: function(device) {
  160. var result = [];
  161. var realDevice = this._realDevice;
  162. device = device || this.current();
  163. if (device.deviceType) {
  164. result.push("dx-device-" + device.deviceType);
  165. if ("desktop" !== device.deviceType) {
  166. result.push("dx-device-mobile")
  167. }
  168. }
  169. result.push("dx-device-" + realDevice.platform);
  170. if (realDevice.version && realDevice.version.length) {
  171. result.push("dx-device-" + realDevice.platform + "-" + realDevice.version[0])
  172. }
  173. if (devices.isSimulator()) {
  174. result.push("dx-simulator")
  175. }
  176. if (Config().rtlEnabled) {
  177. result.push("dx-rtl")
  178. }
  179. return result
  180. },
  181. attachCssClasses: function(element, device) {
  182. this._deviceClasses = this._getCssClasses(device).join(" ");
  183. $(element).addClass(this._deviceClasses)
  184. },
  185. detachCssClasses: function(element) {
  186. $(element).removeClass(this._deviceClasses)
  187. },
  188. isSimulator: function() {
  189. try {
  190. return this._isSimulator || windowUtils.hasWindow() && this._window.top !== this._window.self && this._window.top["dx-force-device"] || this.isRippleEmulator()
  191. } catch (e) {
  192. return false
  193. }
  194. },
  195. forceSimulator: function() {
  196. this._isSimulator = true
  197. },
  198. _getDevice: function(deviceName) {
  199. if ("genericPhone" === deviceName) {
  200. deviceName = {
  201. deviceType: "phone",
  202. platform: "generic",
  203. generic: true
  204. }
  205. }
  206. if (isPlainObject(deviceName)) {
  207. return this._fromConfig(deviceName)
  208. } else {
  209. var ua;
  210. if (deviceName) {
  211. ua = KNOWN_UA_TABLE[deviceName];
  212. if (!ua) {
  213. throw errors.Error("E0005")
  214. }
  215. } else {
  216. ua = navigator.userAgent
  217. }
  218. return this._fromUA(ua)
  219. }
  220. },
  221. _getDeviceOrNameFromWindowScope: function() {
  222. var result;
  223. if (windowUtils.hasWindow() && (this._window.top["dx-force-device-object"] || this._window.top["dx-force-device"])) {
  224. result = this._window.top["dx-force-device-object"] || this._window.top["dx-force-device"]
  225. }
  226. return result
  227. },
  228. _getDeviceNameFromSessionStorage: function() {
  229. var sessionStorage = SessionStorage();
  230. if (!sessionStorage) {
  231. return
  232. }
  233. var deviceOrName = sessionStorage.getItem("dx-force-device");
  234. try {
  235. return JSON.parse(deviceOrName)
  236. } catch (ex) {
  237. return deviceOrName
  238. }
  239. },
  240. _fromConfig: function(config) {
  241. var result = extend({}, DEFAULT_DEVICE, this._currentDevice, config);
  242. var shortcuts = {
  243. phone: "phone" === result.deviceType,
  244. tablet: "tablet" === result.deviceType,
  245. android: "android" === result.platform,
  246. ios: "ios" === result.platform,
  247. win: "win" === result.platform,
  248. generic: "generic" === result.platform
  249. };
  250. return extend(result, shortcuts)
  251. },
  252. _fromUA: function(ua) {
  253. var config;
  254. each(uaParsers, function(platform, parser) {
  255. config = parser(ua);
  256. return !config
  257. });
  258. if (config) {
  259. return this._fromConfig(config)
  260. }
  261. var isMac = /(mac os)/.test(ua.toLowerCase());
  262. var deviceWithOS = DEFAULT_DEVICE;
  263. deviceWithOS.mac = isMac;
  264. return deviceWithOS
  265. },
  266. _changeOrientation: function() {
  267. var $window = $(this._window);
  268. var orientation = $window.height() > $window.width() ? "portrait" : "landscape";
  269. if (this._currentOrientation === orientation) {
  270. return
  271. }
  272. this._currentOrientation = orientation;
  273. this.fireEvent("orientationChanged", [{
  274. orientation: orientation
  275. }])
  276. },
  277. _recalculateOrientation: function() {
  278. var windowWidth = $(this._window).width();
  279. if (this._currentWidth === windowWidth) {
  280. return
  281. }
  282. this._currentWidth = windowWidth;
  283. this._changeOrientation()
  284. }
  285. }).include(EventsMixin);
  286. var devices = new Devices;
  287. viewPort.changeCallback.add(function(viewPort, prevViewport) {
  288. devices.detachCssClasses(prevViewport);
  289. devices.attachCssClasses(viewPort)
  290. });
  291. if (!devices.isForced() && "win" === devices.current().platform) {
  292. devices.current({
  293. version: [10]
  294. })
  295. }
  296. module.exports = devices;
  297. module.exports.default = module.exports;