]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - assets/default/js/base.js
Feature: support any tag separator
[github/shaarli/Shaarli.git] / assets / default / js / base.js
CommitLineData
a33c5653 1import Awesomplete from 'awesomplete';
4cf3564d 2import he from 'he';
b9b41d25 3
82e3bb5f
A
4/**
5 * Find a parent element according to its tag and its attributes
6 *
7 * @param element Element where to start the search
8 * @param tagName Expected parent tag name
9 * @param attributes Associative array of expected attributes (name=>value).
10 *
11 * @returns Found element or null.
12 */
a33c5653 13function findParent(element, tagName, attributes) {
9192a48b 14 const parentMatch = (key) => attributes[key] !== '' && element.getAttribute(key).indexOf(attributes[key]) !== -1;
a33c5653
A
15 while (element) {
16 if (element.tagName.toLowerCase() === tagName) {
17 if (Object.keys(attributes).find(parentMatch)) {
18 return element;
19 }
aa4797ba 20 }
a33c5653
A
21 element = element.parentElement;
22 }
23 return null;
aa4797ba
A
24}
25
82e3bb5f
A
26/**
27 * Ajax request to refresh the CSRF token.
28 */
cd10bc23 29function refreshToken(basePath, callback) {
a33c5653 30 const xhr = new XMLHttpRequest();
764d34a7 31 xhr.open('GET', `${basePath}/admin/token`);
a33c5653 32 xhr.onload = () => {
301c7ab1
A
33 const elements = document.querySelectorAll('input[name="token"]');
34 [...elements].forEach((element) => {
301c7ab1
A
35 element.setAttribute('value', xhr.responseText);
36 });
cd10bc23
A
37
38 if (callback) {
39 callback(xhr.response);
40 }
a33c5653
A
41 };
42 xhr.send();
43}
44
b3bd8c3e 45function createAwesompleteInstance(element, separator, tags = []) {
a33c5653 46 const awesome = new Awesomplete(Awesomplete.$(element));
b3bd8c3e
A
47
48 // Tags are separated by separator
49 awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
a33c5653
A
50 // Insert new selected tag in the input
51 awesome.replace = (text) => {
b3bd8c3e
A
52 const before = awesome.input.value.match(new RegExp(`^.+${separator}+|`))[0];
53 awesome.input.value = `${before}${text}${separator}`;
a33c5653
A
54 };
55 // Highlight found items
b3bd8c3e 56 awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
a33c5653 57 // Don't display already selected items
b3bd8c3e 58 const reg = new RegExp(`/(\w+)${separator}/g`);
a33c5653
A
59 let match;
60 awesome.data = (item, input) => {
61 while ((match = reg.exec(input))) {
62 if (item === match[1]) {
63 return '';
64 }
65 }
66 return item;
67 };
68 awesome.minChars = 1;
69 if (tags.length) {
70 awesome.list = tags;
71 }
72
73 return awesome;
aa4797ba
A
74}
75
82e3bb5f
A
76/**
77 * Update awesomplete list of tag for all elements matching the given selector
78 *
79 * @param selector CSS selector
80 * @param tags Array of tags
81 * @param instances List of existing awesomplete instances
b3bd8c3e 82 * @param separator Tags separator character
82e3bb5f 83 */
b3bd8c3e 84function updateAwesompleteList(selector, tags, instances, separator) {
a33c5653 85 if (instances.length === 0) {
82e3bb5f 86 // First load: create Awesomplete instances
a33c5653
A
87 const elements = document.querySelectorAll(selector);
88 [...elements].forEach((element) => {
b3bd8c3e 89 instances.push(createAwesompleteInstance(element, separator, tags));
a33c5653
A
90 });
91 } else {
92 // Update awesomplete tag list
93 instances.map((item) => {
94 item.list = tags;
95 return item;
96 });
97 }
98 return instances;
82e3bb5f
A
99}
100
a0737313
A
101/**
102 * Add the class 'hidden' to city options not attached to the current selected continent.
103 *
104 * @param cities List of <option> elements
105 * @param currentContinent Current selected continent
106 * @param reset Set to true to reset the selected value
107 */
a33c5653
A
108function hideTimezoneCities(cities, currentContinent, reset = null) {
109 let first = true;
110 if (reset == null) {
111 reset = false;
112 }
113 [...cities].forEach((option) => {
114 if (option.getAttribute('data-continent') !== currentContinent) {
115 option.className = 'hidden';
116 } else {
117 option.className = '';
118 if (reset === true && first === true) {
119 option.setAttribute('selected', 'selected');
120 first = false;
121 }
aa4797ba 122 }
a33c5653
A
123 });
124}
125
126/**
127 * Retrieve an element up in the tree from its class name.
128 */
129function getParentByClass(el, className) {
130 const p = el.parentNode;
131 if (p == null || p.classList.contains(className)) {
132 return p;
133 }
134 return getParentByClass(p, className);
135}
136
137function toggleHorizontal() {
138 [...document.getElementById('shaarli-menu').querySelectorAll('.menu-transform')].forEach((el) => {
139 el.classList.toggle('pure-menu-horizontal');
140 });
141}
142
143function toggleMenu(menu) {
144 // set timeout so that the panel has a chance to roll up
145 // before the menu switches states
146 if (menu.classList.contains('open')) {
147 setTimeout(toggleHorizontal, 500);
148 } else {
149 toggleHorizontal();
150 }
151 menu.classList.toggle('open');
152 document.getElementById('menu-toggle').classList.toggle('x');
153}
154
155function closeMenu(menu) {
156 if (menu.classList.contains('open')) {
157 toggleMenu(menu);
158 }
159}
160
161function toggleFold(button, description, thumb) {
162 // Switch fold/expand - up = fold
163 if (button.classList.contains('fa-chevron-up')) {
164 button.title = document.getElementById('translation-expand').innerHTML;
165 if (description != null) {
166 description.style.display = 'none';
167 }
168 if (thumb != null) {
169 thumb.style.display = 'none';
170 }
171 } else {
172 button.title = document.getElementById('translation-fold').innerHTML;
173 if (description != null) {
174 description.style.display = 'block';
175 }
176 if (thumb != null) {
177 thumb.style.display = 'block';
178 }
179 }
180 button.classList.toggle('fa-chevron-down');
181 button.classList.toggle('fa-chevron-up');
182}
183
184function removeClass(element, classname) {
185 element.className = element.className.replace(new RegExp(`(?:^|\\s)${classname}(?:\\s|$)`), ' ');
186}
187
188function init(description) {
189 function resize() {
190 /* Fix jumpy resizing: https://stackoverflow.com/a/18262927/1484919 */
9192a48b
A
191 const scrollTop = window.pageYOffset
192 || (document.documentElement || document.body.parentNode || document.body).scrollTop;
a33c5653
A
193
194 description.style.height = 'auto';
195 description.style.height = `${description.scrollHeight + 10}px`;
196
197 window.scrollTo(0, scrollTop);
198 }
199
200 /* 0-timeout to get the already changed text */
201 function delayedResize() {
202 window.setTimeout(resize, 0);
203 }
204
205 const observe = (element, event, handler) => {
206 element.addEventListener(event, handler, false);
207 };
208 observe(description, 'change', resize);
209 observe(description, 'cut', delayedResize);
210 observe(description, 'paste', delayedResize);
211 observe(description, 'drop', delayedResize);
212 observe(description, 'keydown', delayedResize);
213
214 resize();
215}
216
217(() => {
818b3193 218 const basePath = document.querySelector('input[name="js_base_path"]').value;
b3bd8c3e
A
219 const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]');
220 const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || '\s' : '\s';
818b3193 221
a33c5653
A
222 /**
223 * Handle responsive menu.
224 * Source: http://purecss.io/layouts/tucked-menu-vertical/
225 */
226 const menu = document.getElementById('shaarli-menu');
227 const WINDOW_CHANGE_EVENT = ('onorientationchange' in window) ? 'orientationchange' : 'resize';
228
229 const menuToggle = document.getElementById('menu-toggle');
230 if (menuToggle != null) {
231 menuToggle.addEventListener('click', () => toggleMenu(menu));
232 }
233
234 window.addEventListener(WINDOW_CHANGE_EVENT, () => closeMenu(menu));
235
236 /**
237 * Fold/Expand shaares description and thumbnail.
238 */
239 const foldAllButtons = document.getElementsByClassName('fold-all');
240 const foldButtons = document.getElementsByClassName('fold-button');
241
242 [...foldButtons].forEach((foldButton) => {
243 // Retrieve description
244 let description = null;
245 let thumbnail = null;
246 const linklistItem = getParentByClass(foldButton, 'linklist-item');
247 if (linklistItem != null) {
248 description = linklistItem.querySelector('.linklist-item-description');
249 thumbnail = linklistItem.querySelector('.linklist-item-thumbnail');
250 if (description != null || thumbnail != null) {
251 foldButton.style.display = 'inline';
252 }
253 }
254
255 foldButton.addEventListener('click', (event) => {
256 event.preventDefault();
257 toggleFold(event.target, description, thumbnail);
258 });
259 });
260
261 if (foldAllButtons != null) {
262 [].forEach.call(foldAllButtons, (foldAllButton) => {
263 foldAllButton.addEventListener('click', (event) => {
264 event.preventDefault();
265 const state = foldAllButton.firstElementChild.getAttribute('class').indexOf('down') !== -1 ? 'down' : 'up';
266 [].forEach.call(foldButtons, (foldButton) => {
267 if ((foldButton.firstElementChild.classList.contains('fa-chevron-up') && state === 'down')
268 || (foldButton.firstElementChild.classList.contains('fa-chevron-down') && state === 'up')
269 ) {
270 return;
271 }
272 // Retrieve description
273 let description = null;
274 let thumbnail = null;
275 const linklistItem = getParentByClass(foldButton, 'linklist-item');
276 if (linklistItem != null) {
277 description = linklistItem.querySelector('.linklist-item-description');
278 thumbnail = linklistItem.querySelector('.linklist-item-thumbnail');
279 if (description != null || thumbnail != null) {
280 foldButton.style.display = 'inline';
281 }
282 }
283
284 toggleFold(foldButton.firstElementChild, description, thumbnail);
285 });
286 foldAllButton.firstElementChild.classList.toggle('fa-chevron-down');
287 foldAllButton.firstElementChild.classList.toggle('fa-chevron-up');
288 foldAllButton.title = state === 'down'
289 ? document.getElementById('translation-fold-all').innerHTML
290 : document.getElementById('translation-expand-all').innerHTML;
291 });
292 });
293 }
294
295 /**
296 * Confirmation message before deletion.
297 */
298 const deleteLinks = document.querySelectorAll('.confirm-delete');
299 [...deleteLinks].forEach((deleteLink) => {
300 deleteLink.addEventListener('click', (event) => {
5f987a64
A
301 const type = event.currentTarget.getAttribute('data-type') || 'link';
302 if (!confirm(document.getElementById(`translation-delete-${type}`).innerHTML)) {
a33c5653
A
303 event.preventDefault();
304 }
305 });
306 });
307
308 /**
309 * Close alerts
310 */
311 const closeLinks = document.querySelectorAll('.pure-alert-close');
312 [...closeLinks].forEach((closeLink) => {
313 closeLink.addEventListener('click', (event) => {
314 const alert = getParentByClass(event.target, 'pure-alert-closable');
315 alert.style.display = 'none';
316 });
317 });
318
319 /**
320 * New version dismiss.
321 * Hide the message for one week using localStorage.
322 */
323 const newVersionDismiss = document.getElementById('new-version-dismiss');
324 const newVersionMessage = document.querySelector('.new-version-message');
325 if (newVersionMessage != null
326 && localStorage.getItem('newVersionDismiss') != null
327 && parseInt(localStorage.getItem('newVersionDismiss'), 10) + (7 * 24 * 60 * 60 * 1000) > (new Date()).getTime()
328 ) {
329 newVersionMessage.style.display = 'none';
330 }
331 if (newVersionDismiss != null) {
332 newVersionDismiss.addEventListener('click', () => {
333 localStorage.setItem('newVersionDismiss', (new Date()).getTime().toString());
334 });
335 }
336
337 const hiddenReturnurl = document.getElementsByName('returnurl');
338 if (hiddenReturnurl != null) {
339 hiddenReturnurl.value = window.location.href;
340 }
341
342 /**
343 * Autofocus text fields
344 */
345 const autofocusElements = document.querySelectorAll('.autofocus');
346 let breakLoop = false;
347 [].forEach.call(autofocusElements, (autofocusElement) => {
348 if (autofocusElement.value === '' && !breakLoop) {
349 autofocusElement.focus();
350 breakLoop = true;
351 }
352 });
353
354 /**
355 * Handle sub menus/forms
356 */
357 const openers = document.getElementsByClassName('subheader-opener');
358 if (openers != null) {
359 [...openers].forEach((opener) => {
360 opener.addEventListener('click', (event) => {
361 event.preventDefault();
362
363 const id = opener.getAttribute('data-open-id');
364 const sub = document.getElementById(id);
365
366 if (sub != null) {
367 [...document.getElementsByClassName('subheader-form')].forEach((element) => {
368 if (element !== sub) {
369 removeClass(element, 'open');
a0737313 370 }
a33c5653
A
371 });
372
373 sub.classList.toggle('open');
a0737313 374 }
a33c5653 375 });
a0737313 376 });
a33c5653
A
377 }
378
379 /**
380 * Remove CSS target padding (for fixed bar)
381 */
382 if (location.hash !== '') {
383 const anchor = document.getElementById(location.hash.substr(1));
384 if (anchor != null) {
385 const padsize = anchor.clientHeight;
386 window.scroll(0, window.scrollY - padsize);
387 anchor.style.paddingTop = '0';
388 }
389 }
390
391 /**
392 * Text area resizer
393 */
394 const description = document.getElementById('lf_description');
395
396 if (description != null) {
397 init(description);
398 // Submit editlink form with CTRL + Enter in the text area.
399 description.addEventListener('keydown', (event) => {
400 if (event.ctrlKey && event.keyCode === 13) {
401 document.getElementById('button-save-edit').click();
402 }
403 });
404 }
405
406 /**
407 * Bookmarklet alert
408 */
409 const bookmarkletLinks = document.querySelectorAll('.bookmarklet-link');
410 const bkmMessage = document.getElementById('bookmarklet-alert');
411 [].forEach.call(bookmarkletLinks, (link) => {
412 link.addEventListener('click', (event) => {
413 event.preventDefault();
414 alert(bkmMessage.value);
415 });
416 });
417
a33c5653
A
418 const continent = document.getElementById('continent');
419 const city = document.getElementById('city');
420 if (continent != null && city != null) {
421 continent.addEventListener('change', () => {
422 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, true);
423 });
424 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, false);
425 }
426
427 /**
428 * Bulk actions
429 */
fc574e64 430 const linkCheckboxes = document.querySelectorAll('.link-checkbox');
a33c5653
A
431 const bar = document.getElementById('actions');
432 [...linkCheckboxes].forEach((checkbox) => {
433 checkbox.style.display = 'inline-block';
fc574e64
A
434 checkbox.addEventListener('change', () => {
435 const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
a33c5653
A
436 const count = [...linkCheckedCheckboxes].length;
437 if (count === 0 && bar.classList.contains('open')) {
438 bar.classList.toggle('open');
439 } else if (count > 0 && !bar.classList.contains('open')) {
440 bar.classList.toggle('open');
441 }
442 });
443 });
444
445 const deleteButton = document.getElementById('actions-delete');
446 const token = document.getElementById('token');
447 if (deleteButton != null && token != null) {
448 deleteButton.addEventListener('click', (event) => {
449 event.preventDefault();
450
451 const links = [];
fc574e64 452 const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
a33c5653
A
453 [...linkCheckedCheckboxes].forEach((checkbox) => {
454 links.push({
455 id: checkbox.value,
456 title: document.querySelector(`.linklist-item[data-id="${checkbox.value}"] .linklist-link`).innerHTML,
457 });
458 });
459
460 let message = `Are you sure you want to delete ${links.length} links?\n`;
461 message += 'This action is IRREVERSIBLE!\n\nTitles:\n';
462 const ids = [];
463 links.forEach((item) => {
464 message += ` - ${item.title}\n`;
465 ids.push(item.id);
466 });
467
468 if (window.confirm(message)) {
9c75f877 469 window.location = `${basePath}/admin/shaare/delete?id=${ids.join('+')}&token=${token.value}`;
a33c5653
A
470 }
471 });
472 }
473
8d03f705
A
474 const changeVisibilityButtons = document.querySelectorAll('.actions-change-visibility');
475 if (changeVisibilityButtons != null && token != null) {
476 [...changeVisibilityButtons].forEach((button) => {
477 button.addEventListener('click', (event) => {
478 event.preventDefault();
479 const visibility = event.target.getAttribute('data-visibility');
480
481 const links = [];
482 const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
483 [...linkCheckedCheckboxes].forEach((checkbox) => {
484 links.push({
485 id: checkbox.value,
486 title: document.querySelector(`.linklist-item[data-id="${checkbox.value}"] .linklist-link`).innerHTML,
487 });
488 });
489
9192a48b
A
490 const ids = links.map((item) => item.id);
491 window.location = (
492 `${basePath}/admin/shaare/visibility?token=${token.value}&newVisibility=${visibility}&id=${ids.join('+')}`
493 );
8d03f705
A
494 });
495 });
496 }
497
fc574e64
A
498 /**
499 * Select all button
500 */
501 const selectAllButtons = document.querySelectorAll('.select-all-button');
502 [...selectAllButtons].forEach((selectAllButton) => {
503 selectAllButton.addEventListener('click', (e) => {
504 e.preventDefault();
505 const checked = selectAllButton.classList.contains('filter-off');
506 [...selectAllButtons].forEach((selectAllButton2) => {
507 selectAllButton2.classList.toggle('filter-off');
508 selectAllButton2.classList.toggle('filter-on');
509 });
510 [...linkCheckboxes].forEach((linkCheckbox) => {
511 linkCheckbox.checked = checked;
512 linkCheckbox.dispatchEvent(new Event('change'));
513 });
514 });
515 });
516
a33c5653
A
517 /**
518 * Tag list operations
519 *
520 * TODO: support error code in the backend for AJAX requests
521 */
522 const tagList = document.querySelector('input[name="taglist"]');
523 let existingTags = tagList ? tagList.value.split(' ') : [];
524 let awesomepletes = [];
525
526 // Display/Hide rename form
527 const renameTagButtons = document.querySelectorAll('.rename-tag');
528 [...renameTagButtons].forEach((rename) => {
529 rename.addEventListener('click', (event) => {
530 event.preventDefault();
531 const block = findParent(event.target, 'div', { class: 'tag-list-item' });
532 const form = block.querySelector('.rename-tag-form');
533 if (form.style.display === 'none' || form.style.display === '') {
534 form.style.display = 'block';
535 } else {
536 form.style.display = 'none';
537 }
538 block.querySelector('input').focus();
539 });
540 });
541
542 // Rename a tag with an AJAX request
543 const renameTagSubmits = document.querySelectorAll('.validate-rename-tag');
544 [...renameTagSubmits].forEach((rename) => {
545 rename.addEventListener('click', (event) => {
546 event.preventDefault();
547 const block = findParent(event.target, 'div', { class: 'tag-list-item' });
548 const input = block.querySelector('.rename-tag-input');
549 const totag = input.value.replace('/"/g', '\\"');
550 if (totag.trim() === '') {
551 return;
552 }
553 const refreshedToken = document.getElementById('token').value;
554 const fromtag = block.getAttribute('data-tag');
72fbbcd6 555 const fromtagUrl = block.getAttribute('data-tag-url');
a33c5653 556 const xhr = new XMLHttpRequest();
9c75f877 557 xhr.open('POST', `${basePath}/admin/tags`);
a33c5653
A
558 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
559 xhr.onload = () => {
560 if (xhr.status !== 200) {
561 alert(`An error occurred. Return code: ${xhr.status}`);
562 location.reload();
563 } else {
564 block.setAttribute('data-tag', totag);
72fbbcd6 565 block.setAttribute('data-tag-url', encodeURIComponent(totag));
a33c5653
A
566 input.setAttribute('name', totag);
567 input.setAttribute('value', totag);
568 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none';
4cf3564d 569 block.querySelector('a.tag-link').innerHTML = he.encode(totag);
818b3193
A
570 block
571 .querySelector('a.tag-link')
572 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`);
72fbbcd6
A
573 block
574 .querySelector('a.count')
575 .setAttribute('href', `${basePath}/add-tag/${encodeURIComponent(totag)}`);
818b3193
A
576 block
577 .querySelector('a.rename-tag')
9c75f877 578 .setAttribute('href', `${basePath}/admin/tags?fromtag=${encodeURIComponent(totag)}`);
a33c5653
A
579
580 // Refresh awesomplete values
9192a48b 581 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag));
b3bd8c3e 582 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
a33c5653
A
583 }
584 };
72fbbcd6 585 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`);
818b3193 586 refreshToken(basePath);
a33c5653
A
587 });
588 });
589
590 // Validate input with enter key
591 const renameTagInputs = document.querySelectorAll('.rename-tag-input');
592 [...renameTagInputs].forEach((rename) => {
593 rename.addEventListener('keypress', (event) => {
594 if (event.keyCode === 13) { // enter
595 findParent(event.target, 'div', { class: 'tag-list-item' }).querySelector('.validate-rename-tag').click();
596 }
597 });
598 });
599
600 // Delete a tag with an AJAX query (alert popup confirmation)
601 const deleteTagButtons = document.querySelectorAll('.delete-tag');
602 [...deleteTagButtons].forEach((rename) => {
603 rename.style.display = 'inline';
604 rename.addEventListener('click', (event) => {
605 event.preventDefault();
606 const block = findParent(event.target, 'div', { class: 'tag-list-item' });
607 const tag = block.getAttribute('data-tag');
72fbbcd6 608 const tagUrl = block.getAttribute('data-tag-url');
4fa9a3c5 609 const refreshedToken = document.getElementById('token').value;
a33c5653
A
610
611 if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) {
612 const xhr = new XMLHttpRequest();
9c75f877 613 xhr.open('POST', `${basePath}/admin/tags`);
a33c5653
A
614 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
615 xhr.onload = () => {
616 block.remove();
617 };
72fbbcd6 618 xhr.send(`deletetag=1&fromtag=${tagUrl}&token=${refreshedToken}`);
818b3193 619 refreshToken(basePath);
a33c5653 620
9192a48b 621 existingTags = existingTags.filter((tagItem) => tagItem !== tag);
b3bd8c3e 622 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
a33c5653
A
623 }
624 });
625 });
626
627 const autocompleteFields = document.querySelectorAll('input[data-multiple]');
628 [...autocompleteFields].forEach((autocompleteField) => {
b3bd8c3e 629 awesomepletes.push(createAwesompleteInstance(autocompleteField, tagsSeparator));
a33c5653 630 });
cd10bc23
A
631
632 const exportForm = document.querySelector('#exportform');
633 if (exportForm != null) {
634 exportForm.addEventListener('submit', (event) => {
635 event.preventDefault();
636
637 refreshToken(basePath, () => {
638 event.target.submit();
639 });
640 });
641 }
5d8de758
A
642
643 const bulkCreationButton = document.querySelector('.addlink-batch-show-more-block');
644 if (bulkCreationButton != null) {
645 const toggleBulkCreationVisibility = (showMoreBlockElement, formElement) => {
646 if (bulkCreationButton.classList.contains('pure-u-0')) {
647 showMoreBlockElement.classList.remove('pure-u-0');
648 formElement.classList.add('pure-u-0');
649 } else {
650 showMoreBlockElement.classList.add('pure-u-0');
651 formElement.classList.remove('pure-u-0');
652 }
653 };
654
655 const bulkCreationForm = document.querySelector('.addlink-batch-form-block');
656
657 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
658 bulkCreationButton.querySelector('a').addEventListener('click', (e) => {
659 e.preventDefault();
660 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
661 });
25e90d8d
A
662
663 // Force to send falsy value if the checkbox is not checked.
664 const privateButton = bulkCreationForm.querySelector('input[type="checkbox"][name="private"]');
665 const privateHiddenButton = bulkCreationForm.querySelector('input[type="hidden"][name="private"]');
666 privateButton.addEventListener('click', () => {
667 privateHiddenButton.disabled = !privateHiddenButton.disabled;
668 });
669 privateHiddenButton.disabled = privateButton.checked;
5d8de758 670 }
a33c5653 671})();