aboutsummaryrefslogtreecommitdiffhomepage
path: root/assets/default/js/base.js
diff options
context:
space:
mode:
Diffstat (limited to 'assets/default/js/base.js')
-rw-r--r--assets/default/js/base.js114
1 files changed, 83 insertions, 31 deletions
diff --git a/assets/default/js/base.js b/assets/default/js/base.js
index d5c29c69..4163577d 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
@@ -10,7 +11,7 @@ import Awesomplete from 'awesomplete';
10 * @returns Found element or null. 11 * @returns Found element or null.
11 */ 12 */
12function findParent(element, tagName, attributes) { 13function findParent(element, tagName, attributes) {
13 const parentMatch = key => attributes[key] !== '' && element.getAttribute(key).indexOf(attributes[key]) !== -1; 14 const parentMatch = (key) => attributes[key] !== '' && element.getAttribute(key).indexOf(attributes[key]) !== -1;
14 while (element) { 15 while (element) {
15 if (element.tagName.toLowerCase() === tagName) { 16 if (element.tagName.toLowerCase() === tagName) {
16 if (Object.keys(attributes).find(parentMatch)) { 17 if (Object.keys(attributes).find(parentMatch)) {
@@ -25,12 +26,18 @@ function findParent(element, tagName, attributes) {
25/** 26/**
26 * Ajax request to refresh the CSRF token. 27 * Ajax request to refresh the CSRF token.
27 */ 28 */
28function refreshToken() { 29function refreshToken(basePath, callback) {
29 const xhr = new XMLHttpRequest(); 30 const xhr = new XMLHttpRequest();
30 xhr.open('GET', '?do=token'); 31 xhr.open('GET', `${basePath}/admin/token`);
31 xhr.onload = () => { 32 xhr.onload = () => {
32 const token = document.getElementById('token'); 33 const elements = document.querySelectorAll('input[name="token"]');
33 token.setAttribute('value', xhr.responseText); 34 [...elements].forEach((element) => {
35 element.setAttribute('value', xhr.responseText);
36 });
37
38 if (callback) {
39 callback(xhr.response);
40 }
34 }; 41 };
35 xhr.send(); 42 xhr.send();
36} 43}
@@ -90,15 +97,6 @@ function updateAwesompleteList(selector, tags, instances) {
90} 97}
91 98
92/** 99/**
93 * html_entities in JS
94 *
95 * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript
96 */
97function htmlEntities(str) {
98 return str.replace(/[\u00A0-\u9999<>&]/gim, i => `&#${i.charCodeAt(0)};`);
99}
100
101/**
102 * Add the class 'hidden' to city options not attached to the current selected continent. 100 * Add the class 'hidden' to city options not attached to the current selected continent.
103 * 101 *
104 * @param cities List of <option> elements 102 * @param cities List of <option> elements
@@ -188,8 +186,8 @@ function removeClass(element, classname) {
188function init(description) { 186function init(description) {
189 function resize() { 187 function resize() {
190 /* Fix jumpy resizing: https://stackoverflow.com/a/18262927/1484919 */ 188 /* Fix jumpy resizing: https://stackoverflow.com/a/18262927/1484919 */
191 const scrollTop = window.pageYOffset || 189 const scrollTop = window.pageYOffset
192 (document.documentElement || document.body.parentNode || document.body).scrollTop; 190 || (document.documentElement || document.body.parentNode || document.body).scrollTop;
193 191
194 description.style.height = 'auto'; 192 description.style.height = 'auto';
195 description.style.height = `${description.scrollHeight + 10}px`; 193 description.style.height = `${description.scrollHeight + 10}px`;
@@ -215,6 +213,8 @@ function init(description) {
215} 213}
216 214
217(() => { 215(() => {
216 const basePath = document.querySelector('input[name="js_base_path"]').value;
217
218 /** 218 /**
219 * Handle responsive menu. 219 * Handle responsive menu.
220 * Source: http://purecss.io/layouts/tucked-menu-vertical/ 220 * Source: http://purecss.io/layouts/tucked-menu-vertical/
@@ -294,7 +294,7 @@ function init(description) {
294 const deleteLinks = document.querySelectorAll('.confirm-delete'); 294 const deleteLinks = document.querySelectorAll('.confirm-delete');
295 [...deleteLinks].forEach((deleteLink) => { 295 [...deleteLinks].forEach((deleteLink) => {
296 deleteLink.addEventListener('click', (event) => { 296 deleteLink.addEventListener('click', (event) => {
297 if (!confirm(document.getElementById('translation-delete-link').innerHTML)) { 297 if (!confirm(document.getElementById('translation-delete-tag').innerHTML)) {
298 event.preventDefault(); 298 event.preventDefault();
299 } 299 }
300 }); 300 });
@@ -461,7 +461,7 @@ function init(description) {
461 }); 461 });
462 462
463 if (window.confirm(message)) { 463 if (window.confirm(message)) {
464 window.location = `?delete_link&lf_linkdate=${ids.join('+')}&token=${token.value}`; 464 window.location = `${basePath}/admin/shaare/delete?id=${ids.join('+')}&token=${token.value}`;
465 } 465 }
466 }); 466 });
467 } 467 }
@@ -482,8 +482,10 @@ function init(description) {
482 }); 482 });
483 }); 483 });
484 484
485 const ids = links.map(item => item.id); 485 const ids = links.map((item) => item.id);
486 window.location = `?change_visibility&token=${token.value}&newVisibility=${visibility}&ids=${ids.join('+')}`; 486 window.location = (
487 `${basePath}/admin/shaare/visibility?token=${token.value}&newVisibility=${visibility}&id=${ids.join('+')}`
488 );
487 }); 489 });
488 }); 490 });
489 } 491 }
@@ -545,8 +547,9 @@ function init(description) {
545 } 547 }
546 const refreshedToken = document.getElementById('token').value; 548 const refreshedToken = document.getElementById('token').value;
547 const fromtag = block.getAttribute('data-tag'); 549 const fromtag = block.getAttribute('data-tag');
550 const fromtagUrl = block.getAttribute('data-tag-url');
548 const xhr = new XMLHttpRequest(); 551 const xhr = new XMLHttpRequest();
549 xhr.open('POST', '?do=changetag'); 552 xhr.open('POST', `${basePath}/admin/tags`);
550 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 553 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
551 xhr.onload = () => { 554 xhr.onload = () => {
552 if (xhr.status !== 200) { 555 if (xhr.status !== 200) {
@@ -554,20 +557,28 @@ function init(description) {
554 location.reload(); 557 location.reload();
555 } else { 558 } else {
556 block.setAttribute('data-tag', totag); 559 block.setAttribute('data-tag', totag);
560 block.setAttribute('data-tag-url', encodeURIComponent(totag));
557 input.setAttribute('name', totag); 561 input.setAttribute('name', totag);
558 input.setAttribute('value', totag); 562 input.setAttribute('value', totag);
559 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none'; 563 findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none';
560 block.querySelector('a.tag-link').innerHTML = htmlEntities(totag); 564 block.querySelector('a.tag-link').innerHTML = he.encode(totag);
561 block.querySelector('a.tag-link').setAttribute('href', `?searchtags=${encodeURIComponent(totag)}`); 565 block
562 block.querySelector('a.rename-tag').setAttribute('href', `?do=changetag&fromtag=${encodeURIComponent(totag)}`); 566 .querySelector('a.tag-link')
567 .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`);
568 block
569 .querySelector('a.count')
570 .setAttribute('href', `${basePath}/add-tag/${encodeURIComponent(totag)}`);
571 block
572 .querySelector('a.rename-tag')
573 .setAttribute('href', `${basePath}/admin/tags?fromtag=${encodeURIComponent(totag)}`);
563 574
564 // Refresh awesomplete values 575 // Refresh awesomplete values
565 existingTags = existingTags.map(tag => (tag === fromtag ? totag : tag)); 576 existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag));
566 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 577 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes);
567 } 578 }
568 }; 579 };
569 xhr.send(`renametag=1&fromtag=${encodeURIComponent(fromtag)}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`); 580 xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`);
570 refreshToken(); 581 refreshToken(basePath);
571 }); 582 });
572 }); 583 });
573 584
@@ -589,19 +600,20 @@ function init(description) {
589 event.preventDefault(); 600 event.preventDefault();
590 const block = findParent(event.target, 'div', { class: 'tag-list-item' }); 601 const block = findParent(event.target, 'div', { class: 'tag-list-item' });
591 const tag = block.getAttribute('data-tag'); 602 const tag = block.getAttribute('data-tag');
603 const tagUrl = block.getAttribute('data-tag-url');
592 const refreshedToken = document.getElementById('token').value; 604 const refreshedToken = document.getElementById('token').value;
593 605
594 if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) { 606 if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) {
595 const xhr = new XMLHttpRequest(); 607 const xhr = new XMLHttpRequest();
596 xhr.open('POST', '?do=changetag'); 608 xhr.open('POST', `${basePath}/admin/tags`);
597 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 609 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
598 xhr.onload = () => { 610 xhr.onload = () => {
599 block.remove(); 611 block.remove();
600 }; 612 };
601 xhr.send(encodeURI(`deletetag=1&fromtag=${tag}&token=${refreshedToken}`)); 613 xhr.send(`deletetag=1&fromtag=${tagUrl}&token=${refreshedToken}`);
602 refreshToken(); 614 refreshToken(basePath);
603 615
604 existingTags = existingTags.filter(tagItem => tagItem !== tag); 616 existingTags = existingTags.filter((tagItem) => tagItem !== tag);
605 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); 617 awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes);
606 } 618 }
607 }); 619 });
@@ -611,4 +623,44 @@ function init(description) {
611 [...autocompleteFields].forEach((autocompleteField) => { 623 [...autocompleteFields].forEach((autocompleteField) => {
612 awesomepletes.push(createAwesompleteInstance(autocompleteField)); 624 awesomepletes.push(createAwesompleteInstance(autocompleteField));
613 }); 625 });
626
627 const exportForm = document.querySelector('#exportform');
628 if (exportForm != null) {
629 exportForm.addEventListener('submit', (event) => {
630 event.preventDefault();
631
632 refreshToken(basePath, () => {
633 event.target.submit();
634 });
635 });
636 }
637
638 const bulkCreationButton = document.querySelector('.addlink-batch-show-more-block');
639 if (bulkCreationButton != null) {
640 const toggleBulkCreationVisibility = (showMoreBlockElement, formElement) => {
641 if (bulkCreationButton.classList.contains('pure-u-0')) {
642 showMoreBlockElement.classList.remove('pure-u-0');
643 formElement.classList.add('pure-u-0');
644 } else {
645 showMoreBlockElement.classList.add('pure-u-0');
646 formElement.classList.remove('pure-u-0');
647 }
648 };
649
650 const bulkCreationForm = document.querySelector('.addlink-batch-form-block');
651
652 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
653 bulkCreationButton.querySelector('a').addEventListener('click', (e) => {
654 e.preventDefault();
655 toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm);
656 });
657
658 // Force to send falsy value if the checkbox is not checked.
659 const privateButton = bulkCreationForm.querySelector('input[type="checkbox"][name="private"]');
660 const privateHiddenButton = bulkCreationForm.querySelector('input[type="hidden"][name="private"]');
661 privateButton.addEventListener('click', () => {
662 privateHiddenButton.disabled = !privateHiddenButton.disabled;
663 });
664 privateHiddenButton.disabled = privateButton.checked;
665 }
614})(); 666})();