tracker.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /**
  2. * DevExtreme (viz/vector_map/tracker.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 _events_engine = require("../../events/core/events_engine");
  11. var _events_engine2 = _interopRequireDefault(_events_engine);
  12. var _window = require("../../core/utils/window");
  13. var _window2 = _interopRequireDefault(_window);
  14. var _dom_adapter = require("../../core/dom_adapter");
  15. var _dom_adapter2 = _interopRequireDefault(_dom_adapter);
  16. var _event_emitter = require("./event_emitter");
  17. var _event_emitter2 = _interopRequireDefault(_event_emitter);
  18. var _utils = require("../../events/utils");
  19. var _utils2 = _interopRequireDefault(_utils);
  20. var _wheel = require("../../events/core/wheel");
  21. var _utils3 = require("../core/utils");
  22. function _interopRequireDefault(obj) {
  23. return obj && obj.__esModule ? obj : {
  24. "default": obj
  25. }
  26. }
  27. var navigator = _window2.default.getNavigator();
  28. var _math = Math;
  29. var _abs = _math.abs;
  30. var _sqrt = _math.sqrt;
  31. var _round = _math.round;
  32. var _addNamespace = _utils2.default.addNamespace;
  33. var _now = Date.now;
  34. var _NAME = "dxVectorMap";
  35. var EVENT_START = "start";
  36. var EVENT_MOVE = "move";
  37. var EVENT_END = "end";
  38. var EVENT_ZOOM = "zoom";
  39. var EVENT_HOVER_ON = "hover-on";
  40. var EVENT_HOVER_OFF = "hover-off";
  41. var EVENT_CLICK = "click";
  42. var EVENT_FOCUS_ON = "focus-on";
  43. var EVENT_FOCUS_MOVE = "focus-move";
  44. var EVENT_FOCUS_OFF = "focus-off";
  45. var CLICK_TIME_THRESHOLD = 500;
  46. var CLICK_COORD_THRESHOLD_MOUSE = 5;
  47. var CLICK_COORD_THRESHOLD_TOUCH = 20;
  48. var DRAG_COORD_THRESHOLD_MOUSE = 5;
  49. var DRAG_COORD_THRESHOLD_TOUCH = 10;
  50. var FOCUS_OFF_DELAY = 100;
  51. var WHEEL_COOLDOWN = 50;
  52. var WHEEL_DIRECTION_COOLDOWN = 300;
  53. var EVENTS;
  54. setupEvents();
  55. function Tracker(parameters) {
  56. var that = this;
  57. that._root = parameters.root;
  58. that._createEventHandlers(parameters.dataKey);
  59. that._createProjectionHandlers(parameters.projection);
  60. that._initEvents();
  61. that._focus = new Focus(function(name, arg) {
  62. that._fire(name, arg)
  63. });
  64. that._attachHandlers()
  65. }
  66. Tracker.prototype = {
  67. constructor: Tracker,
  68. dispose: function() {
  69. var that = this;
  70. that._detachHandlers();
  71. that._disposeEvents();
  72. that._focus.dispose();
  73. that._root = that._focus = that._docHandlers = that._rootHandlers = null
  74. },
  75. _eventNames: [EVENT_START, EVENT_MOVE, EVENT_END, EVENT_ZOOM, EVENT_CLICK, EVENT_HOVER_ON, EVENT_HOVER_OFF, EVENT_FOCUS_ON, EVENT_FOCUS_OFF, EVENT_FOCUS_MOVE],
  76. _startClick: function(event, data) {
  77. if (!data) {
  78. return
  79. }
  80. var coords = getEventCoords(event);
  81. this._clickState = {
  82. x: coords.x,
  83. y: coords.y,
  84. threshold: isTouchEvent(event) ? CLICK_COORD_THRESHOLD_TOUCH : CLICK_COORD_THRESHOLD_MOUSE,
  85. time: _now()
  86. }
  87. },
  88. _endClick: function(event, data) {
  89. var state = this._clickState;
  90. if (!state) {
  91. return
  92. }
  93. if (data && _now() - state.time <= CLICK_TIME_THRESHOLD) {
  94. var threshold = state.threshold;
  95. var coords = getEventCoords(event);
  96. if (_abs(coords.x - state.x) <= threshold && _abs(coords.y - state.y) <= threshold) {
  97. this._fire(EVENT_CLICK, {
  98. data: data,
  99. x: coords.x,
  100. y: coords.y,
  101. $event: event
  102. })
  103. }
  104. }
  105. this._clickState = null
  106. },
  107. _startDrag: function(event, data) {
  108. if (!data) {
  109. return
  110. }
  111. var coords = getEventCoords(event);
  112. var state = this._dragState = {
  113. x: coords.x,
  114. y: coords.y,
  115. data: data
  116. };
  117. this._fire(EVENT_START, {
  118. x: state.x,
  119. y: state.y,
  120. data: state.data
  121. })
  122. },
  123. _moveDrag: function(event, data) {
  124. var state = this._dragState;
  125. if (!state) {
  126. return
  127. }
  128. var coords = getEventCoords(event);
  129. var threshold = isTouchEvent(event) ? DRAG_COORD_THRESHOLD_TOUCH : DRAG_COORD_THRESHOLD_MOUSE;
  130. if (state.active || _abs(coords.x - state.x) > threshold || _abs(coords.y - state.y) > threshold) {
  131. state.x = coords.x;
  132. state.y = coords.y;
  133. state.active = true;
  134. state.data = data || {};
  135. this._fire(EVENT_MOVE, {
  136. x: state.x,
  137. y: state.y,
  138. data: state.data
  139. })
  140. }
  141. },
  142. _endDrag: function() {
  143. var state = this._dragState;
  144. if (!state) {
  145. return
  146. }
  147. this._dragState = null;
  148. this._fire(EVENT_END, {
  149. x: state.x,
  150. y: state.y,
  151. data: state.data
  152. })
  153. },
  154. _wheelZoom: function(event, data) {
  155. if (!data) {
  156. return
  157. }
  158. var that = this;
  159. var lock = that._wheelLock;
  160. var time = _now();
  161. if (time - lock.time <= WHEEL_COOLDOWN) {
  162. return
  163. }
  164. if (time - lock.dirTime > WHEEL_DIRECTION_COOLDOWN) {
  165. lock.dir = 0
  166. }
  167. var delta = adjustWheelDelta(event.delta / 120 || 0, lock);
  168. if (0 === delta) {
  169. return
  170. }
  171. var coords = getEventCoords(event);
  172. that._fire(EVENT_ZOOM, {
  173. delta: delta,
  174. x: coords.x,
  175. y: coords.y
  176. });
  177. lock.time = lock.dirTime = time
  178. },
  179. _startZoom: function(event, data) {
  180. if (!isTouchEvent(event) || !data) {
  181. return
  182. }
  183. var state = this._zoomState = this._zoomState || {};
  184. var coords;
  185. var pointer2;
  186. if (state.pointer1 && state.pointer2) {
  187. return
  188. }
  189. if (void 0 === state.pointer1) {
  190. state.pointer1 = getPointerId(event) || 0;
  191. coords = getMultitouchEventCoords(event, state.pointer1);
  192. state.x1 = state.x1_0 = coords.x;
  193. state.y1 = state.y1_0 = coords.y
  194. }
  195. if (void 0 === state.pointer2) {
  196. pointer2 = getPointerId(event) || 1;
  197. if (pointer2 !== state.pointer1) {
  198. coords = getMultitouchEventCoords(event, pointer2);
  199. if (coords) {
  200. state.x2 = state.x2_0 = coords.x;
  201. state.y2 = state.y2_0 = coords.y;
  202. state.pointer2 = pointer2;
  203. state.ready = true;
  204. this._endDrag()
  205. }
  206. }
  207. }
  208. },
  209. _moveZoom: function(event) {
  210. var state = this._zoomState;
  211. var coords;
  212. if (!state || !isTouchEvent(event)) {
  213. return
  214. }
  215. if (void 0 !== state.pointer1) {
  216. coords = getMultitouchEventCoords(event, state.pointer1);
  217. if (coords) {
  218. state.x1 = coords.x;
  219. state.y1 = coords.y
  220. }
  221. }
  222. if (void 0 !== state.pointer2) {
  223. coords = getMultitouchEventCoords(event, state.pointer2);
  224. if (coords) {
  225. state.x2 = coords.x;
  226. state.y2 = coords.y
  227. }
  228. }
  229. },
  230. _endZoom: function(event) {
  231. var state = this._zoomState;
  232. if (!state || !isTouchEvent(event)) {
  233. return
  234. }
  235. if (state.ready) {
  236. var startDistance = getDistance(state.x1_0, state.y1_0, state.x2_0, state.y2_0);
  237. var currentDistance = getDistance(state.x1, state.y1, state.x2, state.y2);
  238. this._fire(EVENT_ZOOM, {
  239. ratio: currentDistance / startDistance,
  240. x: (state.x1_0 + state.x2_0) / 2,
  241. y: (state.y1_0 + state.y2_0) / 2
  242. })
  243. }
  244. this._zoomState = null
  245. },
  246. _startHover: function(event, data) {
  247. this._doHover(event, data, true)
  248. },
  249. _moveHover: function(event, data) {
  250. this._doHover(event, data, false)
  251. },
  252. _doHover: function(event, data, isTouch) {
  253. var that = this;
  254. if (that._dragState && that._dragState.active || that._zoomState && that._zoomState.ready) {
  255. that._cancelHover();
  256. return
  257. }
  258. if (isTouchEvent(event) !== isTouch || that._hoverTarget === event.target || that._hoverState && that._hoverState.data === data) {
  259. return
  260. }
  261. that._cancelHover();
  262. if (data) {
  263. that._hoverState = {
  264. data: data
  265. };
  266. that._fire(EVENT_HOVER_ON, {
  267. data: data
  268. })
  269. }
  270. that._hoverTarget = event.target
  271. },
  272. _cancelHover: function() {
  273. var state = this._hoverState;
  274. this._hoverState = this._hoverTarget = null;
  275. if (state) {
  276. this._fire(EVENT_HOVER_OFF, {
  277. data: state.data
  278. })
  279. }
  280. },
  281. _startFocus: function(event, data) {
  282. this._doFocus(event, data, true)
  283. },
  284. _moveFocus: function(event, data) {
  285. this._doFocus(event, data, false)
  286. },
  287. _doFocus: function(event, data, isTouch) {
  288. var that = this;
  289. if (that._dragState && that._dragState.active || that._zoomState && that._zoomState.ready) {
  290. that._cancelFocus();
  291. return
  292. }
  293. if (isTouchEvent(event) !== isTouch) {
  294. return
  295. }
  296. that._focus.turnOff();
  297. data && that._focus.turnOn(data, getEventCoords(event))
  298. },
  299. _cancelFocus: function() {
  300. this._focus.cancel()
  301. },
  302. _createEventHandlers: function(DATA_KEY) {
  303. var that = this;
  304. that._docHandlers = {};
  305. that._rootHandlers = {};
  306. that._rootHandlers[EVENTS.start] = that._docHandlers[EVENTS.start] = function(event) {
  307. var isTouch = isTouchEvent(event);
  308. var data = getData(event);
  309. if (isTouch && !that._isTouchEnabled) {
  310. return
  311. }
  312. if (data) {
  313. event.preventDefault();
  314. event.stopPropagation()
  315. }
  316. that._startClick(event, data);
  317. that._startDrag(event, data);
  318. that._startZoom(event, data);
  319. that._startHover(event, data);
  320. that._startFocus(event, data)
  321. };
  322. that._docHandlers[EVENTS.move] = function(event) {
  323. var isTouch = isTouchEvent(event);
  324. var data = getData(event);
  325. if (isTouch && !that._isTouchEnabled) {
  326. return
  327. }
  328. that._moveDrag(event, data);
  329. that._moveZoom(event, data);
  330. that._moveHover(event, data);
  331. that._moveFocus(event, data)
  332. };
  333. that._docHandlers[EVENTS.end] = function(event) {
  334. var isTouch = isTouchEvent(event);
  335. var data = getData(event);
  336. if (isTouch && !that._isTouchEnabled) {
  337. return
  338. }
  339. that._endClick(event, data);
  340. that._endDrag(event, data);
  341. that._endZoom(event, data)
  342. };
  343. that._rootHandlers[EVENTS.wheel] = function(event) {
  344. that._cancelFocus();
  345. if (!that._isWheelEnabled) {
  346. return
  347. }
  348. var data = getData(event);
  349. if (data) {
  350. event.preventDefault();
  351. event.stopPropagation();
  352. that._wheelZoom(event, data)
  353. }
  354. };
  355. that._wheelLock = {
  356. dir: 0
  357. };
  358. function getData(event) {
  359. var target = event.target;
  360. return ("tspan" === target.tagName ? target.parentNode : target)[DATA_KEY]
  361. }
  362. },
  363. _createProjectionHandlers: function(projection) {
  364. var that = this;
  365. projection.on({
  366. center: handler,
  367. zoom: handler
  368. });
  369. function handler() {
  370. that._cancelFocus()
  371. }
  372. },
  373. reset: function() {
  374. var that = this;
  375. that._clickState = null;
  376. that._endDrag();
  377. that._cancelHover();
  378. that._cancelFocus()
  379. },
  380. setOptions: function(options) {
  381. var that = this;
  382. that.reset();
  383. that._detachHandlers();
  384. that._isTouchEnabled = !!(0, _utils3.parseScalar)(options.touchEnabled, true);
  385. that._isWheelEnabled = !!(0, _utils3.parseScalar)(options.wheelEnabled, true);
  386. that._attachHandlers()
  387. },
  388. _detachHandlers: function() {
  389. var that = this;
  390. if (that._isTouchEnabled) {
  391. that._root.css({
  392. "touch-action": "",
  393. "-webkit-user-select": ""
  394. }).off(_addNamespace("MSHoldVisual", _NAME)).off(_addNamespace("contextmenu", _NAME))
  395. }
  396. _events_engine2.default.off(_dom_adapter2.default.getDocument(), that._docHandlers);
  397. that._root.off(that._rootHandlers)
  398. },
  399. _attachHandlers: function() {
  400. var that = this;
  401. if (that._isTouchEnabled) {
  402. that._root.css({
  403. "touch-action": "none",
  404. "-webkit-user-select": "none"
  405. }).on(_addNamespace("MSHoldVisual", _NAME), function(event) {
  406. event.preventDefault()
  407. }).on(_addNamespace("contextmenu", _NAME), function(event) {
  408. isTouchEvent(event) && event.preventDefault()
  409. })
  410. }
  411. _events_engine2.default.on(_dom_adapter2.default.getDocument(), that._docHandlers);
  412. that._root.on(that._rootHandlers)
  413. }
  414. };
  415. var Focus = function(fire) {
  416. var that = this;
  417. var _activeData = null;
  418. var _data = null;
  419. var _disabled = false;
  420. var _offTimer = null;
  421. var _x;
  422. var _y;
  423. that.dispose = function() {
  424. clearTimeout(_offTimer);
  425. that.turnOn = that.turnOff = that.cancel = that.dispose = that = fire = _activeData = _data = _offTimer = null
  426. };
  427. that.turnOn = function(data, coords) {
  428. if (data === _data && _disabled) {
  429. return
  430. }
  431. _disabled = false;
  432. _data = data;
  433. if (_activeData) {
  434. _x = coords.x;
  435. _y = coords.y;
  436. if (_data === _activeData) {
  437. fire(EVENT_FOCUS_MOVE, {
  438. data: _data,
  439. x: _x,
  440. y: _y
  441. });
  442. onCheck(true)
  443. } else {
  444. fire(EVENT_FOCUS_ON, {
  445. data: _data,
  446. x: _x,
  447. y: _y,
  448. done: onCheck
  449. })
  450. }
  451. } else {
  452. _x = coords.x;
  453. _y = coords.y;
  454. fire(EVENT_FOCUS_ON, {
  455. data: _data,
  456. x: _x,
  457. y: _y,
  458. done: onCheck
  459. })
  460. }
  461. function onCheck(result) {
  462. _disabled = !result;
  463. if (result) {
  464. _activeData = _data;
  465. clearTimeout(_offTimer);
  466. _offTimer = null
  467. }
  468. }
  469. };
  470. that.turnOff = function() {
  471. _data = null;
  472. if (_activeData && !_disabled) {
  473. _offTimer = _offTimer || setTimeout(function() {
  474. _offTimer = null;
  475. fire(EVENT_FOCUS_OFF, {
  476. data: _activeData
  477. });
  478. _activeData = null
  479. }, FOCUS_OFF_DELAY)
  480. }
  481. };
  482. that.cancel = function() {
  483. clearTimeout(_offTimer);
  484. if (_activeData) {
  485. fire(EVENT_FOCUS_OFF, {
  486. data: _activeData
  487. })
  488. }
  489. _activeData = _data = _offTimer = null
  490. }
  491. };
  492. _event_emitter2.default.makeEventEmitter(Tracker);
  493. exports.Tracker = Tracker;
  494. function getDistance(x1, y1, x2, y2) {
  495. return _sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
  496. }
  497. function isTouchEvent(event) {
  498. var type = event.originalEvent.type;
  499. var pointerType = event.originalEvent.pointerType;
  500. return /^touch/.test(type) || /^MSPointer/.test(type) && 4 !== pointerType || /^pointer/.test(type) && "mouse" !== pointerType
  501. }
  502. function selectItem(flags, items) {
  503. var i = 0;
  504. var ii = flags.length;
  505. var item;
  506. for (; i < ii; ++i) {
  507. if (flags[i]) {
  508. item = items[i];
  509. break
  510. }
  511. }
  512. return _addNamespace(item || items[i], _NAME)
  513. }
  514. function setupEvents() {
  515. var flags = [navigator.pointerEnabled, navigator.msPointerEnabled, _window2.default.hasProperty("ontouchstart")];
  516. EVENTS = {
  517. start: selectItem(flags, ["pointerdown", "MSPointerDown", "touchstart mousedown", "mousedown"]),
  518. move: selectItem(flags, ["pointermove", "MSPointerMove", "touchmove mousemove", "mousemove"]),
  519. end: selectItem(flags, ["pointerup", "MSPointerUp", "touchend mouseup", "mouseup"]),
  520. wheel: _addNamespace(_wheel.name, _NAME)
  521. }
  522. }
  523. function getEventCoords(event) {
  524. var originalEvent = event.originalEvent;
  525. var touch = originalEvent.touches && originalEvent.touches[0] || {};
  526. return {
  527. x: touch.pageX || originalEvent.pageX || event.pageX,
  528. y: touch.pageY || originalEvent.pageY || event.pageY
  529. }
  530. }
  531. function getPointerId(event) {
  532. return event.originalEvent.pointerId
  533. }
  534. function getMultitouchEventCoords(event, pointerId) {
  535. var originalEvent = event.originalEvent;
  536. if (void 0 !== originalEvent.pointerId) {
  537. originalEvent = originalEvent.pointerId === pointerId ? originalEvent : null
  538. } else {
  539. originalEvent = originalEvent.touches[pointerId]
  540. }
  541. return originalEvent ? {
  542. x: originalEvent.pageX || event.pageX,
  543. y: originalEvent.pageY || event.pageY
  544. } : null
  545. }
  546. function adjustWheelDelta(delta, lock) {
  547. if (0 === delta) {
  548. return 0
  549. }
  550. var _delta = _abs(delta);
  551. var sign = _round(delta / _delta);
  552. if (lock.dir && sign !== lock.dir) {
  553. return 0
  554. }
  555. lock.dir = sign;
  556. if (_delta < .1) {
  557. _delta = 0
  558. } else {
  559. if (_delta < 1) {
  560. _delta = 1
  561. } else {
  562. if (_delta > 4) {
  563. _delta = 4
  564. } else {
  565. _delta = _round(_delta)
  566. }
  567. }
  568. }
  569. return sign * _delta
  570. }