阅行客电子档案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1472 lines
42 KiB

2 years ago
  1. /*!
  2. * PEP v0.4.3 | https://github.com/jquery/PEP
  3. * Copyright jQuery Foundation and other contributors | http://jquery.org/license
  4. */
  5. (function (global, factory) {
  6. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  7. typeof define === 'function' && define.amd ? define(factory) :
  8. (global.PointerEventsPolyfill = factory());
  9. }(this, function () { 'use strict';
  10. /**
  11. * This is the constructor for new PointerEvents.
  12. *
  13. * New Pointer Events must be given a type, and an optional dictionary of
  14. * initialization properties.
  15. *
  16. * Due to certain platform requirements, events returned from the constructor
  17. * identify as MouseEvents.
  18. *
  19. * @constructor
  20. * @param {String} inType The type of the event to create.
  21. * @param {Object} [inDict] An optional dictionary of initial event properties.
  22. * @return {Event} A new PointerEvent of type `inType`, initialized with properties from `inDict`.
  23. */
  24. var MOUSE_PROPS = [
  25. 'bubbles',
  26. 'cancelable',
  27. 'view',
  28. 'detail',
  29. 'screenX',
  30. 'screenY',
  31. 'clientX',
  32. 'clientY',
  33. 'ctrlKey',
  34. 'altKey',
  35. 'shiftKey',
  36. 'metaKey',
  37. 'button',
  38. 'relatedTarget',
  39. 'pageX',
  40. 'pageY'
  41. ];
  42. var MOUSE_DEFAULTS = [
  43. false,
  44. false,
  45. null,
  46. null,
  47. 0,
  48. 0,
  49. 0,
  50. 0,
  51. false,
  52. false,
  53. false,
  54. false,
  55. 0,
  56. null,
  57. 0,
  58. 0
  59. ];
  60. function PointerEvent(inType, inDict) {
  61. inDict = inDict || Object.create(null);
  62. var e = document.createEvent('Event');
  63. e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
  64. // define inherited MouseEvent properties
  65. // skip bubbles and cancelable since they're set above in initEvent()
  66. for (var i = 2, p; i < MOUSE_PROPS.length; i++) {
  67. p = MOUSE_PROPS[i];
  68. e[p] = inDict[p] || MOUSE_DEFAULTS[i];
  69. }
  70. e.buttons = inDict.buttons || 0;
  71. // Spec requires that pointers without pressure specified use 0.5 for down
  72. // state and 0 for up state.
  73. var pressure = 0;
  74. if (inDict.pressure && e.buttons) {
  75. pressure = inDict.pressure;
  76. } else {
  77. pressure = e.buttons ? 0.5 : 0;
  78. }
  79. // add x/y properties aliased to clientX/Y
  80. e.x = e.clientX;
  81. e.y = e.clientY;
  82. // define the properties of the PointerEvent interface
  83. e.pointerId = inDict.pointerId || 0;
  84. e.width = inDict.width || 0;
  85. e.height = inDict.height || 0;
  86. e.pressure = pressure;
  87. e.tiltX = inDict.tiltX || 0;
  88. e.tiltY = inDict.tiltY || 0;
  89. e.twist = inDict.twist || 0;
  90. e.tangentialPressure = inDict.tangentialPressure || 0;
  91. e.pointerType = inDict.pointerType || '';
  92. e.hwTimestamp = inDict.hwTimestamp || 0;
  93. e.isPrimary = inDict.isPrimary || false;
  94. return e;
  95. }
  96. /**
  97. * This module implements a map of pointer states
  98. */
  99. var USE_MAP = window.Map && window.Map.prototype.forEach;
  100. var PointerMap = USE_MAP ? Map : SparseArrayMap;
  101. function SparseArrayMap() {
  102. this.array = [];
  103. this.size = 0;
  104. }
  105. SparseArrayMap.prototype = {
  106. set: function(k, v) {
  107. if (v === undefined) {
  108. return this.delete(k);
  109. }
  110. if (!this.has(k)) {
  111. this.size++;
  112. }
  113. this.array[k] = v;
  114. },
  115. has: function(k) {
  116. return this.array[k] !== undefined;
  117. },
  118. delete: function(k) {
  119. if (this.has(k)) {
  120. delete this.array[k];
  121. this.size--;
  122. }
  123. },
  124. get: function(k) {
  125. return this.array[k];
  126. },
  127. clear: function() {
  128. this.array.length = 0;
  129. this.size = 0;
  130. },
  131. // return value, key, map
  132. forEach: function(callback, thisArg) {
  133. return this.array.forEach(function(v, k) {
  134. callback.call(thisArg, v, k, this);
  135. }, this);
  136. }
  137. };
  138. var CLONE_PROPS = [
  139. // MouseEvent
  140. 'bubbles',
  141. 'cancelable',
  142. 'view',
  143. 'detail',
  144. 'screenX',
  145. 'screenY',
  146. 'clientX',
  147. 'clientY',
  148. 'ctrlKey',
  149. 'altKey',
  150. 'shiftKey',
  151. 'metaKey',
  152. 'button',
  153. 'relatedTarget',
  154. // DOM Level 3
  155. 'buttons',
  156. // PointerEvent
  157. 'pointerId',
  158. 'width',
  159. 'height',
  160. 'pressure',
  161. 'tiltX',
  162. 'tiltY',
  163. 'pointerType',
  164. 'hwTimestamp',
  165. 'isPrimary',
  166. // event instance
  167. 'type',
  168. 'target',
  169. 'currentTarget',
  170. 'which',
  171. 'pageX',
  172. 'pageY',
  173. 'timeStamp'
  174. ];
  175. var CLONE_DEFAULTS = [
  176. // MouseEvent
  177. false,
  178. false,
  179. null,
  180. null,
  181. 0,
  182. 0,
  183. 0,
  184. 0,
  185. false,
  186. false,
  187. false,
  188. false,
  189. 0,
  190. null,
  191. // DOM Level 3
  192. 0,
  193. // PointerEvent
  194. 0,
  195. 0,
  196. 0,
  197. 0,
  198. 0,
  199. 0,
  200. '',
  201. 0,
  202. false,
  203. // event instance
  204. '',
  205. null,
  206. null,
  207. 0,
  208. 0,
  209. 0,
  210. 0
  211. ];
  212. var BOUNDARY_EVENTS = {
  213. 'pointerover': 1,
  214. 'pointerout': 1,
  215. 'pointerenter': 1,
  216. 'pointerleave': 1
  217. };
  218. var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
  219. /**
  220. * This module is for normalizing events. Mouse and Touch events will be
  221. * collected here, and fire PointerEvents that have the same semantics, no
  222. * matter the source.
  223. * Events fired:
  224. * - pointerdown: a pointing is added
  225. * - pointerup: a pointer is removed
  226. * - pointermove: a pointer is moved
  227. * - pointerover: a pointer crosses into an element
  228. * - pointerout: a pointer leaves an element
  229. * - pointercancel: a pointer will no longer generate events
  230. */
  231. var dispatcher = {
  232. pointermap: new PointerMap(),
  233. eventMap: Object.create(null),
  234. captureInfo: Object.create(null),
  235. // Scope objects for native events.
  236. // This exists for ease of testing.
  237. eventSources: Object.create(null),
  238. eventSourceList: [],
  239. /**
  240. * Add a new event source that will generate pointer events.
  241. *
  242. * `inSource` must contain an array of event names named `events`, and
  243. * functions with the names specified in the `events` array.
  244. * @param {string} name A name for the event source
  245. * @param {Object} source A new source of platform events.
  246. */
  247. registerSource: function(name, source) {
  248. var s = source;
  249. var newEvents = s.events;
  250. if (newEvents) {
  251. newEvents.forEach(function(e) {
  252. if (s[e]) {
  253. this.eventMap[e] = s[e].bind(s);
  254. }
  255. }, this);
  256. this.eventSources[name] = s;
  257. this.eventSourceList.push(s);
  258. }
  259. },
  260. register: function(element) {
  261. var l = this.eventSourceList.length;
  262. for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
  263. // call eventsource register
  264. es.register.call(es, element);
  265. }
  266. },
  267. unregister: function(element) {
  268. var l = this.eventSourceList.length;
  269. for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
  270. // call eventsource register
  271. es.unregister.call(es, element);
  272. }
  273. },
  274. contains: /*scope.external.contains || */function(container, contained) {
  275. try {
  276. return container.contains(contained);
  277. } catch (ex) {
  278. // most likely: https://bugzilla.mozilla.org/show_bug.cgi?id=208427
  279. return false;
  280. }
  281. },
  282. // EVENTS
  283. down: function(inEvent) {
  284. inEvent.bubbles = true;
  285. this.fireEvent('pointerdown', inEvent);
  286. },
  287. move: function(inEvent) {
  288. inEvent.bubbles = true;
  289. this.fireEvent('pointermove', inEvent);
  290. },
  291. up: function(inEvent) {
  292. inEvent.bubbles = true;
  293. this.fireEvent('pointerup', inEvent);
  294. },
  295. enter: function(inEvent) {
  296. inEvent.bubbles = false;
  297. this.fireEvent('pointerenter', inEvent);
  298. },
  299. leave: function(inEvent) {
  300. inEvent.bubbles = false;
  301. this.fireEvent('pointerleave', inEvent);
  302. },
  303. over: function(inEvent) {
  304. inEvent.bubbles = true;
  305. this.fireEvent('pointerover', inEvent);
  306. },
  307. out: function(inEvent) {
  308. inEvent.bubbles = true;
  309. this.fireEvent('pointerout', inEvent);
  310. },
  311. cancel: function(inEvent) {
  312. inEvent.bubbles = true;
  313. this.fireEvent('pointercancel', inEvent);
  314. },
  315. leaveOut: function(event) {
  316. this.out(event);
  317. this.propagate(event, this.leave, false);
  318. },
  319. enterOver: function(event) {
  320. this.over(event);
  321. this.propagate(event, this.enter, true);
  322. },
  323. // LISTENER LOGIC
  324. eventHandler: function(inEvent) {
  325. // This is used to prevent multiple dispatch of pointerevents from
  326. // platform events. This can happen when two elements in different scopes
  327. // are set up to create pointer events, which is relevant to Shadow DOM.
  328. if (inEvent._handledByPE) {
  329. return;
  330. }
  331. var type = inEvent.type;
  332. var fn = this.eventMap && this.eventMap[type];
  333. if (fn) {
  334. fn(inEvent);
  335. }
  336. inEvent._handledByPE = true;
  337. },
  338. // set up event listeners
  339. listen: function(target, events) {
  340. events.forEach(function(e) {
  341. this.addEvent(target, e);
  342. }, this);
  343. },
  344. // remove event listeners
  345. unlisten: function(target, events) {
  346. events.forEach(function(e) {
  347. this.removeEvent(target, e);
  348. }, this);
  349. },
  350. addEvent: /*scope.external.addEvent || */function(target, eventName) {
  351. target.addEventListener(eventName, this.boundHandler);
  352. },
  353. removeEvent: /*scope.external.removeEvent || */function(target, eventName) {
  354. target.removeEventListener(eventName, this.boundHandler);
  355. },
  356. // EVENT CREATION AND TRACKING
  357. /**
  358. * Creates a new Event of type `inType`, based on the information in
  359. * `inEvent`.
  360. *
  361. * @param {string} inType A string representing the type of event to create
  362. * @param {Event} inEvent A platform event with a target
  363. * @return {Event} A PointerEvent of type `inType`
  364. */
  365. makeEvent: function(inType, inEvent) {
  366. // relatedTarget must be null if pointer is captured
  367. if (this.captureInfo[inEvent.pointerId]) {
  368. inEvent.relatedTarget = null;
  369. }
  370. var e = new PointerEvent(inType, inEvent);
  371. if (inEvent.preventDefault) {
  372. e.preventDefault = inEvent.preventDefault;
  373. }
  374. e._target = e._target || inEvent.target;
  375. return e;
  376. },
  377. // make and dispatch an event in one call
  378. fireEvent: function(inType, inEvent) {
  379. var e = this.makeEvent(inType, inEvent);
  380. return this.dispatchEvent(e);
  381. },
  382. /**
  383. * Returns a snapshot of inEvent, with writable properties.
  384. *
  385. * @param {Event} inEvent An event that contains properties to copy.
  386. * @return {Object} An object containing shallow copies of `inEvent`'s
  387. * properties.
  388. */
  389. cloneEvent: function(inEvent) {
  390. var eventCopy = Object.create(null);
  391. var p;
  392. for (var i = 0; i < CLONE_PROPS.length; i++) {
  393. p = CLONE_PROPS[i];
  394. eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
  395. // Work around SVGInstanceElement shadow tree
  396. // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
  397. // This is the behavior implemented by Firefox.
  398. if (HAS_SVG_INSTANCE && (p === 'target' || p === 'relatedTarget')) {
  399. if (eventCopy[p] instanceof SVGElementInstance) {
  400. eventCopy[p] = eventCopy[p].correspondingUseElement;
  401. }
  402. }
  403. }
  404. // keep the semantics of preventDefault
  405. if (inEvent.preventDefault) {
  406. eventCopy.preventDefault = function() {
  407. inEvent.preventDefault();
  408. };
  409. }
  410. return eventCopy;
  411. },
  412. getTarget: function(inEvent) {
  413. var capture = this.captureInfo[inEvent.pointerId];
  414. if (!capture) {
  415. return inEvent._target;
  416. }
  417. if (inEvent._target === capture || !(inEvent.type in BOUNDARY_EVENTS)) {
  418. return capture;
  419. }
  420. },
  421. propagate: function(event, fn, propagateDown) {
  422. var target = event.target;
  423. var targets = [];
  424. // Order of conditions due to document.contains() missing in IE.
  425. while (target !== document && !target.contains(event.relatedTarget)) {
  426. targets.push(target);
  427. target = target.parentNode;
  428. // Touch: Do not propagate if node is detached.
  429. if (!target) {
  430. return;
  431. }
  432. }
  433. if (propagateDown) {
  434. targets.reverse();
  435. }
  436. targets.forEach(function(target) {
  437. event.target = target;
  438. fn.call(this, event);
  439. }, this);
  440. },
  441. setCapture: function(inPointerId, inTarget, skipDispatch) {
  442. if (this.captureInfo[inPointerId]) {
  443. this.releaseCapture(inPointerId, skipDispatch);
  444. }
  445. this.captureInfo[inPointerId] = inTarget;
  446. this.implicitRelease = this.releaseCapture.bind(this, inPointerId, skipDispatch);
  447. document.addEventListener('pointerup', this.implicitRelease);
  448. document.addEventListener('pointercancel', this.implicitRelease);
  449. var e = new PointerEvent('gotpointercapture');
  450. e.pointerId = inPointerId;
  451. e._target = inTarget;
  452. if (!skipDispatch) {
  453. this.asyncDispatchEvent(e);
  454. }
  455. },
  456. releaseCapture: function(inPointerId, skipDispatch) {
  457. var t = this.captureInfo[inPointerId];
  458. if (!t) {
  459. return;
  460. }
  461. this.captureInfo[inPointerId] = undefined;
  462. document.removeEventListener('pointerup', this.implicitRelease);
  463. document.removeEventListener('pointercancel', this.implicitRelease);
  464. var e = new PointerEvent('lostpointercapture');
  465. e.pointerId = inPointerId;
  466. e._target = t;
  467. if (!skipDispatch) {
  468. this.asyncDispatchEvent(e);
  469. }
  470. },
  471. /**
  472. * Dispatches the event to its target.
  473. *
  474. * @param {Event} inEvent The event to be dispatched.
  475. * @return {Boolean} True if an event handler returns true, false otherwise.
  476. */
  477. dispatchEvent: /*scope.external.dispatchEvent || */function(inEvent) {
  478. var t = this.getTarget(inEvent);
  479. if (t) {
  480. return t.dispatchEvent(inEvent);
  481. }
  482. },
  483. asyncDispatchEvent: function(inEvent) {
  484. requestAnimationFrame(this.dispatchEvent.bind(this, inEvent));
  485. }
  486. };
  487. dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
  488. var targeting = {
  489. shadow: function(inEl) {
  490. if (inEl) {
  491. return inEl.shadowRoot || inEl.webkitShadowRoot;
  492. }
  493. },
  494. canTarget: function(shadow) {
  495. return shadow && Boolean(shadow.elementFromPoint);
  496. },
  497. targetingShadow: function(inEl) {
  498. var s = this.shadow(inEl);
  499. if (this.canTarget(s)) {
  500. return s;
  501. }
  502. },
  503. olderShadow: function(shadow) {
  504. var os = shadow.olderShadowRoot;
  505. if (!os) {
  506. var se = shadow.querySelector('shadow');
  507. if (se) {
  508. os = se.olderShadowRoot;
  509. }
  510. }
  511. return os;
  512. },
  513. allShadows: function(element) {
  514. var shadows = [];
  515. var s = this.shadow(element);
  516. while (s) {
  517. shadows.push(s);
  518. s = this.olderShadow(s);
  519. }
  520. return shadows;
  521. },
  522. searchRoot: function(inRoot, x, y) {
  523. if (inRoot) {
  524. var t = inRoot.elementFromPoint(x, y);
  525. var st, sr;
  526. // is element a shadow host?
  527. sr = this.targetingShadow(t);
  528. while (sr) {
  529. // find the the element inside the shadow root
  530. st = sr.elementFromPoint(x, y);
  531. if (!st) {
  532. // check for older shadows
  533. sr = this.olderShadow(sr);
  534. } else {
  535. // shadowed element may contain a shadow root
  536. var ssr = this.targetingShadow(st);
  537. return this.searchRoot(ssr, x, y) || st;
  538. }
  539. }
  540. // light dom element is the target
  541. return t;
  542. }
  543. },
  544. owner: function(element) {
  545. var s = element;
  546. // walk up until you hit the shadow root or document
  547. while (s.parentNode) {
  548. s = s.parentNode;
  549. }
  550. // the owner element is expected to be a Document or ShadowRoot
  551. if (s.nodeType !== Node.DOCUMENT_NODE && s.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
  552. s = document;
  553. }
  554. return s;
  555. },
  556. findTarget: function(inEvent) {
  557. var x = inEvent.clientX;
  558. var y = inEvent.clientY;
  559. // if the listener is in the shadow root, it is much faster to start there
  560. var s = this.owner(inEvent.target);
  561. // if x, y is not in this root, fall back to document search
  562. if (!s.elementFromPoint(x, y)) {
  563. s = document;
  564. }
  565. return this.searchRoot(s, x, y);
  566. }
  567. };
  568. var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
  569. var map = Array.prototype.map.call.bind(Array.prototype.map);
  570. var toArray = Array.prototype.slice.call.bind(Array.prototype.slice);
  571. var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
  572. var MO = window.MutationObserver || window.WebKitMutationObserver;
  573. var SELECTOR = '[touch-action]';
  574. var OBSERVER_INIT = {
  575. subtree: true,
  576. childList: true,
  577. attributes: true,
  578. attributeOldValue: true,
  579. attributeFilter: ['touch-action']
  580. };
  581. function Installer(add, remove, changed, binder) {
  582. this.addCallback = add.bind(binder);
  583. this.removeCallback = remove.bind(binder);
  584. this.changedCallback = changed.bind(binder);
  585. if (MO) {
  586. this.observer = new MO(this.mutationWatcher.bind(this));
  587. }
  588. }
  589. Installer.prototype = {
  590. watchSubtree: function(target) {
  591. // Only watch scopes that can target find, as these are top-level.
  592. // Otherwise we can see duplicate additions and removals that add noise.
  593. //
  594. // TODO(dfreedman): For some instances with ShadowDOMPolyfill, we can see
  595. // a removal without an insertion when a node is redistributed among
  596. // shadows. Since it all ends up correct in the document, watching only
  597. // the document will yield the correct mutations to watch.
  598. if (this.observer && targeting.canTarget(target)) {
  599. this.observer.observe(target, OBSERVER_INIT);
  600. }
  601. },
  602. enableOnSubtree: function(target) {
  603. this.watchSubtree(target);
  604. if (target === document && document.readyState !== 'complete') {
  605. this.installOnLoad();
  606. } else {
  607. this.installNewSubtree(target);
  608. }
  609. },
  610. installNewSubtree: function(target) {
  611. forEach(this.findElements(target), this.addElement, this);
  612. },
  613. findElements: function(target) {
  614. if (target.querySelectorAll) {
  615. return target.querySelectorAll(SELECTOR);
  616. }
  617. return [];
  618. },
  619. removeElement: function(el) {
  620. this.removeCallback(el);
  621. },
  622. addElement: function(el) {
  623. this.addCallback(el);
  624. },
  625. elementChanged: function(el, oldValue) {
  626. this.changedCallback(el, oldValue);
  627. },
  628. concatLists: function(accum, list) {
  629. return accum.concat(toArray(list));
  630. },
  631. // register all touch-action = none nodes on document load
  632. installOnLoad: function() {
  633. document.addEventListener('readystatechange', function() {
  634. if (document.readyState === 'complete') {
  635. this.installNewSubtree(document);
  636. }
  637. }.bind(this));
  638. },
  639. isElement: function(n) {
  640. return n.nodeType === Node.ELEMENT_NODE;
  641. },
  642. flattenMutationTree: function(inNodes) {
  643. // find children with touch-action
  644. var tree = map(inNodes, this.findElements, this);
  645. // make sure the added nodes are accounted for
  646. tree.push(filter(inNodes, this.isElement));
  647. // flatten the list
  648. return tree.reduce(this.concatLists, []);
  649. },
  650. mutationWatcher: function(mutations) {
  651. mutations.forEach(this.mutationHandler, this);
  652. },
  653. mutationHandler: function(m) {
  654. if (m.type === 'childList') {
  655. var added = this.flattenMutationTree(m.addedNodes);
  656. added.forEach(this.addElement, this);
  657. var removed = this.flattenMutationTree(m.removedNodes);
  658. removed.forEach(this.removeElement, this);
  659. } else if (m.type === 'attributes') {
  660. this.elementChanged(m.target, m.oldValue);
  661. }
  662. }
  663. };
  664. function shadowSelector(v) {
  665. return 'body /shadow-deep/ ' + selector(v);
  666. }
  667. function selector(v) {
  668. return '[touch-action="' + v + '"]';
  669. }
  670. function rule(v) {
  671. return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + '; }';
  672. }
  673. var attrib2css = [
  674. 'none',
  675. 'auto',
  676. 'pan-x',
  677. 'pan-y',
  678. {
  679. rule: 'pan-x pan-y',
  680. selectors: [
  681. 'pan-x pan-y',
  682. 'pan-y pan-x'
  683. ]
  684. }
  685. ];
  686. var styles = '';
  687. // only install stylesheet if the browser has touch action support
  688. var hasNativePE = window.PointerEvent || window.MSPointerEvent;
  689. // only add shadow selectors if shadowdom is supported
  690. var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
  691. function applyAttributeStyles() {
  692. if (hasNativePE) {
  693. attrib2css.forEach(function(r) {
  694. if (String(r) === r) {
  695. styles += selector(r) + rule(r) + '\n';
  696. if (hasShadowRoot) {
  697. styles += shadowSelector(r) + rule(r) + '\n';
  698. }
  699. } else {
  700. styles += r.selectors.map(selector) + rule(r.rule) + '\n';
  701. if (hasShadowRoot) {
  702. styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
  703. }
  704. }
  705. });
  706. var el = document.createElement('style');
  707. el.textContent = styles;
  708. document.head.appendChild(el);
  709. }
  710. }
  711. var pointermap = dispatcher.pointermap;
  712. // radius around touchend that swallows mouse events
  713. var DEDUP_DIST = 25;
  714. // left, middle, right, back, forward
  715. var BUTTON_TO_BUTTONS = [1, 4, 2, 8, 16];
  716. var HAS_BUTTONS = false;
  717. try {
  718. HAS_BUTTONS = new MouseEvent('test', { buttons: 1 }).buttons === 1;
  719. } catch (e) {}
  720. // handler block for native mouse events
  721. var mouseEvents = {
  722. POINTER_ID: 1,
  723. POINTER_TYPE: 'mouse',
  724. events: [
  725. 'mousedown',
  726. 'mousemove',
  727. 'mouseup',
  728. 'mouseover',
  729. 'mouseout'
  730. ],
  731. register: function(target) {
  732. dispatcher.listen(target, this.events);
  733. },
  734. unregister: function(target) {
  735. dispatcher.unlisten(target, this.events);
  736. },
  737. lastTouches: [],
  738. // collide with the global mouse listener
  739. isEventSimulatedFromTouch: function(inEvent) {
  740. var lts = this.lastTouches;
  741. var x = inEvent.clientX;
  742. var y = inEvent.clientY;
  743. for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
  744. // simulated mouse events will be swallowed near a primary touchend
  745. var dx = Math.abs(x - t.x);
  746. var dy = Math.abs(y - t.y);
  747. if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
  748. return true;
  749. }
  750. }
  751. },
  752. prepareEvent: function(inEvent) {
  753. var e = dispatcher.cloneEvent(inEvent);
  754. // forward mouse preventDefault
  755. var pd = e.preventDefault;
  756. e.preventDefault = function() {
  757. inEvent.preventDefault();
  758. pd();
  759. };
  760. e.pointerId = this.POINTER_ID;
  761. e.isPrimary = true;
  762. e.pointerType = this.POINTER_TYPE;
  763. return e;
  764. },
  765. prepareButtonsForMove: function(e, inEvent) {
  766. var p = pointermap.get(this.POINTER_ID);
  767. // Update buttons state after possible out-of-document mouseup.
  768. if (inEvent.which === 0 || !p) {
  769. e.buttons = 0;
  770. } else {
  771. e.buttons = p.buttons;
  772. }
  773. inEvent.buttons = e.buttons;
  774. },
  775. mousedown: function(inEvent) {
  776. if (!this.isEventSimulatedFromTouch(inEvent)) {
  777. var p = pointermap.get(this.POINTER_ID);
  778. var e = this.prepareEvent(inEvent);
  779. if (!HAS_BUTTONS) {
  780. e.buttons = BUTTON_TO_BUTTONS[e.button];
  781. if (p) { e.buttons |= p.buttons; }
  782. inEvent.buttons = e.buttons;
  783. }
  784. pointermap.set(this.POINTER_ID, inEvent);
  785. if (!p || p.buttons === 0) {
  786. dispatcher.down(e);
  787. } else {
  788. dispatcher.move(e);
  789. }
  790. }
  791. },
  792. mousemove: function(inEvent) {
  793. if (!this.isEventSimulatedFromTouch(inEvent)) {
  794. var e = this.prepareEvent(inEvent);
  795. if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }
  796. e.button = -1;
  797. pointermap.set(this.POINTER_ID, inEvent);
  798. dispatcher.move(e);
  799. }
  800. },
  801. mouseup: function(inEvent) {
  802. if (!this.isEventSimulatedFromTouch(inEvent)) {
  803. var p = pointermap.get(this.POINTER_ID);
  804. var e = this.prepareEvent(inEvent);
  805. if (!HAS_BUTTONS) {
  806. var up = BUTTON_TO_BUTTONS[e.button];
  807. // Produces wrong state of buttons in Browsers without `buttons` support
  808. // when a mouse button that was pressed outside the document is released
  809. // inside and other buttons are still pressed down.
  810. e.buttons = p ? p.buttons & ~up : 0;
  811. inEvent.buttons = e.buttons;
  812. }
  813. pointermap.set(this.POINTER_ID, inEvent);
  814. // Support: Firefox <=44 only
  815. // FF Ubuntu includes the lifted button in the `buttons` property on
  816. // mouseup.
  817. // https://bugzilla.mozilla.org/show_bug.cgi?id=1223366
  818. e.buttons &= ~BUTTON_TO_BUTTONS[e.button];
  819. if (e.buttons === 0) {
  820. dispatcher.up(e);
  821. } else {
  822. dispatcher.move(e);
  823. }
  824. }
  825. },
  826. mouseover: function(inEvent) {
  827. if (!this.isEventSimulatedFromTouch(inEvent)) {
  828. var e = this.prepareEvent(inEvent);
  829. if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }
  830. e.button = -1;
  831. pointermap.set(this.POINTER_ID, inEvent);
  832. dispatcher.enterOver(e);
  833. }
  834. },
  835. mouseout: function(inEvent) {
  836. if (!this.isEventSimulatedFromTouch(inEvent)) {
  837. var e = this.prepareEvent(inEvent);
  838. if (!HAS_BUTTONS) { this.prepareButtonsForMove(e, inEvent); }
  839. e.button = -1;
  840. dispatcher.leaveOut(e);
  841. }
  842. },
  843. cancel: function(inEvent) {
  844. var e = this.prepareEvent(inEvent);
  845. dispatcher.cancel(e);
  846. this.deactivateMouse();
  847. },
  848. deactivateMouse: function() {
  849. pointermap.delete(this.POINTER_ID);
  850. }
  851. };
  852. var captureInfo = dispatcher.captureInfo;
  853. var findTarget = targeting.findTarget.bind(targeting);
  854. var allShadows = targeting.allShadows.bind(targeting);
  855. var pointermap$1 = dispatcher.pointermap;
  856. // This should be long enough to ignore compat mouse events made by touch
  857. var DEDUP_TIMEOUT = 2500;
  858. var CLICK_COUNT_TIMEOUT = 200;
  859. var ATTRIB = 'touch-action';
  860. var INSTALLER;
  861. // handler block for native touch events
  862. var touchEvents = {
  863. events: [
  864. 'touchstart',
  865. 'touchmove',
  866. 'touchend',
  867. 'touchcancel'
  868. ],
  869. register: function(target) {
  870. INSTALLER.enableOnSubtree(target);
  871. },
  872. unregister: function() {
  873. // TODO(dfreedman): is it worth it to disconnect the MO?
  874. },
  875. elementAdded: function(el) {
  876. var a = el.getAttribute(ATTRIB);
  877. var st = this.touchActionToScrollType(a);
  878. if (st) {
  879. el._scrollType = st;
  880. dispatcher.listen(el, this.events);
  881. // set touch-action on shadows as well
  882. allShadows(el).forEach(function(s) {
  883. s._scrollType = st;
  884. dispatcher.listen(s, this.events);
  885. }, this);
  886. }
  887. },
  888. elementRemoved: function(el) {
  889. el._scrollType = undefined;
  890. dispatcher.unlisten(el, this.events);
  891. // remove touch-action from shadow
  892. allShadows(el).forEach(function(s) {
  893. s._scrollType = undefined;
  894. dispatcher.unlisten(s, this.events);
  895. }, this);
  896. },
  897. elementChanged: function(el, oldValue) {
  898. var a = el.getAttribute(ATTRIB);
  899. var st = this.touchActionToScrollType(a);
  900. var oldSt = this.touchActionToScrollType(oldValue);
  901. // simply update scrollType if listeners are already established
  902. if (st && oldSt) {
  903. el._scrollType = st;
  904. allShadows(el).forEach(function(s) {
  905. s._scrollType = st;
  906. }, this);
  907. } else if (oldSt) {
  908. this.elementRemoved(el);
  909. } else if (st) {
  910. this.elementAdded(el);
  911. }
  912. },
  913. scrollTypes: {
  914. EMITTER: 'none',
  915. XSCROLLER: 'pan-x',
  916. YSCROLLER: 'pan-y',
  917. SCROLLER: /^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/
  918. },
  919. touchActionToScrollType: function(touchAction) {
  920. var t = touchAction;
  921. var st = this.scrollTypes;
  922. if (t === 'none') {
  923. return 'none';
  924. } else if (t === st.XSCROLLER) {
  925. return 'X';
  926. } else if (t === st.YSCROLLER) {
  927. return 'Y';
  928. } else if (st.SCROLLER.exec(t)) {
  929. return 'XY';
  930. }
  931. },
  932. POINTER_TYPE: 'touch',
  933. firstTouch: null,
  934. isPrimaryTouch: function(inTouch) {
  935. return this.firstTouch === inTouch.identifier;
  936. },
  937. setPrimaryTouch: function(inTouch) {
  938. // set primary touch if there no pointers, or the only pointer is the mouse
  939. if (pointermap$1.size === 0 || (pointermap$1.size === 1 && pointermap$1.has(1))) {
  940. this.firstTouch = inTouch.identifier;
  941. this.firstXY = { X: inTouch.clientX, Y: inTouch.clientY };
  942. this.scrolling = false;
  943. this.cancelResetClickCount();
  944. }
  945. },
  946. removePrimaryPointer: function(inPointer) {
  947. if (inPointer.isPrimary) {
  948. this.firstTouch = null;
  949. this.firstXY = null;
  950. this.resetClickCount();
  951. }
  952. },
  953. clickCount: 0,
  954. resetId: null,
  955. resetClickCount: function() {
  956. var fn = function() {
  957. this.clickCount = 0;
  958. this.resetId = null;
  959. }.bind(this);
  960. this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
  961. },
  962. cancelResetClickCount: function() {
  963. if (this.resetId) {
  964. clearTimeout(this.resetId);
  965. }
  966. },
  967. typeToButtons: function(type) {
  968. var ret = 0;
  969. if (type === 'touchstart' || type === 'touchmove') {
  970. ret = 1;
  971. }
  972. return ret;
  973. },
  974. touchToPointer: function(inTouch) {
  975. var cte = this.currentTouchEvent;
  976. var e = dispatcher.cloneEvent(inTouch);
  977. // We reserve pointerId 1 for Mouse.
  978. // Touch identifiers can start at 0.
  979. // Add 2 to the touch identifier for compatibility.
  980. var id = e.pointerId = inTouch.identifier + 2;
  981. e.target = captureInfo[id] || findTarget(e);
  982. e.bubbles = true;
  983. e.cancelable = true;
  984. e.detail = this.clickCount;
  985. e.button = 0;
  986. e.buttons = this.typeToButtons(cte.type);
  987. e.width = (inTouch.radiusX || inTouch.webkitRadiusX || 0) * 2;
  988. e.height = (inTouch.radiusY || inTouch.webkitRadiusY || 0) * 2;
  989. e.pressure = inTouch.force || inTouch.webkitForce || 0.5;
  990. e.isPrimary = this.isPrimaryTouch(inTouch);
  991. e.pointerType = this.POINTER_TYPE;
  992. // forward modifier keys
  993. e.altKey = cte.altKey;
  994. e.ctrlKey = cte.ctrlKey;
  995. e.metaKey = cte.metaKey;
  996. e.shiftKey = cte.shiftKey;
  997. // forward touch preventDefaults
  998. var self = this;
  999. e.preventDefault = function() {
  1000. self.scrolling = false;
  1001. self.firstXY = null;
  1002. cte.preventDefault();
  1003. };
  1004. return e;
  1005. },
  1006. processTouches: function(inEvent, inFunction) {
  1007. var tl = inEvent.changedTouches;
  1008. this.currentTouchEvent = inEvent;
  1009. for (var i = 0, t; i < tl.length; i++) {
  1010. t = tl[i];
  1011. inFunction.call(this, this.touchToPointer(t));
  1012. }
  1013. },
  1014. // For single axis scrollers, determines whether the element should emit
  1015. // pointer events or behave as a scroller
  1016. shouldScroll: function(inEvent) {
  1017. if (this.firstXY) {
  1018. var ret;
  1019. var scrollAxis = inEvent.currentTarget._scrollType;
  1020. if (scrollAxis === 'none') {
  1021. // this element is a touch-action: none, should never scroll
  1022. ret = false;
  1023. } else if (scrollAxis === 'XY') {
  1024. // this element should always scroll
  1025. ret = true;
  1026. } else {
  1027. var t = inEvent.changedTouches[0];
  1028. // check the intended scroll axis, and other axis
  1029. var a = scrollAxis;
  1030. var oa = scrollAxis === 'Y' ? 'X' : 'Y';
  1031. var da = Math.abs(t['client' + a] - this.firstXY[a]);
  1032. var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
  1033. // if delta in the scroll axis > delta other axis, scroll instead of
  1034. // making events
  1035. ret = da >= doa;
  1036. }
  1037. this.firstXY = null;
  1038. return ret;
  1039. }
  1040. },
  1041. findTouch: function(inTL, inId) {
  1042. for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
  1043. if (t.identifier === inId) {
  1044. return true;
  1045. }
  1046. }
  1047. },
  1048. // In some instances, a touchstart can happen without a touchend. This
  1049. // leaves the pointermap in a broken state.
  1050. // Therefore, on every touchstart, we remove the touches that did not fire a
  1051. // touchend event.
  1052. // To keep state globally consistent, we fire a
  1053. // pointercancel for this "abandoned" touch
  1054. vacuumTouches: function(inEvent) {
  1055. var tl = inEvent.touches;
  1056. // pointermap.size should be < tl.length here, as the touchstart has not
  1057. // been processed yet.
  1058. if (pointermap$1.size >= tl.length) {
  1059. var d = [];
  1060. pointermap$1.forEach(function(value, key) {
  1061. // Never remove pointerId == 1, which is mouse.
  1062. // Touch identifiers are 2 smaller than their pointerId, which is the
  1063. // index in pointermap.
  1064. if (key !== 1 && !this.findTouch(tl, key - 2)) {
  1065. var p = value.out;
  1066. d.push(p);
  1067. }
  1068. }, this);
  1069. d.forEach(this.cancelOut, this);
  1070. }
  1071. },
  1072. touchstart: function(inEvent) {
  1073. this.vacuumTouches(inEvent);
  1074. this.setPrimaryTouch(inEvent.changedTouches[0]);
  1075. this.dedupSynthMouse(inEvent);
  1076. if (!this.scrolling) {
  1077. this.clickCount++;
  1078. this.processTouches(inEvent, this.overDown);
  1079. }
  1080. },
  1081. overDown: function(inPointer) {
  1082. pointermap$1.set(inPointer.pointerId, {
  1083. target: inPointer.target,
  1084. out: inPointer,
  1085. outTarget: inPointer.target
  1086. });
  1087. dispatcher.enterOver(inPointer);
  1088. dispatcher.down(inPointer);
  1089. },
  1090. touchmove: function(inEvent) {
  1091. if (!this.scrolling) {
  1092. if (this.shouldScroll(inEvent)) {
  1093. this.scrolling = true;
  1094. this.touchcancel(inEvent);
  1095. } else {
  1096. inEvent.preventDefault();
  1097. this.processTouches(inEvent, this.moveOverOut);
  1098. }
  1099. }
  1100. },
  1101. moveOverOut: function(inPointer) {
  1102. var event = inPointer;
  1103. var pointer = pointermap$1.get(event.pointerId);
  1104. // a finger drifted off the screen, ignore it
  1105. if (!pointer) {
  1106. return;
  1107. }
  1108. var outEvent = pointer.out;
  1109. var outTarget = pointer.outTarget;
  1110. dispatcher.move(event);
  1111. if (outEvent && outTarget !== event.target) {
  1112. outEvent.relatedTarget = event.target;
  1113. event.relatedTarget = outTarget;
  1114. // recover from retargeting by shadow
  1115. outEvent.target = outTarget;
  1116. if (event.target) {
  1117. dispatcher.leaveOut(outEvent);
  1118. dispatcher.enterOver(event);
  1119. } else {
  1120. // clean up case when finger leaves the screen
  1121. event.target = outTarget;
  1122. event.relatedTarget = null;
  1123. this.cancelOut(event);
  1124. }
  1125. }
  1126. pointer.out = event;
  1127. pointer.outTarget = event.target;
  1128. },
  1129. touchend: function(inEvent) {
  1130. this.dedupSynthMouse(inEvent);
  1131. this.processTouches(inEvent, this.upOut);
  1132. },
  1133. upOut: function(inPointer) {
  1134. if (!this.scrolling) {
  1135. dispatcher.up(inPointer);
  1136. dispatcher.leaveOut(inPointer);
  1137. }
  1138. this.cleanUpPointer(inPointer);
  1139. },
  1140. touchcancel: function(inEvent) {
  1141. this.processTouches(inEvent, this.cancelOut);
  1142. },
  1143. cancelOut: function(inPointer) {
  1144. dispatcher.cancel(inPointer);
  1145. dispatcher.leaveOut(inPointer);
  1146. this.cleanUpPointer(inPointer);
  1147. },
  1148. cleanUpPointer: function(inPointer) {
  1149. pointermap$1.delete(inPointer.pointerId);
  1150. this.removePrimaryPointer(inPointer);
  1151. },
  1152. // prevent synth mouse events from creating pointer events
  1153. dedupSynthMouse: function(inEvent) {
  1154. var lts = mouseEvents.lastTouches;
  1155. var t = inEvent.changedTouches[0];
  1156. // only the primary finger will synth mouse events
  1157. if (this.isPrimaryTouch(t)) {
  1158. // remember x/y of last touch
  1159. var lt = { x: t.clientX, y: t.clientY };
  1160. lts.push(lt);
  1161. var fn = (function(lts, lt) {
  1162. var i = lts.indexOf(lt);
  1163. if (i > -1) {
  1164. lts.splice(i, 1);
  1165. }
  1166. }).bind(null, lts, lt);
  1167. setTimeout(fn, DEDUP_TIMEOUT);
  1168. }
  1169. }
  1170. };
  1171. INSTALLER = new Installer(touchEvents.elementAdded, touchEvents.elementRemoved,
  1172. touchEvents.elementChanged, touchEvents);
  1173. var pointermap$2 = dispatcher.pointermap;
  1174. var HAS_BITMAP_TYPE = window.MSPointerEvent &&
  1175. typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
  1176. var msEvents = {
  1177. events: [
  1178. 'MSPointerDown',
  1179. 'MSPointerMove',
  1180. 'MSPointerUp',
  1181. 'MSPointerOut',
  1182. 'MSPointerOver',
  1183. 'MSPointerCancel',
  1184. 'MSGotPointerCapture',
  1185. 'MSLostPointerCapture'
  1186. ],
  1187. register: function(target) {
  1188. dispatcher.listen(target, this.events);
  1189. },
  1190. unregister: function(target) {
  1191. dispatcher.unlisten(target, this.events);
  1192. },
  1193. POINTER_TYPES: [
  1194. '',
  1195. 'unavailable',
  1196. 'touch',
  1197. 'pen',
  1198. 'mouse'
  1199. ],
  1200. prepareEvent: function(inEvent) {
  1201. var e = inEvent;
  1202. if (HAS_BITMAP_TYPE) {
  1203. e = dispatcher.cloneEvent(inEvent);
  1204. e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
  1205. }
  1206. return e;
  1207. },
  1208. cleanup: function(id) {
  1209. pointermap$2.delete(id);
  1210. },
  1211. MSPointerDown: function(inEvent) {
  1212. pointermap$2.set(inEvent.pointerId, inEvent);
  1213. var e = this.prepareEvent(inEvent);
  1214. dispatcher.down(e);
  1215. },
  1216. MSPointerMove: function(inEvent) {
  1217. var e = this.prepareEvent(inEvent);
  1218. dispatcher.move(e);
  1219. },
  1220. MSPointerUp: function(inEvent) {
  1221. var e = this.prepareEvent(inEvent);
  1222. dispatcher.up(e);
  1223. this.cleanup(inEvent.pointerId);
  1224. },
  1225. MSPointerOut: function(inEvent) {
  1226. var e = this.prepareEvent(inEvent);
  1227. dispatcher.leaveOut(e);
  1228. },
  1229. MSPointerOver: function(inEvent) {
  1230. var e = this.prepareEvent(inEvent);
  1231. dispatcher.enterOver(e);
  1232. },
  1233. MSPointerCancel: function(inEvent) {
  1234. var e = this.prepareEvent(inEvent);
  1235. dispatcher.cancel(e);
  1236. this.cleanup(inEvent.pointerId);
  1237. },
  1238. MSLostPointerCapture: function(inEvent) {
  1239. var e = dispatcher.makeEvent('lostpointercapture', inEvent);
  1240. dispatcher.dispatchEvent(e);
  1241. },
  1242. MSGotPointerCapture: function(inEvent) {
  1243. var e = dispatcher.makeEvent('gotpointercapture', inEvent);
  1244. dispatcher.dispatchEvent(e);
  1245. }
  1246. };
  1247. function applyPolyfill() {
  1248. // only activate if this platform does not have pointer events
  1249. if (!window.PointerEvent) {
  1250. window.PointerEvent = PointerEvent;
  1251. if (window.navigator.msPointerEnabled) {
  1252. var tp = window.navigator.msMaxTouchPoints;
  1253. Object.defineProperty(window.navigator, 'maxTouchPoints', {
  1254. value: tp,
  1255. enumerable: true
  1256. });
  1257. dispatcher.registerSource('ms', msEvents);
  1258. } else {
  1259. Object.defineProperty(window.navigator, 'maxTouchPoints', {
  1260. value: 0,
  1261. enumerable: true
  1262. });
  1263. dispatcher.registerSource('mouse', mouseEvents);
  1264. if (window.ontouchstart !== undefined) {
  1265. dispatcher.registerSource('touch', touchEvents);
  1266. }
  1267. }
  1268. dispatcher.register(document);
  1269. }
  1270. }
  1271. var n = window.navigator;
  1272. var s;
  1273. var r;
  1274. var h;
  1275. function assertActive(id) {
  1276. if (!dispatcher.pointermap.has(id)) {
  1277. var error = new Error('InvalidPointerId');
  1278. error.name = 'InvalidPointerId';
  1279. throw error;
  1280. }
  1281. }
  1282. function assertConnected(elem) {
  1283. var parent = elem.parentNode;
  1284. while (parent && parent !== elem.ownerDocument) {
  1285. parent = parent.parentNode;
  1286. }
  1287. if (!parent) {
  1288. var error = new Error('InvalidStateError');
  1289. error.name = 'InvalidStateError';
  1290. throw error;
  1291. }
  1292. }
  1293. function inActiveButtonState(id) {
  1294. var p = dispatcher.pointermap.get(id);
  1295. return p.buttons !== 0;
  1296. }
  1297. if (n.msPointerEnabled) {
  1298. s = function(pointerId) {
  1299. assertActive(pointerId);
  1300. assertConnected(this);
  1301. if (inActiveButtonState(pointerId)) {
  1302. dispatcher.setCapture(pointerId, this, true);
  1303. this.msSetPointerCapture(pointerId);
  1304. }
  1305. };
  1306. r = function(pointerId) {
  1307. assertActive(pointerId);
  1308. dispatcher.releaseCapture(pointerId, true);
  1309. this.msReleasePointerCapture(pointerId);
  1310. };
  1311. } else {
  1312. s = function setPointerCapture(pointerId) {
  1313. assertActive(pointerId);
  1314. assertConnected(this);
  1315. if (inActiveButtonState(pointerId)) {
  1316. dispatcher.setCapture(pointerId, this);
  1317. }
  1318. };
  1319. r = function releasePointerCapture(pointerId) {
  1320. assertActive(pointerId);
  1321. dispatcher.releaseCapture(pointerId);
  1322. };
  1323. }
  1324. h = function hasPointerCapture(pointerId) {
  1325. return !!dispatcher.captureInfo[pointerId];
  1326. };
  1327. function applyPolyfill$1() {
  1328. if (window.Element && !Element.prototype.setPointerCapture) {
  1329. Object.defineProperties(Element.prototype, {
  1330. 'setPointerCapture': {
  1331. value: s
  1332. },
  1333. 'releasePointerCapture': {
  1334. value: r
  1335. },
  1336. 'hasPointerCapture': {
  1337. value: h
  1338. }
  1339. });
  1340. }
  1341. }
  1342. applyAttributeStyles();
  1343. applyPolyfill();
  1344. applyPolyfill$1();
  1345. var pointerevents = {
  1346. dispatcher: dispatcher,
  1347. Installer: Installer,
  1348. PointerEvent: PointerEvent,
  1349. PointerMap: PointerMap,
  1350. targetFinding: targeting
  1351. };
  1352. return pointerevents;
  1353. }));