/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; /** * Apply a 'flashed' background and foreground color to elements. Intended * to be used with flashElementOff as a way of drawing attention to an element. * * @param {Node} backgroundElt * The element to set the highlighted background color on. * @param {Node} foregroundElt * The element to set the matching foreground color on. * Optional. This will equal backgroundElt if not set. */ function flashElementOn(backgroundElt, foregroundElt = backgroundElt) { if (!backgroundElt || !foregroundElt) { return; } // Make sure the animation class is not here backgroundElt.classList.remove("flash-out"); // Change the background backgroundElt.classList.add("theme-bg-contrast"); foregroundElt.classList.add("theme-fg-contrast"); [].forEach.call( foregroundElt.querySelectorAll("[class*=theme-fg-color]"), span => span.classList.add("theme-fg-contrast") ); } /** * Remove a 'flashed' background and foreground color to elements. * See flashElementOn. * * @param {Node} backgroundElt * The element to reomve the highlighted background color on. * @param {Node} foregroundElt * The element to remove the matching foreground color on. * Optional. This will equal backgroundElt if not set. */ function flashElementOff(backgroundElt, foregroundElt = backgroundElt) { if (!backgroundElt || !foregroundElt) { return; } // Add the animation class to smoothly remove the background backgroundElt.classList.add("flash-out"); // Remove the background backgroundElt.classList.remove("theme-bg-contrast"); foregroundElt.classList.remove("theme-fg-contrast"); [].forEach.call( foregroundElt.querySelectorAll("[class*=theme-fg-color]"), span => span.classList.remove("theme-fg-contrast") ); } /** * Retrieve the available width between a provided element left edge and a container right * edge. This used can be used as a max-width for inplace-editor (autocomplete) widgets * replacing Editor elements of the the markup-view; */ function getAutocompleteMaxWidth(element, container) { let elementRect = element.getBoundingClientRect(); let containerRect = container.getBoundingClientRect(); return containerRect.right - elementRect.left - 2; } /** * Parse attribute names and values from a string. * * @param {String} attr * The input string for which names/values are to be parsed. * @param {HTMLDocument} doc * A document that can be used to test valid attributes. * @return {Array} * An array of attribute names and their values. */ function parseAttributeValues(attr, doc) { attr = attr.trim(); let parseAndGetNode = str => { return new DOMParser().parseFromString(str, "text/html").body.childNodes[0]; }; // Handle bad user inputs by appending a " or ' if it fails to parse without // them. Also note that a SVG tag is used to make sure the HTML parser // preserves mixed-case attributes let el = parseAndGetNode("") || parseAndGetNode("") || parseAndGetNode(""); let div = doc.createElement("div"); let attributes = []; for (let {name, value} of el.attributes) { // Try to set on an element in the document, throws exception on bad input. // Prevents InvalidCharacterError - "String contains an invalid character". try { div.setAttribute(name, value); attributes.push({ name, value }); } catch (e) { // This may throw exceptions on bad input. // Prevents InvalidCharacterError - "String contains an invalid // character". } } return attributes; } /** * Truncate the string and add ellipsis to the middle of the string. */ function truncateString(str, maxLength) { if (!str || str.length <= maxLength) { return str; } return str.substring(0, Math.ceil(maxLength / 2)) + "…" + str.substring(str.length - Math.floor(maxLength / 2)); } module.exports = { flashElementOn, flashElementOff, getAutocompleteMaxWidth, parseAttributeValues, truncateString, };