michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["ContentUtil"]; michael@0: michael@0: const XHTML_NS = "http://www.w3.org/1999/xhtml"; michael@0: const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent; michael@0: michael@0: this.ContentUtil = { michael@0: populateFragmentFromString: function populateFragmentFromString(fragment, str) { michael@0: let re = /^([^#]*)#(\d+)\b([^#]*)/, michael@0: document = fragment.ownerDocument, michael@0: // the remaining arguments are our {text, className} values michael@0: replacements = Array.slice(arguments, 2), michael@0: match; michael@0: michael@0: // walk over the string, building textNode/spans as nec. with replacement content michael@0: // note that #1,#2 etc. may not appear in numerical order in the string michael@0: while ((match = re.exec(str))) { michael@0: let [mstring,pre,num,post] = match, michael@0: replaceText = "", michael@0: replaceClass, michael@0: idx = num-1; // markers are 1-based, replacement indices 0 based michael@0: michael@0: str = str.substring(re.lastIndex+mstring.length); michael@0: michael@0: if (pre) michael@0: fragment.appendChild(document.createTextNode(pre)); michael@0: michael@0: if (replacements[idx]) { michael@0: replaceText = replacements[idx].text; michael@0: let spanNode = document.createElementNS(XHTML_NS, "span"); michael@0: spanNode.appendChild(document.createTextNode(replaceText)); michael@0: // add class to the span when provided michael@0: if(replacements[idx].className) michael@0: spanNode.classList.add(replacements[idx].className); michael@0: michael@0: fragment.appendChild(spanNode); michael@0: } else { michael@0: // put it back if no replacement was provided michael@0: fragment.appendChild(document.createTextNode("#"+num)); michael@0: } michael@0: michael@0: if(post) michael@0: fragment.appendChild(document.createTextNode(post)); michael@0: } michael@0: if(str) michael@0: fragment.appendChild(document.createTextNode(str)); michael@0: michael@0: return fragment; michael@0: }, michael@0: michael@0: // Pass several objects in and it will combine them all into the first object and return it. michael@0: // NOTE: Deep copy is not supported michael@0: extend: function extend() { michael@0: // copy reference to target object michael@0: let target = arguments[0] || {}; michael@0: let length = arguments.length; michael@0: michael@0: if (length === 1) { michael@0: return target; michael@0: } michael@0: michael@0: // Handle case when target is a string or something michael@0: if (typeof target != "object" && typeof target != "function") { michael@0: target = {}; michael@0: } michael@0: michael@0: for (let i = 1; i < length; i++) { michael@0: // Only deal with non-null/undefined values michael@0: let options = arguments[i]; michael@0: if (options != null) { michael@0: // Extend the base object michael@0: for (let name in options) { michael@0: let copy = options[name]; michael@0: michael@0: // Prevent never-ending loop michael@0: if (target === copy) michael@0: continue; michael@0: michael@0: if (copy !== undefined) michael@0: target[name] = copy; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Return the modified object michael@0: return target; michael@0: }, michael@0: michael@0: // Checks if a keycode is used for list navigation. michael@0: isNavigationKey: function (keyCode) { michael@0: let navigationKeys = [ michael@0: nsIDOMKeyEvent.DOM_VK_DOWN, michael@0: nsIDOMKeyEvent.DOM_VK_UP, michael@0: nsIDOMKeyEvent.DOM_VK_LEFT, michael@0: nsIDOMKeyEvent.DOM_VK_RIGHT, michael@0: nsIDOMKeyEvent.DOM_VK_PAGE_UP, michael@0: nsIDOMKeyEvent.DOM_VK_PAGE_DOWN, michael@0: nsIDOMKeyEvent.DOM_VK_ESCAPE]; michael@0: michael@0: return navigationKeys.indexOf(keyCode) != -1; michael@0: } michael@0: };