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/common/js/metadata.js | 39 ++++++++++++++++++++++++++++++ assets/default/js/base.js | 12 ++-------- assets/default/scss/shaarli.scss | 51 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 assets/common/js/metadata.js (limited to 'assets') diff --git a/assets/common/js/metadata.js b/assets/common/js/metadata.js new file mode 100644 index 00000000..5200b481 --- /dev/null +++ b/assets/common/js/metadata.js @@ -0,0 +1,39 @@ +import he from 'he'; + +function clearLoaders(loaders) { + if (loaders != null && loaders.length > 0) { + [...loaders].forEach((loader) => { + loader.classList.remove('loading-input'); + }); + } +} + +(() => { + const loaders = document.querySelectorAll('.loading-input'); + const inputTitle = document.querySelector('input[name="lf_title"]'); + if (inputTitle != null && inputTitle.value.length > 0) { + clearLoaders(loaders); + return; + } + + const url = document.querySelector('input[name="lf_url"]').value; + const basePath = document.querySelector('input[name="js_base_path"]').value; + + const xhr = new XMLHttpRequest(); + xhr.open('GET', `${basePath}/admin/metadata?url=${encodeURI(url)}`, true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.onload = () => { + const result = JSON.parse(xhr.response); + Object.keys(result).forEach((key) => { + if (result[key] !== null && result[key].length) { + const element = document.querySelector(`input[name="lf_${key}"], textarea[name="lf_${key}"]`); + if (element != null && element.value.length === 0) { + element.value = he.decode(result[key]); + } + } + }); + clearLoaders(loaders); + }; + + xhr.send(); +})(); 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)}`); diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss index a528adb0..df9c867b 100644 --- a/assets/default/scss/shaarli.scss +++ b/assets/default/scss/shaarli.scss @@ -1269,6 +1269,57 @@ form { } } +.loading-input { + position: relative; + + @keyframes around { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + .icon-container { + position: absolute; + right: 60px; + top: calc(50% - 10px); + } + + .loader { + position: relative; + height: 20px; + width: 20px; + display: inline-block; + animation: around 5.4s infinite; + + &::after, + &::before { + content: ""; + background: $form-input-background; + position: absolute; + display: inline-block; + width: 100%; + height: 100%; + border-width: 2px; + border-color: #333 #333 transparent transparent; + border-style: solid; + border-radius: 20px; + box-sizing: border-box; + top: 0; + left: 0; + animation: around 0.7s ease-in-out infinite; + } + + &::after { + animation: around 0.7s ease-in-out 0.1s infinite; + background: transparent; + } + } +} + // LOGIN .login-form-container { .remember-me { -- cgit v1.2.3