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.

common-global.js 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import {mqBinarySearch} from '../utils.js';
  2. import createDropzone from './dropzone.js';
  3. import {initCompColorPicker} from './comp/ColorPicker.js';
  4. import 'jquery.are-you-sure';
  5. const {csrfToken} = window.config;
  6. export function initGlobalFormDirtyLeaveConfirm() {
  7. // Warn users that try to leave a page after entering data into a form.
  8. // Except on sign-in pages, and for forms marked as 'ignore-dirty'.
  9. if ($('.user.signin').length === 0) {
  10. $('form:not(.ignore-dirty)').areYouSure();
  11. }
  12. }
  13. export function initHeadNavbarContentToggle() {
  14. const content = $('#navbar');
  15. const toggle = $('#navbar-expand-toggle');
  16. let isExpanded = false;
  17. toggle.on('click', () => {
  18. isExpanded = !isExpanded;
  19. if (isExpanded) {
  20. content.addClass('shown');
  21. toggle.addClass('active');
  22. } else {
  23. content.removeClass('shown');
  24. toggle.removeClass('active');
  25. }
  26. });
  27. }
  28. export function initFootLanguageMenu() {
  29. function linkLanguageAction() {
  30. const $this = $(this);
  31. $.post($this.data('url')).always(() => {
  32. window.location.reload();
  33. });
  34. }
  35. $('.language-menu a[lang]').on('click', linkLanguageAction);
  36. }
  37. export function initGlobalEnterQuickSubmit() {
  38. $('.js-quick-submit').on('keydown', function (e) {
  39. if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) {
  40. $(this).closest('form').trigger('submit');
  41. }
  42. });
  43. }
  44. export function initGlobalButtonClickOnEnter() {
  45. $(document).on('keypress', '.ui.button', (e) => {
  46. if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar
  47. $(e.target).trigger('click');
  48. }
  49. });
  50. }
  51. export function initGlobalCommon() {
  52. // Show exact time
  53. $('.time-since').each(function () {
  54. $(this)
  55. .addClass('tooltip')
  56. .attr('data-content', $(this).attr('title'))
  57. .attr('title', '');
  58. });
  59. // Undo Safari emoji glitch fix at high enough zoom levels
  60. if (navigator.userAgent.match('Safari')) {
  61. $(window).resize(() => {
  62. const px = mqBinarySearch('width', 0, 4096, 1, 'px');
  63. const em = mqBinarySearch('width', 0, 1024, 0.01, 'em');
  64. if (em * 16 * 1.25 - px <= -1) {
  65. $('body').addClass('safari-above125');
  66. } else {
  67. $('body').removeClass('safari-above125');
  68. }
  69. });
  70. }
  71. // Semantic UI modules.
  72. $('.dropdown:not(.custom)').dropdown({
  73. fullTextSearch: 'exact'
  74. });
  75. $('.jump.dropdown').dropdown({
  76. action: 'hide',
  77. onShow() {
  78. $('.tooltip').popup('hide');
  79. },
  80. fullTextSearch: 'exact'
  81. });
  82. $('.slide.up.dropdown').dropdown({
  83. transition: 'slide up',
  84. fullTextSearch: 'exact'
  85. });
  86. $('.upward.dropdown').dropdown({
  87. direction: 'upward',
  88. fullTextSearch: 'exact'
  89. });
  90. $('.ui.checkbox').checkbox();
  91. $('.ui.progress').progress({
  92. showActivity: false
  93. });
  94. // init popups
  95. $('.tooltip').each((_, el) => {
  96. const $el = $(el);
  97. const attr = $el.attr('data-variation');
  98. const attrs = attr ? attr.split(' ') : [];
  99. const variations = new Set([...attrs, 'inverted', 'tiny']);
  100. $el.attr('data-variation', [...variations].join(' ')).popup();
  101. });
  102. $('.top.menu .tooltip').popup({
  103. onShow() {
  104. if ($('.top.menu .menu.transition').hasClass('visible')) {
  105. return false;
  106. }
  107. }
  108. });
  109. $('.tabular.menu .item').tab();
  110. $('.tabable.menu .item').tab();
  111. $('.toggle.button').on('click', function () {
  112. $($(this).data('target')).slideToggle(100);
  113. });
  114. // make table <tr> and <td> elements clickable like a link
  115. $('tr[data-href], td[data-href]').on('click', function (e) {
  116. const href = $(this).data('href');
  117. if (e.target.nodeName === 'A') {
  118. // if a user clicks on <a>, then the <tr> or <td> should not act as a link.
  119. return;
  120. }
  121. if (e.ctrlKey || e.metaKey) {
  122. // ctrl+click or meta+click opens a new window in modern browsers
  123. window.open(href);
  124. } else {
  125. window.location = href;
  126. }
  127. });
  128. }
  129. export function initGlobalDropzone() {
  130. // Dropzone
  131. for (const el of document.querySelectorAll('.dropzone')) {
  132. const $dropzone = $(el);
  133. const _promise = createDropzone(el, {
  134. url: $dropzone.data('upload-url'),
  135. headers: {'X-Csrf-Token': csrfToken},
  136. maxFiles: $dropzone.data('max-file'),
  137. maxFilesize: $dropzone.data('max-size'),
  138. acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'),
  139. addRemoveLinks: true,
  140. dictDefaultMessage: $dropzone.data('default-message'),
  141. dictInvalidFileType: $dropzone.data('invalid-input-type'),
  142. dictFileTooBig: $dropzone.data('file-too-big'),
  143. dictRemoveFile: $dropzone.data('remove-file'),
  144. timeout: 0,
  145. thumbnailMethod: 'contain',
  146. thumbnailWidth: 480,
  147. thumbnailHeight: 480,
  148. init() {
  149. this.on('success', (_file, data) => {
  150. const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
  151. $dropzone.find('.files').append(input);
  152. });
  153. this.on('removedfile', (file) => {
  154. $(`#${file.uuid}`).remove();
  155. if ($dropzone.data('remove-url')) {
  156. $.post($dropzone.data('remove-url'), {
  157. file: file.uuid,
  158. _csrf: csrfToken,
  159. });
  160. }
  161. });
  162. },
  163. });
  164. }
  165. }
  166. export function initGlobalLinkActions() {
  167. function showDeletePopup() {
  168. const $this = $(this);
  169. const dataArray = $this.data();
  170. let filter = '';
  171. if ($this.data('modal-id')) {
  172. filter += `#${$this.data('modal-id')}`;
  173. }
  174. const dialog = $(`.delete.modal${filter}`);
  175. dialog.find('.name').text($this.data('name'));
  176. for (const [key, value] of Object.entries(dataArray)) {
  177. if (key && key.startsWith('data')) {
  178. dialog.find(`.${key}`).text(value);
  179. }
  180. }
  181. dialog.modal({
  182. closable: false,
  183. onApprove() {
  184. if ($this.data('type') === 'form') {
  185. $($this.data('form')).trigger('submit');
  186. return;
  187. }
  188. const postData = {
  189. _csrf: csrfToken,
  190. };
  191. for (const [key, value] of Object.entries(dataArray)) {
  192. if (key && key.startsWith('data')) {
  193. postData[key.substr(4)] = value;
  194. }
  195. if (key === 'id') {
  196. postData['id'] = value;
  197. }
  198. }
  199. $.post($this.data('url'), postData).done((data) => {
  200. window.location.href = data.redirect;
  201. });
  202. }
  203. }).modal('show');
  204. return false;
  205. }
  206. function showAddAllPopup() {
  207. const $this = $(this);
  208. let filter = '';
  209. if ($this.attr('id')) {
  210. filter += `#${$this.attr('id')}`;
  211. }
  212. const dialog = $(`.addall.modal${filter}`);
  213. dialog.find('.name').text($this.data('name'));
  214. dialog.modal({
  215. closable: false,
  216. onApprove() {
  217. if ($this.data('type') === 'form') {
  218. $($this.data('form')).trigger('submit');
  219. return;
  220. }
  221. $.post($this.data('url'), {
  222. _csrf: csrfToken,
  223. id: $this.data('id')
  224. }).done((data) => {
  225. window.location.href = data.redirect;
  226. });
  227. }
  228. }).modal('show');
  229. return false;
  230. }
  231. function linkAction(e) {
  232. e.preventDefault();
  233. const $this = $(this);
  234. const redirect = $this.data('redirect');
  235. $.post($this.data('url'), {
  236. _csrf: csrfToken
  237. }).done((data) => {
  238. if (data.redirect) {
  239. window.location.href = data.redirect;
  240. } else if (redirect) {
  241. window.location.href = redirect;
  242. } else {
  243. window.location.reload();
  244. }
  245. });
  246. }
  247. // Helpers.
  248. $('.delete-button').on('click', showDeletePopup);
  249. $('.link-action').on('click', linkAction);
  250. // FIXME: this function is only used once, and not common, not well designed. should be refactored later
  251. $('.add-all-button').on('click', showAddAllPopup);
  252. // FIXME: this is only used once, and should be replace with `link-action` instead
  253. $('.undo-button').on('click', function () {
  254. const $this = $(this);
  255. $.post($this.data('url'), {
  256. _csrf: csrfToken,
  257. id: $this.data('id')
  258. }).done((data) => {
  259. window.location.href = data.redirect;
  260. });
  261. });
  262. }
  263. export function initGlobalButtons() {
  264. $('.show-panel.button').on('click', function () {
  265. $($(this).data('panel')).show();
  266. });
  267. $('.hide-panel.button').on('click', function (event) {
  268. $($(this).data('panel')).hide();
  269. event.preventDefault();
  270. });
  271. $('.show-modal.button').on('click', function () {
  272. $($(this).data('modal')).modal('show');
  273. const colorPickers = $($(this).data('modal')).find('.color-picker');
  274. if (colorPickers.length > 0) {
  275. initCompColorPicker();
  276. }
  277. });
  278. $('.delete-post.button').on('click', function () {
  279. const $this = $(this);
  280. $.post($this.data('request-url'), {
  281. _csrf: csrfToken
  282. }).done(() => {
  283. window.location.href = $this.data('done-url');
  284. });
  285. });
  286. }