events.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. const VE = {
  2. core: {
  3. /**
  4. * Main method that invokes further event processing
  5. * @param root is root HTML DOM element that. Pass HTML DOM Element or css selector
  6. * @param event_type (eg: click, mouseover etc..)
  7. */
  8. register: (root, event_type) => {
  9. root = !root ? 'body' : root; // if elm is not passed just bind events to body DOM Element
  10. event_type = !event_type ? 'click' : event_type; // set event type to "click" by default
  11. $(root).bind(event_type, (evt) => {
  12. VE.core.dispatch(evt, $(evt.target), event_type); // dispatch captured event
  13. });
  14. },
  15. /**
  16. * Dispatch event that was previously registered
  17. * @param evt related event object
  18. * @param elm that was catched
  19. * @param event_type (eg: click, mouseover etc..)
  20. */
  21. dispatch: (evt, elm, event_type) => {
  22. if ('undefined' == typeof VE.callbacks[event_type]) {
  23. return VE.helpers.warn(
  24. 'There is no corresponding object that should contain event callbacks for "' +
  25. event_type +
  26. '" event type'
  27. );
  28. }
  29. // get class of element
  30. const classes = $(elm).attr('class');
  31. // if no classes are attached, then just stop any further processings
  32. if (!classes) {
  33. return; // no classes assigned
  34. }
  35. // split the classes and check if it related to function
  36. $(classes.split(/\s/)).each((i, key) => {
  37. VE.callbacks[event_type][key] && VE.callbacks[event_type][key](evt, elm);
  38. });
  39. },
  40. },
  41. navigation: {
  42. state: {
  43. active_menu: 1,
  44. menu_selector: '.main-menu-item',
  45. menu_active_selector: '.active',
  46. },
  47. enter_focused: () => {
  48. if ($('.units').hasClass('active')) {
  49. location.href = $(
  50. '.units.active .l-unit.focus .actions-panel__col.actions-panel__edit a'
  51. ).attr('href');
  52. } else {
  53. if ($(VE.navigation.state.menu_selector + '.focus a').attr('href')) {
  54. location.href = $(VE.navigation.state.menu_selector + '.focus a').attr('href');
  55. }
  56. }
  57. },
  58. move_focus_left: () => {
  59. let index = parseInt(
  60. $(VE.navigation.state.menu_selector).index($(VE.navigation.state.menu_selector + '.focus'))
  61. );
  62. if (index == -1)
  63. index = parseInt(
  64. $(VE.navigation.state.menu_selector).index($(VE.navigation.state.menu_active_selector))
  65. );
  66. if ($('.units').hasClass('active')) {
  67. $('.units').removeClass('active');
  68. index++;
  69. }
  70. $(VE.navigation.state.menu_selector).removeClass('focus');
  71. if (index > 0) {
  72. $($(VE.navigation.state.menu_selector)[index - 1]).addClass('focus');
  73. } else {
  74. VE.navigation.switch_menu('last');
  75. }
  76. },
  77. move_focus_right: () => {
  78. const max_index = $(VE.navigation.state.menu_selector).length - 1;
  79. let index = parseInt(
  80. $(VE.navigation.state.menu_selector).index($(VE.navigation.state.menu_selector + '.focus'))
  81. );
  82. if (index == -1)
  83. index =
  84. parseInt(
  85. $(VE.navigation.state.menu_selector).index($(VE.navigation.state.menu_active_selector))
  86. ) || 0;
  87. $(VE.navigation.state.menu_selector).removeClass('focus');
  88. if ($('.units').hasClass('active')) {
  89. $('.units').removeClass('active');
  90. index--;
  91. }
  92. if (index < max_index) {
  93. $($(VE.navigation.state.menu_selector)[index + 1]).addClass('focus');
  94. } else {
  95. VE.navigation.switch_menu('first');
  96. }
  97. },
  98. move_focus_down: () => {
  99. const max_index = $('.units .l-unit:not(.header)').length - 1;
  100. let index = parseInt($('.units .l-unit').index($('.units .l-unit.focus')));
  101. if (index < max_index) {
  102. $('.units .l-unit.focus').removeClass('focus');
  103. $($('.units .l-unit:not(.header)')[index + 1]).addClass('focus');
  104. $('html, body').animate({ scrollTop: $('.units .l-unit.focus').offset().top - 200 }, 200);
  105. }
  106. },
  107. move_focus_up: () => {
  108. let index = parseInt($('.units .l-unit:not(.header)').index($('.units .l-unit.focus')));
  109. if (index == -1) index = 0;
  110. if (index > 0) {
  111. $('.units .l-unit.focus').removeClass('focus');
  112. $($('.units .l-unit:not(.header)')[index - 1]).addClass('focus');
  113. $('html, body').animate({ scrollTop: $('.units .l-unit.focus').offset().top - 200 }, 200);
  114. }
  115. },
  116. switch_menu: (position) => {
  117. position = position || 'first'; // last
  118. if (VE.navigation.state.active_menu == 0) {
  119. VE.navigation.state.active_menu = 1;
  120. VE.navigation.state.menu_selector = '.main-menu-item';
  121. VE.navigation.state.menu_active_selector = '.active';
  122. if (position == 'first') {
  123. $($(VE.navigation.state.menu_selector)[0]).addClass('focus');
  124. } else {
  125. const max_index = $(VE.navigation.state.menu_selector).length - 1;
  126. $($(VE.navigation.state.menu_selector)[max_index]).addClass('focus');
  127. }
  128. }
  129. },
  130. shortcut: (elm) => {
  131. /** @type {'js' | 'href'} */
  132. const action = elm.attr('key-action');
  133. switch (action) {
  134. case 'js':
  135. VE.core.dispatch(true, elm.find('.data-controls'), 'click');
  136. break;
  137. case 'href':
  138. location.href = elm.find('a').attr('href');
  139. break;
  140. default:
  141. break;
  142. }
  143. },
  144. },
  145. callbacks: {
  146. click: {
  147. // Usage: <a class="data-controls do_something" title="Something">Do something</a>
  148. // do_something: (evt, elm) => {
  149. // const ref = elm.hasClass('actions-panel') ? elm : elm.parents('.actions-panel');
  150. // const title = $(elm).parent().attr('title') || $(elm).attr('title');
  151. // const targetUrl = $('input[name="suspend_url"]', ref).val();
  152. // VE.helpers.createConfirmationDialog({ title, targetUrl });
  153. // },
  154. },
  155. },
  156. helpers: {
  157. /**
  158. * Create confirmation <dialog> on the fly
  159. * @param title The title of the dialog displayed in the header
  160. * @param message The message displayed in the body of the dialog
  161. * @param targetUrl URL that will be redirected to if user clicks "OK"
  162. */
  163. createConfirmationDialog: ({ title, message = 'Are you sure?', targetUrl }) => {
  164. // Create the dialog
  165. const dialog = document.createElement('dialog');
  166. dialog.classList.add('modal');
  167. // Create and insert the title
  168. if (title) {
  169. const titleElem = document.createElement('h2');
  170. titleElem.textContent = title;
  171. titleElem.classList.add('modal-title');
  172. dialog.appendChild(titleElem);
  173. }
  174. // Create and insert the message
  175. const messageElem = document.createElement('p');
  176. messageElem.textContent = message;
  177. messageElem.classList.add('modal-message');
  178. dialog.appendChild(messageElem);
  179. // Create and insert the options
  180. const optionsElem = document.createElement('div');
  181. optionsElem.classList.add('modal-options');
  182. const confirmButton = document.createElement('button');
  183. confirmButton.type = 'submit';
  184. confirmButton.classList.add('button');
  185. confirmButton.textContent = 'OK';
  186. optionsElem.appendChild(confirmButton);
  187. const cancelButton = document.createElement('button');
  188. cancelButton.type = 'button';
  189. cancelButton.classList.add('button', 'button-secondary', 'cancel', 'u-ml5');
  190. cancelButton.textContent = 'Cancel';
  191. if (targetUrl) {
  192. optionsElem.appendChild(cancelButton);
  193. }
  194. dialog.appendChild(optionsElem);
  195. // Define named functions to handle the event listeners
  196. const handleConfirm = () => {
  197. if (targetUrl) {
  198. window.location.href = targetUrl;
  199. }
  200. handleClose();
  201. };
  202. const handleCancel = () => handleClose();
  203. const handleClose = () => {
  204. confirmButton.removeEventListener('click', handleConfirm);
  205. cancelButton.removeEventListener('click', handleCancel);
  206. dialog.removeEventListener('close', handleClose);
  207. document.removeEventListener('keydown', handleKeydown);
  208. document.body.removeChild(dialog);
  209. };
  210. const handleKeydown = ({ key }) => {
  211. if (key === 'Escape') {
  212. handleClose();
  213. }
  214. };
  215. // Add event listeners
  216. confirmButton.addEventListener('click', handleConfirm);
  217. cancelButton.addEventListener('click', handleCancel);
  218. dialog.addEventListener('close', handleClose);
  219. document.addEventListener('keydown', handleKeydown);
  220. // Add to DOM and show
  221. document.body.appendChild(dialog);
  222. dialog.showModal();
  223. },
  224. warn: (msg) => {
  225. alert('WARNING: ' + msg);
  226. },
  227. extendPasswordFields: () => {
  228. const references = ['.js-password-input'];
  229. $(document).ready(() => {
  230. $(references).each((i, ref) => {
  231. VE.helpers.initAdditionalPasswordFieldElements(ref);
  232. });
  233. });
  234. },
  235. initAdditionalPasswordFieldElements: (ref) => {
  236. const enabled = Cookies.read('hide_passwords') == 1 ? true : false;
  237. if (enabled) {
  238. Cookies.set('hide_passwords', 1, 365);
  239. $(ref).prop('type', 'password');
  240. }
  241. $(ref).prop('autocomplete', 'off');
  242. const html =
  243. '<span class="toggle-password"><i class="toggle-psw-visibility-icon fas fa-eye-slash ' +
  244. enabled
  245. ? ''
  246. : 'u-opacity-50' +
  247. '" onclick="VE.helpers.toggleHiddenPasswordText(\'' +
  248. ref +
  249. '\', this)"></i></span>';
  250. $(ref).after(html);
  251. },
  252. toggleHiddenPasswordText: (ref, triggering_elm) => {
  253. $(triggering_elm).toggleClass('u-opacity-50');
  254. if ($(ref).prop('type') == 'text') {
  255. Cookies.set('hide_passwords', 1, 365);
  256. $(ref).prop('type', 'password');
  257. } else {
  258. Cookies.set('hide_passwords', 0, 365);
  259. $(ref).prop('type', 'text');
  260. }
  261. },
  262. },
  263. tmp: {
  264. sort_par: 'sort-name',
  265. sort_direction: -1,
  266. sort_as_int: false,
  267. },
  268. };
  269. VE.helpers.extendPasswordFields();