From 4cf3564d28dc8e4d08a3e64f09ad045ffbde97ae Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 25 Sep 2020 13:29:36 +0200 Subject: Add a setting to retrieve bookmark metadata asynchrounously - There is a new standalone script (metadata.js) which requests a new controller to get bookmark metadata and fill the form async - This feature is enabled with the new setting: general.enable_async_metadata (enabled by default) - general.retrieve_description is now enabled by default - A small rotating loader animation has a been added to bookmark inputs when metadata is being retrieved (default template) - Custom JS htmlentities has been removed and mathiasbynens/he library is used instead Fixes #1563 --- assets/default/js/base.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index be986ae0..31688815 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -1,4 +1,5 @@ import Awesomplete from 'awesomplete'; +import he from 'he'; /** * Find a parent element according to its tag and its attributes @@ -95,15 +96,6 @@ function updateAwesompleteList(selector, tags, instances) { return instances; } -/** - * html_entities in JS - * - * @see http://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript - */ -function htmlEntities(str) { - return str.replace(/[\u00A0-\u9999<>&]/gim, (i) => `&#${i.charCodeAt(0)};`); -} - /** * Add the class 'hidden' to city options not attached to the current selected continent. * @@ -569,7 +561,7 @@ function init(description) { input.setAttribute('name', totag); input.setAttribute('value', totag); findParent(input, 'div', { class: 'rename-tag-form' }).style.display = 'none'; - block.querySelector('a.tag-link').innerHTML = htmlEntities(totag); + block.querySelector('a.tag-link').innerHTML = he.encode(totag); block .querySelector('a.tag-link') .setAttribute('href', `${basePath}/?searchtags=${encodeURIComponent(totag)}`); -- cgit v1.2.3 From 3cb4e8a44cd09676e28757540c938a80e3e7ff60 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 16 Oct 2020 20:03:25 +0200 Subject: Improve Manage tags page Fixes #1125 --- assets/default/js/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index be986ae0..aadffc13 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -302,7 +302,7 @@ function init(description) { const deleteLinks = document.querySelectorAll('.confirm-delete'); [...deleteLinks].forEach((deleteLink) => { deleteLink.addEventListener('click', (event) => { - if (!confirm(document.getElementById('translation-delete-link').innerHTML)) { + if (!confirm(document.getElementById('translation-delete-tag').innerHTML)) { event.preventDefault(); } }); -- cgit v1.2.3 From 5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 10 Oct 2020 17:40:26 +0200 Subject: Feature: bulk creation of bookmarks This changes creates a new form in addlink page allowing to create multiple bookmarks at once more easily. It focuses on re-using as much existing code and template component as possible. These changes includes: - a new form in addlink (hidden behind a button by default), containing a text area for URL, and tags/private status to apply to created links - this form displays a new template called editlink.batch, itself including editlink template multiple times - User interation in this new templates are handle by a new JS script (shaare-batch.js) making AJAX requests, and therefore does not need page reloading - ManageShaareController has been split into 3 distinct controllers: + ShaareAdd: displays addlink template + ShaareManage: various operation applied on existing shaares (change visibility, pin, deletion, etc.) + ShaarePublish: handles creation/edit forms and saving Shaare's form - Updated translations Fixes #137 --- assets/default/js/base.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 7f6b9637..9161b4fc 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -634,4 +634,25 @@ function init(description) { }); }); } + + const bulkCreationButton = document.querySelector('.addlink-batch-show-more-block'); + if (bulkCreationButton != null) { + const toggleBulkCreationVisibility = (showMoreBlockElement, formElement) => { + if (bulkCreationButton.classList.contains('pure-u-0')) { + showMoreBlockElement.classList.remove('pure-u-0'); + formElement.classList.add('pure-u-0'); + } else { + showMoreBlockElement.classList.add('pure-u-0'); + formElement.classList.remove('pure-u-0'); + } + }; + + const bulkCreationForm = document.querySelector('.addlink-batch-form-block'); + + toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm); + bulkCreationButton.querySelector('a').addEventListener('click', (e) => { + e.preventDefault(); + toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm); + }); + } })(); -- cgit v1.2.3 From 25e90d8d75382721ff7473fa1686090fcfeb46ff Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 11 Oct 2020 13:34:38 +0200 Subject: Bulk creation: fix private status based on the first form --- assets/default/js/base.js | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 9161b4fc..4163577d 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -654,5 +654,13 @@ function init(description) { e.preventDefault(); toggleBulkCreationVisibility(bulkCreationButton, bulkCreationForm); }); + + // Force to send falsy value if the checkbox is not checked. + const privateButton = bulkCreationForm.querySelector('input[type="checkbox"][name="private"]'); + const privateHiddenButton = bulkCreationForm.querySelector('input[type="hidden"][name="private"]'); + privateButton.addEventListener('click', () => { + privateHiddenButton.disabled = !privateHiddenButton.disabled; + }); + privateHiddenButton.disabled = privateButton.checked; } })(); -- cgit v1.2.3 From 5f987a64d88e0c1bb0da8bde9050e3409879cbda Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 16:32:15 +0100 Subject: Fix confirm popup before bookmark deletion Regression introduced by #1596 Fixes #1623 --- assets/default/js/base.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 4163577d..66badfb2 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -294,7 +294,8 @@ function init(description) { const deleteLinks = document.querySelectorAll('.confirm-delete'); [...deleteLinks].forEach((deleteLink) => { deleteLink.addEventListener('click', (event) => { - if (!confirm(document.getElementById('translation-delete-tag').innerHTML)) { + const type = event.currentTarget.getAttribute('data-type') || 'link'; + if (!confirm(document.getElementById(`translation-delete-${type}`).innerHTML)) { event.preventDefault(); } }); -- cgit v1.2.3 From b3bd8c3e8d367975980043e772f7cd78b7f96bc6 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 22 Oct 2020 16:21:03 +0200 Subject: Feature: support any tag separator So it allows to have multiple words tags. Breaking change: commas ',' are no longer a default separator. Fixes #594 --- assets/default/js/base.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 66badfb2..e7bf4909 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -42,19 +42,20 @@ function refreshToken(basePath, callback) { xhr.send(); } -function createAwesompleteInstance(element, tags = []) { +function createAwesompleteInstance(element, separator, tags = []) { const awesome = new Awesomplete(Awesomplete.$(element)); - // Tags are separated by a space - awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(/[^ ]*$/)[0]); + + // Tags are separated by separator + awesome.filter = (text, input) => Awesomplete.FILTER_CONTAINS(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Insert new selected tag in the input awesome.replace = (text) => { - const before = awesome.input.value.match(/^.+ \s*|/)[0]; - awesome.input.value = `${before}${text} `; + const before = awesome.input.value.match(new RegExp(`^.+${separator}+|`))[0]; + awesome.input.value = `${before}${text}${separator}`; }; // Highlight found items - awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(/[^ ]*$/)[0]); + awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Don't display already selected items - const reg = /(\w+) /g; + const reg = new RegExp(`/(\w+)${separator}/g`); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { @@ -78,13 +79,14 @@ function createAwesompleteInstance(element, tags = []) { * @param selector CSS selector * @param tags Array of tags * @param instances List of existing awesomplete instances + * @param separator Tags separator character */ -function updateAwesompleteList(selector, tags, instances) { +function updateAwesompleteList(selector, tags, instances, separator) { if (instances.length === 0) { // First load: create Awesomplete instances const elements = document.querySelectorAll(selector); [...elements].forEach((element) => { - instances.push(createAwesompleteInstance(element, tags)); + instances.push(createAwesompleteInstance(element, separator, tags)); }); } else { // Update awesomplete tag list @@ -214,6 +216,8 @@ function init(description) { (() => { const basePath = document.querySelector('input[name="js_base_path"]').value; + const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || '\s' : '\s'; /** * Handle responsive menu. @@ -575,7 +579,7 @@ function init(description) { // Refresh awesomplete values existingTags = existingTags.map((tag) => (tag === fromtag ? totag : tag)); - awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); + awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator); } }; xhr.send(`renametag=1&fromtag=${fromtagUrl}&totag=${encodeURIComponent(totag)}&token=${refreshedToken}`); @@ -615,14 +619,14 @@ function init(description) { refreshToken(basePath); existingTags = existingTags.filter((tagItem) => tagItem !== tag); - awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes); + awesomepletes = updateAwesompleteList('.rename-tag-input', existingTags, awesomepletes, tagsSeparator); } }); }); const autocompleteFields = document.querySelectorAll('input[data-multiple]'); [...autocompleteFields].forEach((autocompleteField) => { - awesomepletes.push(createAwesompleteInstance(autocompleteField)); + awesomepletes.push(createAwesompleteInstance(autocompleteField, tagsSeparator)); }); const exportForm = document.querySelector('#exportform'); -- cgit v1.2.3 From df9aac5b6406bf192f2e4e0987e25d0de88480df Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 18:16:52 +0100 Subject: Tags separator: vintage theme compatibility --- assets/default/js/base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index e7bf4909..37069d69 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -217,7 +217,7 @@ function init(description) { (() => { const basePath = document.querySelector('input[name="js_base_path"]').value; const tagsSeparatorElement = document.querySelector('input[name="tags_separator"]'); - const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || '\s' : '\s'; + const tagsSeparator = tagsSeparatorElement ? tagsSeparatorElement.value || ' ' : ' '; /** * Handle responsive menu. -- cgit v1.2.3 From 8a1ce1da15fdbae99b24700b06f2008c7a657603 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 19:08:38 +0100 Subject: ESLint --- assets/default/js/base.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'assets/default/js/base.js') diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 37069d69..dd532bb7 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -55,7 +55,8 @@ function createAwesompleteInstance(element, separator, tags = []) { // Highlight found items awesome.item = (text, input) => Awesomplete.ITEM(text, input.match(new RegExp(`[^${separator}]*$`))[0]); // Don't display already selected items - const reg = new RegExp(`/(\w+)${separator}/g`); + // WARNING: pseudo classes does not seem to work with string litterals... + const reg = new RegExp(`([^${separator}]+)${separator}`, 'g'); let match; awesome.data = (item, input) => { while ((match = reg.exec(input))) { -- cgit v1.2.3