TextSelectionNormalizer.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2015, 2017
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['../../../../lib/@waca/core-client/js/core-client/ui/core/Class', 'jquery'], function (Class, $) {
  8. // eslint-disable-next-line no-undef
  9. var MODIFIER_KEY = /^Mac/.test(navigator.platform) ? 'metaKey' : 'ctrlKey';
  10. var RANGE_PROPERTIES = ['startContainer', 'endContainer', 'startOffset', 'endOffset'];
  11. /**
  12. * This helper class tries to mimic the behaviour of the new |selectionchange| event in browsers
  13. * that currently do not support it (Firefox). Once all browsers support this event, we can toss
  14. * this class.
  15. */
  16. var TextSelectionNormalizer = Class.extend({
  17. lastRange: null,
  18. init: function init() {
  19. TextSelectionNormalizer.inherited('init', this, arguments);
  20. // Immediately bind the events.
  21. this.bindEvents();
  22. },
  23. destroy: function destroy() {
  24. this.unbindEvents();
  25. },
  26. /**
  27. * Bind to mouse and keyboard events if needed. If the browser already supports the |selectionchange| event
  28. * then do nothing.
  29. */
  30. bindEvents: function bindEvents() {
  31. if (!this.hasNativeEvent()) {
  32. this.lastRange = this.getSelectionRange();
  33. this.fnOnMouseDown = this.onMouseDown.bind(this);
  34. this.fnOnMouseMove = this.onMouseMove.bind(this);
  35. this.fnOnMouseUp = this.onMouseUp.bind(this);
  36. this.fnOnKeyPress = this.onKeyPress.bind(this);
  37. this.fnOnFocus = this.onFocus.bind(this);
  38. $(document).on('mousedown', this.fnOnMouseDown).on('mousemove', this.fnOnMouseMove).on('mouseup', this.fnOnMouseUp).on('keypress', this.fnOnKeyPress);
  39. $(document.defaultView).on('focus', this.fnOnFocus);
  40. }
  41. },
  42. /**
  43. * Unbinds all the events that were binded in bindEvents().
  44. */
  45. unbindEvents: function unbindEvents() {
  46. if (this.fnOnKeyPress) {
  47. $(document).off('mousedown', this.fnOnMouseDown).off('mousemove', this.fnOnMouseMove).off('mouseup', this.fnOnMouseUp).off('keypress', this.fnOnKeyPress);
  48. $(document.defaultView).off('focus', this.fnOnFocus);
  49. this.fnOnMouseDown = null;
  50. this.fnOnMouseMove = null;
  51. this.fnOnMouseUp = null;
  52. this.fnOnKeyPress = null;
  53. this.fnOnFocus = null;
  54. this.lastRange = null;
  55. }
  56. },
  57. /**
  58. * Determine if the browser supports the selectionchange event.
  59. */
  60. hasNativeEvent: function hasNativeEvent() {
  61. var handler = document.onselectionchange;
  62. if (handler !== undefined) {
  63. try {
  64. document.onselectionchange = 0;
  65. return document.onselectionchange === null;
  66. } catch (e) {
  67. console.error('ERROR: onSelectionChange event error.');
  68. } finally {
  69. document.onselectionchange = handler;
  70. }
  71. }
  72. return false;
  73. },
  74. getSelectionRange: function getSelectionRange() {
  75. var sel = document.getSelection();
  76. return sel.rangeCount ? sel.getRangeAt(0) : null;
  77. },
  78. onMouseDown: function onMouseDown(event) {
  79. var _this = this;
  80. var result = void 0;
  81. if (event.button === 0) {
  82. $(document).on('mousemove', this.fnOnMouseMove);
  83. result = new Promise(function (resolve, reject) {
  84. try {
  85. setTimeout(function () {
  86. _this.notifyIfNecessary();
  87. resolve();
  88. }, 0);
  89. } catch (error) {
  90. reject(error);
  91. }
  92. });
  93. } else {
  94. result = Promise.resolve();
  95. }
  96. return result;
  97. },
  98. onMouseMove: function onMouseMove(event) {
  99. if (event.buttons === 1) {
  100. this.notifyIfNecessary();
  101. } else {
  102. $(document).off('mousemove', this.fnOnMouseMove);
  103. }
  104. },
  105. onMouseUp: function onMouseUp(event) {
  106. var _this2 = this;
  107. var result = void 0;
  108. if (event.button === 0) {
  109. result = new Promise(function (resolve, reject) {
  110. try {
  111. setTimeout(function () {
  112. _this2.notifyIfNecessary();
  113. resolve();
  114. }, 0);
  115. } catch (error) {
  116. reject(error);
  117. }
  118. });
  119. } else {
  120. $(document).off('mousemove', this.fnOnMouseMove);
  121. result = Promise.resolve();
  122. }
  123. return result;
  124. },
  125. onKeyPress: function onKeyPress(event) {
  126. var _this3 = this;
  127. var code = event.keyCode;
  128. // Check for arrow keys, or [ctrl|cmd]-a
  129. var notify = code >= 37 && code <= 40 || event[MODIFIER_KEY] && !event.shiftKey && !event.altKey && event.key === 'a';
  130. var result = void 0;
  131. if (notify) {
  132. result = new Promise(function (resolve, reject) {
  133. try {
  134. setTimeout(function () {
  135. _this3.notifyIfNecessary();
  136. resolve();
  137. }, 0);
  138. } catch (error) {
  139. reject(error);
  140. }
  141. });
  142. } else {
  143. result = Promise.resolve();
  144. }
  145. return result;
  146. },
  147. onFocus: function onFocus() {
  148. var _this4 = this;
  149. return new Promise(function (resolve, reject) {
  150. try {
  151. setTimeout(function () {
  152. _this4.notifyIfNecessary();
  153. resolve();
  154. }, 0);
  155. } catch (error) {
  156. reject(error);
  157. }
  158. });
  159. },
  160. notifyIfNecessary: function notifyIfNecessary() {
  161. var rOld = this.lastRange;
  162. var rNew = this.getSelectionRange();
  163. if (!this.sameRange(rNew, rOld)) {
  164. this.lastRange = rNew;
  165. // eslint-disable-next-line no-undef
  166. setTimeout(document.dispatchEvent.bind(document, new Event('selectionchange')), 0);
  167. }
  168. },
  169. sameRange: function sameRange(r1, r2) {
  170. return r1 === r2 || r1 && r2 && RANGE_PROPERTIES.every(function (item) {
  171. return r1[item] === r2[item];
  172. });
  173. }
  174. });
  175. return TextSelectionNormalizer;
  176. });
  177. //# sourceMappingURL=TextSelectionNormalizer.js.map