utils.js (4716B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const FLASH_TARGETS_SELECTOR = "[class*=theme-fg-color], .force-color-on-flash"; 8 9 /** 10 * Apply a 'flashed' background and foreground color to elements. Intended 11 * to be used with flashElementOff as a way of drawing attention to an element. 12 * 13 * @param {Node} backgroundElt 14 * The element to set the highlighted background color on. 15 * @param {object} options 16 * @param {Node} options.foregroundElt 17 * The element to set the matching foreground color on. This will equal 18 * backgroundElt if not set. 19 * @param {string} options.backgroundClass 20 * The background highlight color class to set on the element. 21 */ 22 function flashElementOn( 23 backgroundElt, 24 { foregroundElt = backgroundElt, backgroundClass = "theme-bg-contrast" } = {} 25 ) { 26 if (!backgroundElt || !foregroundElt) { 27 return; 28 } 29 30 // Make sure the animation class is not here 31 backgroundElt.classList.remove("flash-out"); 32 33 // Change the background 34 backgroundElt.classList.add(backgroundClass); 35 36 foregroundElt.classList.add("theme-fg-contrast"); 37 foregroundElt 38 .querySelectorAll(FLASH_TARGETS_SELECTOR) 39 .forEach(span => span.classList.add("theme-fg-contrast")); 40 } 41 42 /** 43 * Remove a 'flashed' background and foreground color to elements. 44 * See flashElementOn. 45 * 46 * @param {Node} backgroundElt 47 * The element to remove the highlighted background color on. 48 * @param {object} options 49 * @param {Node} options.foregroundElt 50 * The element to remove the matching foreground color on. This will equal 51 * backgroundElt if not set. 52 * @param {string} options.backgroundClass 53 * The background highlight color class to remove on the element. 54 */ 55 function flashElementOff( 56 backgroundElt, 57 { foregroundElt = backgroundElt, backgroundClass = "theme-bg-contrast" } = {} 58 ) { 59 if (!backgroundElt || !foregroundElt) { 60 return; 61 } 62 63 // Add the animation class to smoothly remove the background 64 backgroundElt.classList.add("flash-out"); 65 66 // Remove the background 67 backgroundElt.classList.remove(backgroundClass); 68 69 foregroundElt.classList.remove("theme-fg-contrast"); 70 // Make sure the foreground animation class is removed 71 foregroundElt.classList.remove("flash-out"); 72 foregroundElt 73 .querySelectorAll(FLASH_TARGETS_SELECTOR) 74 .forEach(span => span.classList.remove("theme-fg-contrast")); 75 } 76 77 /** 78 * Retrieve the available width between a provided element left edge and a container right 79 * edge. This used can be used as a max-width for inplace-editor (autocomplete) widgets 80 * replacing Editor elements of the the markup-view; 81 */ 82 function getAutocompleteMaxWidth(element, container) { 83 const elementRect = element.getBoundingClientRect(); 84 const containerRect = container.getBoundingClientRect(); 85 return containerRect.right - elementRect.left - 2; 86 } 87 88 /** 89 * Parse attribute names and values from a string. 90 * 91 * @param {string} attr 92 * The input string for which names/values are to be parsed. 93 * @param {HTMLDocument} doc 94 * A document that can be used to test valid attributes. 95 * @return {Array} 96 * An array of attribute names and their values. 97 */ 98 function parseAttributeValues(attr, doc) { 99 attr = attr.trim(); 100 101 const parseAndGetNode = str => { 102 return new DOMParser().parseFromString(str, "text/html").body.childNodes[0]; 103 }; 104 105 // Handle bad user inputs by appending a " or ' if it fails to parse without 106 // them. Also note that a SVG tag is used to make sure the HTML parser 107 // preserves mixed-case attributes 108 const el = 109 parseAndGetNode("<svg " + attr + "></svg>") || 110 parseAndGetNode("<svg " + attr + '"></svg>') || 111 parseAndGetNode("<svg " + attr + "'></svg>"); 112 113 // Create <div> in new document to work around CSP blocking inline styles. 114 const htmlDoc = doc.implementation.createHTMLDocument(); 115 const div = htmlDoc.createElement("div"); 116 const attributes = []; 117 for (const { name, value } of el.attributes) { 118 // Try to set on an element in the document, throws exception on bad input. 119 // Prevents InvalidCharacterError - "String contains an invalid character". 120 try { 121 div.setAttribute(name, value); 122 attributes.push({ name, value }); 123 } catch (e) { 124 // This may throw exceptions on bad input. 125 // Prevents InvalidCharacterError - "String contains an invalid 126 // character". 127 } 128 } 129 130 return attributes; 131 } 132 133 module.exports = { 134 flashElementOn, 135 flashElementOff, 136 getAutocompleteMaxWidth, 137 parseAttributeValues, 138 };