aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/default/js
diff options
context:
space:
mode:
Diffstat (limited to 'assets/default/js')
-rw-r--r--assets/default/js/base.js73
1 files changed, 50 insertions, 23 deletions
diff --git a/assets/default/js/base.js b/assets/default/js/base.js
index be986ae0..dd532bb7 100644
--- a/assets/default/js/base.js
+++ b/assets/default/js/base.js
@@ -1,4 +1,5 @@
1import Awesomplete from 'awesomplete'; 1import Awesomplete from 'awesomplete';
2import he from 'he';
2 3
3/** 4/**
4 * Find a parent element according to its tag and its attributes 5 * Find a parent element according to its tag and its attributes
@@ -41,19 +42,21 @@ function refreshToken(basePath, callback) {
41 xhr.send(); 42 xhr.send();
42} 43}
43 44
44function createAwesompleteInstance(element, tags = []) { 45function createAwesompleteInstance(element, separator, tags = []) {
45 const awesome = new Awesomplete(Awesomplete.$(element)); 46 const awesome = new Awesomplete(Awesomplete.$(element));
46 // Tags are separated by a space 47
47 awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(/[^ ]*$/)[0]); 48 // Tags are separated by separator
49 awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
48 // Insert new selected tag in the input 50 // Insert new selected tag in the input
49 awesome.replace = (text) => { 51 awesome.replace = (text) => {
50 const before = awesome.input.value.match(/^.+ \s*|/)[0]; 52 const before = awesome.input.value.match(new RegExp(`^.+${separator}+|`))[0];
51 awesome.input.value = `${before}${text} `; 53 awesome.input.value = `${before}${text}${separator}`;
52 }; 54 };
53 // Highlight found items 55 // Highlight found items
54 awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(/[^ ]*$/)[0]); 56 awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]);
55 // Don't display already selected items 57 // Don't display already selected items
56 const reg = /(\w+) /g; 58 // WARNING: pseudo classes does not seem to work with string litterals...
59 const reg = new RegExp(`([^${separator}]+)${separator}`, 'g');
57 let match; 60 let match;
58 awesome.data = (item, input) => { 61 awesome.data = (item, input) => {
59 while ((match = reg.exec(input))) { 62 while ((match = reg.exec(input))) {
@@ -77,13 +80,14 @@ function createAwesompleteInstance(element, tags = []) {
77 * @param selector CSS selector 80 * @param selector CSS selector
78 * @param tags Array of tags 81 * @param tags Array of tags
79 * @param instances List of existing awesomplete instances 82 * @param instances List of existing awesomplete instances
83 * @param separator Tags separator character
80 */ 84 */
81function updateAwesompleteList(selector, tags, instances) { 85function updateAwesompleteList(selector, tags, instances, separator) {
82 if (instances.length === 0) { 86 if (instances.length === 0) {
83 // First load: create Awesomplete instances 87 // First load: create Awesomplete instances
84 const elements = document.querySelectorAll(selector); 88 const elements = document.querySelectorAll(selector);
85 [...elements].forEach((element) => { 89 [...elements].forEach((element) => {
86 instances.push(createAwesompleteInstance(element, tags)); 90 instances.push(createAwesompleteInstance(element, separator, tags));
87 }); 91 });
88 } else { 92 } else {
89 // Update awesomplete tag list 93 // Update awesomplete tag list
@@ -96,15 +100,6 @@ function updateAwesompleteList(selector, tags, instances) {
96} 100}
97 101
98/** 102/**
99 * html_entities in JS
100 *
101 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript
102 */
103function htmlEntities(str) {
104 return str.replace(/[\u00A0-\u9999<>&]/gim, (i) => `&#${i.charCodeAt(0)};`);
105}
106
107/**
108 * Add the class 'hidden' to city options not attached to the current selected continent. 103 * Add the class 'hidden' to city options not attached to the current selected continent.
109 * 104 *
110 * @param cities List of <option> elements 105 * @param cities List of <option> elements
@@ -222,6 +217,8 @@ function init(description) {
222 217
223(() => { 218(() => {
224 const basePath = document.querySelector('input[name="js_base_path"]').value; 219 const basePath = document.querySelector('input[name="js_base_path"]').value;
220 const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]');
221 const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || ' ' : ' ';
225 222
226 /** 223 /**
227 * Handle responsive menu. 224 * Handle responsive menu.
@@ -302,7 +299,8 @@ function init(description) {
302 const deleteLinks = document.querySelectorAll('.confirm-delete'); 299 const deleteLinks = document.querySelectorAll('.confirm-delete');
303 [...deleteLinks].forEach((deleteLink) => { 300 [...deleteLinks].forEach((deleteLink) => {
304 deleteLink.addEventListener('click', (event) => { 301 deleteLink.addEventListener('click', (event) => {
305 if (!confirm(document.getElementById('translation-delete-link').innerHTML)) { 302 const type = event.currentTarget.getAttribute('data-type') || 'link';
303 if (!confirm(document.getElementById(`translation-delete-${type}`).innerHTML)) {
306 event.preventDefault(); 304 event.preventDefault();
307 } 305 }
308 }); 306 });
@@ -569,7 +567,7 @@ function init(description) {
569 input.setAttribute('name', totag); 567 input.setAttribute('name', totag);
570 input.setAttribute('value', totag); 568 input.setAttribute('value', totag);
571 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none'; 569 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none';
572 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag); 570 block.querySelector('a.tag-link').innerHTML = he.encode(totag);
573 block 571 block
574 .querySelector('a.tag-link') 572 .querySelector('a.tag-link')
575 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`); 573 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`);
@@ -582,7 +580,7 @@ function init(description) {
582 580
583 // Refresh awesomplete values 581 // Refresh awesomplete values
584 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag)); 582 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag));
585 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 583 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
586 } 584 }
587 }; 585 };
588 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`); 586 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`);
@@ -622,14 +620,14 @@ function init(description) {
622 refreshToken(basePath); 620 refreshToken(basePath);
623 621
624 existingTags = existingTags.filter((tagItem) => tagItem !== tag); 622 existingTags = existingTags.filter((tagItem) => tagItem !== tag);
625 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 623 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator);
626 } 624 }
627 }); 625 });
628 }); 626 });
629 627
630 const autocompleteFields = document.querySelectorAll('input[data-multiple]'); 628 const autocompleteFields = document.querySelectorAll('input[data-multiple]');
631 [...autocompleteFields].forEach((autocompleteField) => { 629 [...autocompleteFields].forEach((autocompleteField) => {
632 awesomepletes.push(createAwesompleteInstance(autocompleteField)); 630 awesomepletes.push(createAwesompleteInstance(autocompleteField, tagsSeparator));
633 }); 631 });
634 632
635 const exportForm = document.querySelector('#exportform'); 633 const exportForm = document.querySelector('#exportform');
@@ -642,4 +640,33 @@ function init(description) {
642 }); 640 });
643 }); 641 });
644 } 642 }
643
644 const bulkCreationButton = document.querySelector('.addlink-batch-show-more-block');
645 if (bulkCreationButton != null) {
646 const toggleBulkCreationVisibility = (showMoreBlockElement, formElement) => {
647 if (bulkCreationButton.classList.contains('pure-u-0')) {
648 showMoreBlockElement.classList.remove('pure-u-0');
649 formElement.classList.add('pure-u-0');
650 } else {
651 showMoreBlockElement.classList.add('pure-u-0');
652 formElement.classList.remove('pure-u-0');
653 }
654 };
655
656 const bulkCreationForm = document.querySelector('.addlink-batch-form-block');
657
658 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
659 bulkCreationButton.querySelector('a').addEventListener('click', (e) => {
660 e.preventDefault();
661 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
662 });
663
664 // Force to send falsy value if the checkbox is not checked.
665 const privateButton = bulkCreationForm.querySelector('input[type="checkbox"][name="private"]');
666 const privateHiddenButton = bulkCreationForm.querySelector('input[type="hidden"][name="private"]');
667 privateButton.addEventListener('click', () => {
668 privateHiddenButton.disabled = !privateHiddenButton.disabled;
669 });
670 privateHiddenButton.disabled = privateButton.checked;
671 }
645})(); 672})();