diff --git a/edit/edit.js b/edit/edit.js index 413f2a13..93c2ba4b 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -17,7 +17,7 @@ onBackgroundReady(); // make querySelectorAll enumeration code readable ["forEach", "some", "indexOf", "map"].forEach(function(method) { - NodeList.prototype[method]= Array.prototype[method]; + NodeList.prototype[method]= Array.prototype[method]; }); // Chrome pre-34 @@ -25,2013 +25,2013 @@ Element.prototype.matches = Element.prototype.matches || Element.prototype.webki // Chrome pre-41 polyfill Element.prototype.closest = Element.prototype.closest || function(selector) { - for (var e = this; e && !e.matches(selector); e = e.parentElement) {} - return e; + for (var e = this; e && !e.matches(selector); e = e.parentElement) {} + return e; }; Array.prototype.rotate = function(amount) { // negative amount == rotate left - var r = this.slice(-amount, this.length); - Array.prototype.push.apply(r, this.slice(0, this.length - r.length)); - return r; + var r = this.slice(-amount, this.length); + Array.prototype.push.apply(r, this.slice(0, this.length - r.length)); + return r; }; Object.defineProperty(Array.prototype, "last", {get: function() { return this[this.length - 1]; }}); // preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs() new MutationObserver((mutations, observer) => { - const themeElement = document.getElementById("cm-theme"); - if (themeElement) { - themeElement.href = prefs.get("editor.theme") == "default" ? "" - : "vendor/codemirror/theme/" + prefs.get("editor.theme") + ".css"; - observer.disconnect(); - } + const themeElement = document.getElementById("cm-theme"); + if (themeElement) { + themeElement.href = prefs.get("editor.theme") == "default" ? "" + : "vendor/codemirror/theme/" + prefs.get("editor.theme") + ".css"; + observer.disconnect(); + } }).observe(document, {subtree: true, childList: true}); getCodeMirrorThemes(); // reroute handling to nearest editor when keypress resolves to one of these commands var hotkeyRerouter = { - commands: { - save: true, jumpToLine: true, nextEditor: true, prevEditor: true, - find: true, findNext: true, findPrev: true, replace: true, replaceAll: true, - toggleStyle: true, - }, - setState: function(enable) { - setTimeout(function() { - document[(enable ? "add" : "remove") + "EventListener"]("keydown", hotkeyRerouter.eventHandler); - }, 0); - }, - eventHandler: function(event) { - var keyName = CodeMirror.keyName(event); - if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand) - || "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) { - event.preventDefault(); - event.stopPropagation(); - } - function handleCommand(command) { - if (hotkeyRerouter.commands[command] === true) { - CodeMirror.commands[command](getEditorInSight(event.target)); - return true; - } - } - } + commands: { + save: true, jumpToLine: true, nextEditor: true, prevEditor: true, + find: true, findNext: true, findPrev: true, replace: true, replaceAll: true, + toggleStyle: true, + }, + setState: function(enable) { + setTimeout(function() { + document[(enable ? "add" : "remove") + "EventListener"]("keydown", hotkeyRerouter.eventHandler); + }, 0); + }, + eventHandler: function(event) { + var keyName = CodeMirror.keyName(event); + if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand) + || "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) { + event.preventDefault(); + event.stopPropagation(); + } + function handleCommand(command) { + if (hotkeyRerouter.commands[command] === true) { + CodeMirror.commands[command](getEditorInSight(event.target)); + return true; + } + } + } }; function onChange(event) { - var node = event.target; - if ("savedValue" in node) { - var currentValue = "checkbox" === node.type ? node.checked : node.value; - setCleanItem(node, node.savedValue === currentValue); - } else { - // the manually added section's applies-to is dirty only when the value is non-empty - setCleanItem(node, node.localName != "input" || !node.value.trim()); - delete node.savedValue; // only valid when actually saved - } - updateTitle(); + var node = event.target; + if ("savedValue" in node) { + var currentValue = "checkbox" === node.type ? node.checked : node.value; + setCleanItem(node, node.savedValue === currentValue); + } else { + // the manually added section's applies-to is dirty only when the value is non-empty + setCleanItem(node, node.localName != "input" || !node.value.trim()); + delete node.savedValue; // only valid when actually saved + } + updateTitle(); } // Set .dirty on stylesheet contributors that have changed function setDirtyClass(node, isDirty) { - node.classList.toggle("dirty", isDirty); + node.classList.toggle("dirty", isDirty); } function setCleanItem(node, isClean) { - if (!node.id) { - node.id = Date.now().toString(32).substr(-6); - } + if (!node.id) { + node.id = Date.now().toString(32).substr(-6); + } - if (isClean) { - delete dirty[node.id]; - // code sections have .CodeMirror property - if (node.CodeMirror) { - node.savedValue = node.CodeMirror.changeGeneration(); - } else { - node.savedValue = "checkbox" === node.type ? node.checked : node.value; - } - } else { - dirty[node.id] = true; - } + if (isClean) { + delete dirty[node.id]; + // code sections have .CodeMirror property + if (node.CodeMirror) { + node.savedValue = node.CodeMirror.changeGeneration(); + } else { + node.savedValue = "checkbox" === node.type ? node.checked : node.value; + } + } else { + dirty[node.id] = true; + } - setDirtyClass(node, !isClean); + setDirtyClass(node, !isClean); } function isCleanGlobal() { - var clean = Object.keys(dirty).length == 0; - setDirtyClass(document.body, !clean); + var clean = Object.keys(dirty).length == 0; + setDirtyClass(document.body, !clean); var saveBtn = document.getElementById("save-button") if (clean){ //saveBtn.removeAttribute('disabled'); }else{ //saveBtn.setAttribute('disabled', true); } - return clean; + return clean; } function setCleanGlobal() { - document.querySelectorAll("#header, #sections > div").forEach(setCleanSection); - dirty = {}; // forget the dirty applies-to ids from a deleted section after the style was saved + document.querySelectorAll("#header, #sections > div").forEach(setCleanSection); + dirty = {}; // forget the dirty applies-to ids from a deleted section after the style was saved } function setCleanSection(section) { - section.querySelectorAll(".style-contributor").forEach(function(node) { setCleanItem(node, true) }); + section.querySelectorAll(".style-contributor").forEach(function(node) { setCleanItem(node, true) }); - // #header section has no codemirror - var cm = section.CodeMirror; - if (cm) { - section.savedValue = cm.changeGeneration(); - indicateCodeChange(cm); - } + // #header section has no codemirror + var cm = section.CodeMirror; + if (cm) { + section.savedValue = cm.changeGeneration(); + indicateCodeChange(cm); + } } function initCodeMirror() { - var CM = CodeMirror; - var isWindowsOS = navigator.appVersion.indexOf("Windows") > 0; + var CM = CodeMirror; + var isWindowsOS = navigator.appVersion.indexOf("Windows") > 0; - // CodeMirror miserably fails on keyMap="" so let's ensure it's not - if (!prefs.get('editor.keyMap')) { - prefs.reset('editor.keyMap'); - } + // CodeMirror miserably fails on keyMap="" so let's ensure it's not + if (!prefs.get('editor.keyMap')) { + prefs.reset('editor.keyMap'); + } - // default option values - Object.assign(CM.defaults, { - mode: 'css', - lineNumbers: true, - lineWrapping: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], - matchBrackets: true, - highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, - hintOptions: {}, - lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get("editor.lintDelay")}, - lintReportDelay: prefs.get("editor.lintReportDelay"), - styleActiveLine: true, - theme: "default", - keyMap: prefs.get("editor.keyMap"), - extraKeys: { // independent of current keyMap - "Alt-Enter": "toggleStyle", - "Alt-PageDown": "nextEditor", - "Alt-PageUp": "prevEditor" - } - }, prefs.get("editor.options")); + // default option values + Object.assign(CM.defaults, { + mode: 'css', + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], + matchBrackets: true, + highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, + hintOptions: {}, + lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get("editor.lintDelay")}, + lintReportDelay: prefs.get("editor.lintReportDelay"), + styleActiveLine: true, + theme: "default", + keyMap: prefs.get("editor.keyMap"), + extraKeys: { // independent of current keyMap + "Alt-Enter": "toggleStyle", + "Alt-PageDown": "nextEditor", + "Alt-PageUp": "prevEditor" + } + }, prefs.get("editor.options")); - // additional commands - CM.commands.jumpToLine = jumpToLine; - CM.commands.nextEditor = function(cm) { nextPrevEditor(cm, 1) }; - CM.commands.prevEditor = function(cm) { nextPrevEditor(cm, -1) }; - CM.commands.save = save; - CM.commands.blockComment = function(cm) { - cm.blockComment(cm.getCursor("from"), cm.getCursor("to"), {fullLines: false}); - }; - CM.commands.toggleStyle = toggleStyle; + // additional commands + CM.commands.jumpToLine = jumpToLine; + CM.commands.nextEditor = function(cm) { nextPrevEditor(cm, 1) }; + CM.commands.prevEditor = function(cm) { nextPrevEditor(cm, -1) }; + CM.commands.save = save; + CM.commands.blockComment = function(cm) { + cm.blockComment(cm.getCursor("from"), cm.getCursor("to"), {fullLines: false}); + }; + CM.commands.toggleStyle = toggleStyle; - // "basic" keymap only has basic keys by design, so we skip it + // "basic" keymap only has basic keys by design, so we skip it - var extraKeysCommands = {}; - Object.keys(CM.defaults.extraKeys).forEach(function(key) { - extraKeysCommands[CM.defaults.extraKeys[key]] = true; - }); - if (!extraKeysCommands.jumpToLine) { - CM.keyMap.sublime["Ctrl-G"] = "jumpToLine"; - CM.keyMap.emacsy["Ctrl-G"] = "jumpToLine"; - CM.keyMap.pcDefault["Ctrl-J"] = "jumpToLine"; - CM.keyMap.macDefault["Cmd-J"] = "jumpToLine"; - } - if (!extraKeysCommands.autocomplete) { - CM.keyMap.pcDefault["Ctrl-Space"] = "autocomplete"; // will be used by "sublime" on PC via fallthrough - CM.keyMap.macDefault["Alt-Space"] = "autocomplete"; // OSX uses Ctrl-Space and Cmd-Space for something else - CM.keyMap.emacsy["Alt-/"] = "autocomplete"; // copied from "emacs" keymap - // "vim" and "emacs" define their own autocomplete hotkeys - } - if (!extraKeysCommands.blockComment) { - CM.keyMap.sublime["Shift-Ctrl-/"] = "blockComment"; - } + var extraKeysCommands = {}; + Object.keys(CM.defaults.extraKeys).forEach(function(key) { + extraKeysCommands[CM.defaults.extraKeys[key]] = true; + }); + if (!extraKeysCommands.jumpToLine) { + CM.keyMap.sublime["Ctrl-G"] = "jumpToLine"; + CM.keyMap.emacsy["Ctrl-G"] = "jumpToLine"; + CM.keyMap.pcDefault["Ctrl-J"] = "jumpToLine"; + CM.keyMap.macDefault["Cmd-J"] = "jumpToLine"; + } + if (!extraKeysCommands.autocomplete) { + CM.keyMap.pcDefault["Ctrl-Space"] = "autocomplete"; // will be used by "sublime" on PC via fallthrough + CM.keyMap.macDefault["Alt-Space"] = "autocomplete"; // OSX uses Ctrl-Space and Cmd-Space for something else + CM.keyMap.emacsy["Alt-/"] = "autocomplete"; // copied from "emacs" keymap + // "vim" and "emacs" define their own autocomplete hotkeys + } + if (!extraKeysCommands.blockComment) { + CM.keyMap.sublime["Shift-Ctrl-/"] = "blockComment"; + } - if (isWindowsOS) { - // "pcDefault" keymap on Windows should have F3/Shift-F3 - if (!extraKeysCommands.findNext) { - CM.keyMap.pcDefault["F3"] = "findNext"; - } - if (!extraKeysCommands.findPrev) { - CM.keyMap.pcDefault["Shift-F3"] = "findPrev"; - } + if (isWindowsOS) { + // "pcDefault" keymap on Windows should have F3/Shift-F3 + if (!extraKeysCommands.findNext) { + CM.keyMap.pcDefault["F3"] = "findNext"; + } + if (!extraKeysCommands.findPrev) { + CM.keyMap.pcDefault["Shift-F3"] = "findPrev"; + } - // try to remap non-interceptable Ctrl-(Shift-)N/T/W hotkeys - ["N", "T", "W"].forEach(function(char) { - [{from: "Ctrl-", to: ["Alt-", "Ctrl-Alt-"]}, - {from: "Shift-Ctrl-", to: ["Ctrl-Alt-", "Shift-Ctrl-Alt-"]} // Note: modifier order in CM is S-C-A - ].forEach(function(remap) { - var oldKey = remap.from + char; - Object.keys(CM.keyMap).forEach(function(keyMapName) { - var keyMap = CM.keyMap[keyMapName]; - var command = keyMap[oldKey]; - if (!command) { - return; - } - remap.to.some(function(newMod) { - var newKey = newMod + char; - if (!(newKey in keyMap)) { - delete keyMap[oldKey]; - keyMap[newKey] = command; - return true; - } - }); - }); - }); - }); - } + // try to remap non-interceptable Ctrl-(Shift-)N/T/W hotkeys + ["N", "T", "W"].forEach(function(char) { + [{from: "Ctrl-", to: ["Alt-", "Ctrl-Alt-"]}, + {from: "Shift-Ctrl-", to: ["Ctrl-Alt-", "Shift-Ctrl-Alt-"]} // Note: modifier order in CM is S-C-A + ].forEach(function(remap) { + var oldKey = remap.from + char; + Object.keys(CM.keyMap).forEach(function(keyMapName) { + var keyMap = CM.keyMap[keyMapName]; + var command = keyMap[oldKey]; + if (!command) { + return; + } + remap.to.some(function(newMod) { + var newKey = newMod + char; + if (!(newKey in keyMap)) { + delete keyMap[oldKey]; + keyMap[newKey] = command; + return true; + } + }); + }); + }); + }); + } - // user option values - CM.getOption = function (o) { - return CodeMirror.defaults[o]; - }; - CM.setOption = function (o, v) { - CodeMirror.defaults[o] = v; - editors.forEach(function(editor) { - editor.setOption(o, v); - }); - }; + // user option values + CM.getOption = function (o) { + return CodeMirror.defaults[o]; + }; + CM.setOption = function (o, v) { + CodeMirror.defaults[o] = v; + editors.forEach(function(editor) { + editor.setOption(o, v); + }); + }; - CM.prototype.getSection = function() { - return this.display.wrapper.parentNode; - }; + CM.prototype.getSection = function() { + return this.display.wrapper.parentNode; + }; - // initialize global editor controls - function optionsHtmlFromArray(options) { - return options.map(function(opt) { return ""; }).join(""); - } - var themeControl = document.getElementById("editor.theme"); - const themeList = localStorage.codeMirrorThemes; - if (themeList) { - themeControl.innerHTML = optionsHtmlFromArray(themeList.split(/\s+/)); - } else { - // Chrome is starting up and shows our edit.html, but the background page isn't loaded yet - const theme = prefs.get("editor.theme"); - themeControl.innerHTML = optionsHtmlFromArray([theme == "default" ? t("defaultTheme") : theme]); - getCodeMirrorThemes().then(() => { - const themes = (localStorage.codeMirrorThemes || '').split(/\s+/); - themeControl.innerHTML = optionsHtmlFromArray(themes); - themeControl.selectedIndex = Math.max(0, themes.indexOf(theme)); - }); - } - document.getElementById("editor.keyMap").innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort()); - document.getElementById("options").addEventListener("change", acmeEventListener, false); - setupLivePrefs(); + // initialize global editor controls + function optionsHtmlFromArray(options) { + return options.map(function(opt) { return ""; }).join(""); + } + var themeControl = document.getElementById("editor.theme"); + const themeList = localStorage.codeMirrorThemes; + if (themeList) { + themeControl.innerHTML = optionsHtmlFromArray(themeList.split(/\s+/)); + } else { + // Chrome is starting up and shows our edit.html, but the background page isn't loaded yet + const theme = prefs.get("editor.theme"); + themeControl.innerHTML = optionsHtmlFromArray([theme == "default" ? t("defaultTheme") : theme]); + getCodeMirrorThemes().then(() => { + const themes = (localStorage.codeMirrorThemes || '').split(/\s+/); + themeControl.innerHTML = optionsHtmlFromArray(themes); + themeControl.selectedIndex = Math.max(0, themes.indexOf(theme)); + }); + } + document.getElementById("editor.keyMap").innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort()); + document.getElementById("options").addEventListener("change", acmeEventListener, false); + setupLivePrefs(); - hotkeyRerouter.setState(true); + hotkeyRerouter.setState(true); } function acmeEventListener(event) { - var el = event.target; - var option = el.id.replace(/^editor\./, ''); - //console.log("acmeEventListener heard %s on %s", event.type, el.id); - if (!option) { - console.error("acmeEventListener: no 'cm_option' %O", el); - return; - } - var value = el.type == "checkbox" ? el.checked : el.value; - switch (option) { - case "tabSize": - CodeMirror.setOption("indentUnit", Number(value)); - break; - case "theme": - var themeLink = document.getElementById("cm-theme"); - // use non-localized "default" internally - if (!value || value == "default" || value == t("defaultTheme")) { - value = "default"; - if (prefs.get(el.id) != value) { - prefs.set(el.id, value); - } - themeLink.href = ""; - el.selectedIndex = 0; - break; - } - var url = chrome.runtime.getURL("vendor/codemirror/theme/" + value + ".css"); - if (themeLink.href == url) { // preloaded in initCodeMirror() - break; - } - // avoid flicker: wait for the second stylesheet to load, then apply the theme - document.head.insertAdjacentHTML("beforeend", - ''); - (function() { - setTimeout(function() { - CodeMirror.setOption(option, value); - themeLink.remove(); - document.getElementById("cm-theme2").id = "cm-theme"; - }, 100); - })(); - return; - case 'autocompleteOnTyping': - editors.forEach(cm => { - const onOff = el.checked ? 'on' : 'off'; - cm[onOff]('change', autocompleteOnTyping); - cm[onOff]('pick', autocompletePicked); - }); - return; - case "matchHighlight": - switch (value) { - case 'token': - case 'selection': - document.body.dataset[option] = value; - value = {showToken: value == 'token' && /[#.\-\w]/, annotateScrollbar: true}; - break; - default: - value = null; - } - } - CodeMirror.setOption(option, value); + var el = event.target; + var option = el.id.replace(/^editor\./, ''); + //console.log("acmeEventListener heard %s on %s", event.type, el.id); + if (!option) { + console.error("acmeEventListener: no 'cm_option' %O", el); + return; + } + var value = el.type == "checkbox" ? el.checked : el.value; + switch (option) { + case "tabSize": + CodeMirror.setOption("indentUnit", Number(value)); + break; + case "theme": + var themeLink = document.getElementById("cm-theme"); + // use non-localized "default" internally + if (!value || value == "default" || value == t("defaultTheme")) { + value = "default"; + if (prefs.get(el.id) != value) { + prefs.set(el.id, value); + } + themeLink.href = ""; + el.selectedIndex = 0; + break; + } + var url = chrome.runtime.getURL("vendor/codemirror/theme/" + value + ".css"); + if (themeLink.href == url) { // preloaded in initCodeMirror() + break; + } + // avoid flicker: wait for the second stylesheet to load, then apply the theme + document.head.insertAdjacentHTML("beforeend", + ''); + (function() { + setTimeout(function() { + CodeMirror.setOption(option, value); + themeLink.remove(); + document.getElementById("cm-theme2").id = "cm-theme"; + }, 100); + })(); + return; + case 'autocompleteOnTyping': + editors.forEach(cm => { + const onOff = el.checked ? 'on' : 'off'; + cm[onOff]('change', autocompleteOnTyping); + cm[onOff]('pick', autocompletePicked); + }); + return; + case "matchHighlight": + switch (value) { + case 'token': + case 'selection': + document.body.dataset[option] = value; + value = {showToken: value == 'token' && /[#.\-\w]/, annotateScrollbar: true}; + break; + default: + value = null; + } + } + CodeMirror.setOption(option, value); } // replace given textarea with the CodeMirror editor function setupCodeMirror(textarea, index) { - const cm = CodeMirror.fromTextArea(textarea, {lint: null}); - const wrapper = cm.display.wrapper; + const cm = CodeMirror.fromTextArea(textarea, {lint: null}); + const wrapper = cm.display.wrapper; - cm.on('change', indicateCodeChange); - if (prefs.get('editor.autocompleteOnTyping')) { - cm.on('change', autocompleteOnTyping); - cm.on('pick', autocompletePicked); - } - cm.on('blur', () => { - editors.lastActive = cm; - hotkeyRerouter.setState(true); - setTimeout(() => { - wrapper.classList.toggle('CodeMirror-active', wrapper.contains(document.activeElement)); - }); - }); - cm.on('focus', () => { - hotkeyRerouter.setState(false); - wrapper.classList.add('CodeMirror-active'); - }); - cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event)); + cm.on('change', indicateCodeChange); + if (prefs.get('editor.autocompleteOnTyping')) { + cm.on('change', autocompleteOnTyping); + cm.on('pick', autocompletePicked); + } + cm.on('blur', () => { + editors.lastActive = cm; + hotkeyRerouter.setState(true); + setTimeout(() => { + wrapper.classList.toggle('CodeMirror-active', wrapper.contains(document.activeElement)); + }); + }); + cm.on('focus', () => { + hotkeyRerouter.setState(false); + wrapper.classList.add('CodeMirror-active'); + }); + cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event)); - let lastClickTime = 0; - const resizeGrip = wrapper.appendChild(template.resizeGrip.cloneNode(true)); - resizeGrip.onmousedown = event => { - if (event.button != 0) { - return; - } - event.preventDefault(); - if (Date.now() - lastClickTime < 500) { - lastClickTime = 0; - toggleSectionHeight(cm); - return; - } - lastClickTime = Date.now(); - const minHeight = cm.defaultTextHeight() - + cm.display.lineDiv.offsetParent.offsetTop /* .CodeMirror-lines padding */ - + wrapper.offsetHeight - wrapper.clientHeight /* borders */; - wrapper.style.pointerEvents = 'none'; - document.body.style.cursor = 's-resize'; - function resize(e) { - const cmPageY = wrapper.getBoundingClientRect().top + window.scrollY; - const height = Math.max(minHeight, e.pageY - cmPageY); - if (height != wrapper.clientHeight) { - cm.setSize(null, height); - } - } - document.addEventListener('mousemove', resize); - document.addEventListener('mouseup', function resizeStop() { - document.removeEventListener('mouseup', resizeStop); - document.removeEventListener('mousemove', resize); - wrapper.style.pointerEvents = ''; - document.body.style.cursor = ''; - }); - }; + let lastClickTime = 0; + const resizeGrip = wrapper.appendChild(template.resizeGrip.cloneNode(true)); + resizeGrip.onmousedown = event => { + if (event.button != 0) { + return; + } + event.preventDefault(); + if (Date.now() - lastClickTime < 500) { + lastClickTime = 0; + toggleSectionHeight(cm); + return; + } + lastClickTime = Date.now(); + const minHeight = cm.defaultTextHeight() + + cm.display.lineDiv.offsetParent.offsetTop /* .CodeMirror-lines padding */ + + wrapper.offsetHeight - wrapper.clientHeight /* borders */; + wrapper.style.pointerEvents = 'none'; + document.body.style.cursor = 's-resize'; + function resize(e) { + const cmPageY = wrapper.getBoundingClientRect().top + window.scrollY; + const height = Math.max(minHeight, e.pageY - cmPageY); + if (height != wrapper.clientHeight) { + cm.setSize(null, height); + } + } + document.addEventListener('mousemove', resize); + document.addEventListener('mouseup', function resizeStop() { + document.removeEventListener('mouseup', resizeStop); + document.removeEventListener('mousemove', resize); + wrapper.style.pointerEvents = ''; + document.body.style.cursor = ''; + }); + }; - editors.splice(index || editors.length, 0, cm); - return cm; + editors.splice(index || editors.length, 0, cm); + return cm; } function indicateCodeChange(cm) { - var section = cm.getSection(); - setCleanItem(section, cm.isClean(section.savedValue)); - updateTitle(); - updateLintReport(cm); + var section = cm.getSection(); + setCleanItem(section, cm.isClean(section.savedValue)); + updateTitle(); + updateLintReport(cm); } function getSectionForChild(e) { - return e.closest("#sections > div"); + return e.closest("#sections > div"); } function getSections() { - return document.querySelectorAll("#sections > div"); + return document.querySelectorAll("#sections > div"); } // remind Chrome to repaint a previously invisible editor box by toggling any element's transform // this bug is present in some versions of Chrome (v37-40 or something) document.addEventListener("scroll", function(event) { - var style = document.getElementById("name").style; - style.webkitTransform = style.webkitTransform ? "" : "scale(1)"; + var style = document.getElementById("name").style; + style.webkitTransform = style.webkitTransform ? "" : "scale(1)"; }); // Shift-Ctrl-Wheel scrolls entire page even when mouse is over a code editor document.addEventListener("wheel", function(event) { - if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) { - // Chrome scrolls horizontally when Shift is pressed but on some PCs this might be different - window.scrollBy(0, event.deltaX || event.deltaY); - event.preventDefault(); - } + if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) { + // Chrome scrolls horizontally when Shift is pressed but on some PCs this might be different + window.scrollBy(0, event.deltaX || event.deltaY); + event.preventDefault(); + } }); queryTabs({currentWindow: true}).then(tabs => { - var windowId = tabs[0].windowId; - if (prefs.get("openEditInWindow")) { - if (sessionStorage.saveSizeOnClose - && 'left' in prefs.get('windowPosition', {}) - && !isWindowMaximized()) { - // window was reopened via Ctrl-Shift-T etc. - chrome.windows.update(windowId, prefs.get('windowPosition')); - } - if (tabs.length == 1 && window.history.length == 1) { - chrome.windows.getAll(function(windows) { - if (windows.length > 1) { - sessionStorageHash("saveSizeOnClose").set(windowId, true); - saveSizeOnClose = true; - } - }); - } else { - saveSizeOnClose = sessionStorageHash("saveSizeOnClose").value[windowId]; - } - } - chrome.tabs.onRemoved.addListener(function(tabId, info) { - sessionStorageHash("manageStylesHistory").unset(tabId); - if (info.windowId == windowId && info.isWindowClosing) { - sessionStorageHash("saveSizeOnClose").unset(windowId); - } - }); + var windowId = tabs[0].windowId; + if (prefs.get("openEditInWindow")) { + if (sessionStorage.saveSizeOnClose + && 'left' in prefs.get('windowPosition', {}) + && !isWindowMaximized()) { + // window was reopened via Ctrl-Shift-T etc. + chrome.windows.update(windowId, prefs.get('windowPosition')); + } + if (tabs.length == 1 && window.history.length == 1) { + chrome.windows.getAll(function(windows) { + if (windows.length > 1) { + sessionStorageHash("saveSizeOnClose").set(windowId, true); + saveSizeOnClose = true; + } + }); + } else { + saveSizeOnClose = sessionStorageHash("saveSizeOnClose").value[windowId]; + } + } + chrome.tabs.onRemoved.addListener(function(tabId, info) { + sessionStorageHash("manageStylesHistory").unset(tabId); + if (info.windowId == windowId && info.isWindowClosing) { + sessionStorageHash("saveSizeOnClose").unset(windowId); + } + }); }); getActiveTab().then(tab => { - useHistoryBack = sessionStorageHash("manageStylesHistory").value[tab.id] == location.href; + useHistoryBack = sessionStorageHash("manageStylesHistory").value[tab.id] == location.href; }); function goBackToManage(event) { - if (useHistoryBack) { - event.stopPropagation(); - event.preventDefault(); - history.back(); - } else if (styleId) { - sessionStorage.justEditedStyleId = styleId; - } + if (useHistoryBack) { + event.stopPropagation(); + event.preventDefault(); + history.back(); + } else if (styleId) { + sessionStorage.justEditedStyleId = styleId; + } } function isWindowMaximized() { - return window.screenLeft == 0 - && window.screenTop == 0 - && window.outerWidth == screen.availWidth - && window.outerHeight == screen.availHeight; + return window.screenLeft == 0 + && window.screenTop == 0 + && window.outerWidth == screen.availWidth + && window.outerHeight == screen.availHeight; } window.onbeforeunload = function() { - if (saveSizeOnClose && !isWindowMaximized()) { - prefs.set("windowPosition", { - left: screenLeft, - top: screenTop, - width: outerWidth, - height: outerHeight - }); - } - document.activeElement.blur(); - if (isCleanGlobal()) { - return; - } - updateLintReport(null, 0); - return confirm(t('styleChangesNotSaved')); + if (saveSizeOnClose && !isWindowMaximized()) { + prefs.set("windowPosition", { + left: screenLeft, + top: screenTop, + width: outerWidth, + height: outerHeight + }); + } + document.activeElement.blur(); + if (isCleanGlobal()) { + return; + } + updateLintReport(null, 0); + return confirm(t('styleChangesNotSaved')); }; function addAppliesTo(list, name, value) { - var showingEverything = list.querySelector(".applies-to-everything") != null; - // blow away "Everything" if it's there - if (showingEverything) { - list.removeChild(list.firstChild); - } - var e; - if (name && value) { - e = template.appliesTo.cloneNode(true); - e.querySelector("[name=applies-type]").value = name; - e.querySelector("[name=applies-value]").value = value; - e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); - } else if (showingEverything || list.hasChildNodes()) { - e = template.appliesTo.cloneNode(true); - if (list.hasChildNodes()) { - e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value; - } - e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); - } else { - e = template.appliesToEverything.cloneNode(true); - } - e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false); - list.appendChild(e); + var showingEverything = list.querySelector(".applies-to-everything") != null; + // blow away "Everything" if it's there + if (showingEverything) { + list.removeChild(list.firstChild); + } + var e; + if (name && value) { + e = template.appliesTo.cloneNode(true); + e.querySelector("[name=applies-type]").value = name; + e.querySelector("[name=applies-value]").value = value; + e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); + } else if (showingEverything || list.hasChildNodes()) { + e = template.appliesTo.cloneNode(true); + if (list.hasChildNodes()) { + e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value; + } + e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); + } else { + e = template.appliesToEverything.cloneNode(true); + } + e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false); + list.appendChild(e); } function addSection(event, section) { - var div = template.section.cloneNode(true); - div.querySelector(".applies-to-help").addEventListener("click", showAppliesToHelp, false); - div.querySelector(".remove-section").addEventListener("click", removeSection, false); - div.querySelector(".add-section").addEventListener("click", addSection, false); - div.querySelector(".beautify-section").addEventListener("click", beautify); + var div = template.section.cloneNode(true); + div.querySelector(".applies-to-help").addEventListener("click", showAppliesToHelp, false); + div.querySelector(".remove-section").addEventListener("click", removeSection, false); + div.querySelector(".add-section").addEventListener("click", addSection, false); + div.querySelector(".beautify-section").addEventListener("click", beautify); - var codeElement = div.querySelector(".code"); - var appliesTo = div.querySelector(".applies-to-list"); - var appliesToAdded = false; + var codeElement = div.querySelector(".code"); + var appliesTo = div.querySelector(".applies-to-list"); + var appliesToAdded = false; - if (section) { - codeElement.value = section.code; - for (var i in propertyToCss) { - if (section[i]) { - section[i].forEach(function(url) { - addAppliesTo(appliesTo, propertyToCss[i], url); - appliesToAdded = true; - }); - } - } - } - if (!appliesToAdded) { - addAppliesTo(appliesTo); - } + if (section) { + codeElement.value = section.code; + for (var i in propertyToCss) { + if (section[i]) { + section[i].forEach(function(url) { + addAppliesTo(appliesTo, propertyToCss[i], url); + appliesToAdded = true; + }); + } + } + } + if (!appliesToAdded) { + addAppliesTo(appliesTo); + } - appliesTo.addEventListener("change", onChange); - appliesTo.addEventListener("input", onChange); + appliesTo.addEventListener("change", onChange); + appliesTo.addEventListener("input", onChange); - toggleTestRegExpVisibility(); - appliesTo.addEventListener('change', toggleTestRegExpVisibility); - div.querySelector('.test-regexp').onclick = showRegExpTester; - function toggleTestRegExpVisibility() { - const show = [...appliesTo.children].some(item => - !item.matches('.applies-to-everything') && - item.querySelector('.applies-type').value == 'regexp' && - item.querySelector('.applies-value').value.trim()); - div.classList.toggle('has-regexp', show); - appliesTo.oninput = appliesTo.oninput || show && (event => { - if (event.target.matches('.applies-value') - && event.target.parentElement.querySelector('.applies-type').value == 'regexp') { - showRegExpTester(null, div); - } - }); - } + toggleTestRegExpVisibility(); + appliesTo.addEventListener('change', toggleTestRegExpVisibility); + div.querySelector('.test-regexp').onclick = showRegExpTester; + function toggleTestRegExpVisibility() { + const show = [...appliesTo.children].some(item => + !item.matches('.applies-to-everything') && + item.querySelector('.applies-type').value == 'regexp' && + item.querySelector('.applies-value').value.trim()); + div.classList.toggle('has-regexp', show); + appliesTo.oninput = appliesTo.oninput || show && (event => { + if (event.target.matches('.applies-value') + && event.target.parentElement.querySelector('.applies-type').value == 'regexp') { + showRegExpTester(null, div); + } + }); + } - var sections = document.getElementById("sections"); - if (event) { - var clickedSection = getSectionForChild(event.target); - sections.insertBefore(div, clickedSection.nextElementSibling); - var newIndex = getSections().indexOf(clickedSection) + 1; - var cm = setupCodeMirror(codeElement, newIndex); - makeSectionVisible(cm); - cm.focus() - renderLintReport(); - } else { - sections.appendChild(div); - var cm = setupCodeMirror(codeElement); - } + var sections = document.getElementById("sections"); + if (event) { + var clickedSection = getSectionForChild(event.target); + sections.insertBefore(div, clickedSection.nextElementSibling); + var newIndex = getSections().indexOf(clickedSection) + 1; + var cm = setupCodeMirror(codeElement, newIndex); + makeSectionVisible(cm); + cm.focus() + renderLintReport(); + } else { + sections.appendChild(div); + var cm = setupCodeMirror(codeElement); + } - div.CodeMirror = cm; - setCleanSection(div); - return div; + div.CodeMirror = cm; + setCleanSection(div); + return div; } function removeAppliesTo(event) { - var appliesTo = event.target.parentNode, - appliesToList = appliesTo.parentNode; - removeAreaAndSetDirty(appliesTo); - if (!appliesToList.hasChildNodes()) { - addAppliesTo(appliesToList); - } + var appliesTo = event.target.parentNode, + appliesToList = appliesTo.parentNode; + removeAreaAndSetDirty(appliesTo); + if (!appliesToList.hasChildNodes()) { + addAppliesTo(appliesToList); + } } function removeSection(event) { - var section = getSectionForChild(event.target); - var cm = section.CodeMirror; - removeAreaAndSetDirty(section); - editors.splice(editors.indexOf(cm), 1); - renderLintReport(); + var section = getSectionForChild(event.target); + var cm = section.CodeMirror; + removeAreaAndSetDirty(section); + editors.splice(editors.indexOf(cm), 1); + renderLintReport(); } function removeAreaAndSetDirty(area) { - var contributors = area.querySelectorAll('.style-contributor'); - if(!contributors.length){ - setCleanItem(area, false); - } - contributors.some(function(node) { - if (node.savedValue) { - // it's a saved section, so make it dirty and stop the enumeration - setCleanItem(area, false); - return true; - } else { - // it's an empty section, so undirty the applies-to items, - // otherwise orphaned ids would keep the style dirty - setCleanItem(node, true); - } - }); - updateTitle(); - area.parentNode.removeChild(area); + var contributors = area.querySelectorAll('.style-contributor'); + if(!contributors.length){ + setCleanItem(area, false); + } + contributors.some(function(node) { + if (node.savedValue) { + // it's a saved section, so make it dirty and stop the enumeration + setCleanItem(area, false); + return true; + } else { + // it's an empty section, so undirty the applies-to items, + // otherwise orphaned ids would keep the style dirty + setCleanItem(node, true); + } + }); + updateTitle(); + area.parentNode.removeChild(area); } function makeSectionVisible(cm) { - var section = cm.getSection(); - var bounds = section.getBoundingClientRect(); - if ((bounds.bottom > window.innerHeight && bounds.top > 0) || (bounds.top < 0 && bounds.bottom < window.innerHeight)) { - if (bounds.top < 0) { - window.scrollBy(0, bounds.top - 1); - } else { - window.scrollBy(0, bounds.bottom - window.innerHeight + 1); - } - } + var section = cm.getSection(); + var bounds = section.getBoundingClientRect(); + if ((bounds.bottom > window.innerHeight && bounds.top > 0) || (bounds.top < 0 && bounds.bottom < window.innerHeight)) { + if (bounds.top < 0) { + window.scrollBy(0, bounds.top - 1); + } else { + window.scrollBy(0, bounds.bottom - window.innerHeight + 1); + } + } } function setupGlobalSearch() { - var originalCommand = { - find: CodeMirror.commands.find, - findNext: CodeMirror.commands.findNext, - findPrev: CodeMirror.commands.findPrev, - replace: CodeMirror.commands.replace - }; - var originalOpenDialog = CodeMirror.prototype.openDialog; - var originalOpenConfirm = CodeMirror.prototype.openConfirm; + var originalCommand = { + find: CodeMirror.commands.find, + findNext: CodeMirror.commands.findNext, + findPrev: CodeMirror.commands.findPrev, + replace: CodeMirror.commands.replace + }; + var originalOpenDialog = CodeMirror.prototype.openDialog; + var originalOpenConfirm = CodeMirror.prototype.openConfirm; - var curState; // cm.state.search for last used 'find' + var curState; // cm.state.search for last used 'find' - function shouldIgnoreCase(query) { // treat all-lowercase non-regexp queries as case-insensitive - return typeof query == "string" && query == query.toLowerCase(); - } + function shouldIgnoreCase(query) { // treat all-lowercase non-regexp queries as case-insensitive + return typeof query == "string" && query == query.toLowerCase(); + } - function updateState(cm, newState) { - if (!newState) { - if (cm.state.search) { - return cm.state.search; - } - if (!curState) { - return null; - } - newState = curState; - } - cm.state.search = { - query: newState.query, - overlay: newState.overlay, - annotate: cm.showMatchesOnScrollbar(newState.query, shouldIgnoreCase(newState.query)) - } - cm.addOverlay(newState.overlay); - return cm.state.search; - } + function updateState(cm, newState) { + if (!newState) { + if (cm.state.search) { + return cm.state.search; + } + if (!curState) { + return null; + } + newState = curState; + } + cm.state.search = { + query: newState.query, + overlay: newState.overlay, + annotate: cm.showMatchesOnScrollbar(newState.query, shouldIgnoreCase(newState.query)) + } + cm.addOverlay(newState.overlay); + return cm.state.search; + } - // temporarily overrides the original openDialog with the provided template's innerHTML - function customizeOpenDialog(cm, template, callback) { - cm.openDialog = function(tmpl, cb, opt) { - // invoke 'callback' and bind 'this' to the original callback - originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt); - }; - setTimeout(function() { cm.openDialog = originalOpenDialog; }, 0); - refocusMinidialog(cm); - } + // temporarily overrides the original openDialog with the provided template's innerHTML + function customizeOpenDialog(cm, template, callback) { + cm.openDialog = function(tmpl, cb, opt) { + // invoke 'callback' and bind 'this' to the original callback + originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt); + }; + setTimeout(function() { cm.openDialog = originalOpenDialog; }, 0); + refocusMinidialog(cm); + } - function focusClosestCM(activeCM) { - editors.lastActive = activeCM; - var cm = getEditorInSight(); - if (cm != activeCM) { - cm.focus(); - } - return cm; + function focusClosestCM(activeCM) { + editors.lastActive = activeCM; + var cm = getEditorInSight(); + if (cm != activeCM) { + cm.focus(); + } + return cm; - } + } - function find(activeCM) { - activeCM = focusClosestCM(activeCM); - customizeOpenDialog(activeCM, template.find, function(query) { - this(query); - curState = activeCM.state.search; - if (editors.length == 1 || !curState.query) { - return; - } - editors.forEach(function(cm) { - if (cm != activeCM) { - cm.execCommand("clearSearch"); - updateState(cm, curState); - } - }); - if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) == 0) { - findNext(activeCM); - } - }); - originalCommand.find(activeCM); - } + function find(activeCM) { + activeCM = focusClosestCM(activeCM); + customizeOpenDialog(activeCM, template.find, function(query) { + this(query); + curState = activeCM.state.search; + if (editors.length == 1 || !curState.query) { + return; + } + editors.forEach(function(cm) { + if (cm != activeCM) { + cm.execCommand("clearSearch"); + updateState(cm, curState); + } + }); + if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) == 0) { + findNext(activeCM); + } + }); + originalCommand.find(activeCM); + } - function findNext(activeCM, reverse) { - var state = updateState(activeCM); - if (!state || !state.query) { - find(activeCM); - return; - } - var pos = activeCM.getCursor(reverse ? "from" : "to"); - activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor + function findNext(activeCM, reverse) { + var state = updateState(activeCM); + if (!state || !state.query) { + find(activeCM); + return; + } + var pos = activeCM.getCursor(reverse ? "from" : "to"); + activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor - var rxQuery = typeof state.query == "object" - ? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? "i" : ""); + var rxQuery = typeof state.query == "object" + ? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? "i" : ""); - if (document.activeElement && document.activeElement.name == "applies-value" - && searchAppliesTo(activeCM)) { - return; - } - for (var i=0, cm=activeCM; i < editors.length; i++) { - state = updateState(cm); - if (!cm.hasFocus()) { - pos = reverse ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(0, 0); - } - var searchCursor = cm.getSearchCursor(state.query, pos, shouldIgnoreCase(state.query)); - if (searchCursor.find(reverse)) { - if (editors.length > 1) { - makeSectionVisible(cm); - cm.focus(); - } - // speedup the original findNext - state.posFrom = reverse ? searchCursor.to() : searchCursor.from(); - state.posTo = CodeMirror.Pos(state.posFrom.line, state.posFrom.ch); - originalCommand[reverse ? "findPrev" : "findNext"](cm); - return; - } else if (!reverse && searchAppliesTo(cm)) { - return; - } - cm = editors[(editors.indexOf(cm) + (reverse ? -1 + editors.length : 1)) % editors.length]; - if (reverse && searchAppliesTo(cm)) { - return; - } - } - // nothing found so far, so call the original search with wrap-around - originalCommand[reverse ? "findPrev" : "findNext"](activeCM); + if (document.activeElement && document.activeElement.name == "applies-value" + && searchAppliesTo(activeCM)) { + return; + } + for (var i=0, cm=activeCM; i < editors.length; i++) { + state = updateState(cm); + if (!cm.hasFocus()) { + pos = reverse ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(0, 0); + } + var searchCursor = cm.getSearchCursor(state.query, pos, shouldIgnoreCase(state.query)); + if (searchCursor.find(reverse)) { + if (editors.length > 1) { + makeSectionVisible(cm); + cm.focus(); + } + // speedup the original findNext + state.posFrom = reverse ? searchCursor.to() : searchCursor.from(); + state.posTo = CodeMirror.Pos(state.posFrom.line, state.posFrom.ch); + originalCommand[reverse ? "findPrev" : "findNext"](cm); + return; + } else if (!reverse && searchAppliesTo(cm)) { + return; + } + cm = editors[(editors.indexOf(cm) + (reverse ? -1 + editors.length : 1)) % editors.length]; + if (reverse && searchAppliesTo(cm)) { + return; + } + } + // nothing found so far, so call the original search with wrap-around + originalCommand[reverse ? "findPrev" : "findNext"](activeCM); - function searchAppliesTo(cm) { - var inputs = [].slice.call(cm.getSection().querySelectorAll(".applies-value")); - if (reverse) { - inputs = inputs.reverse(); - } - inputs.splice(0, inputs.indexOf(document.activeElement) + 1); - return inputs.some(function(input) { - var match = rxQuery.exec(input.value); - if (match) { - input.focus(); - var end = match.index + match[0].length; - // scroll selected part into view in long inputs, - // works only outside of current event handlers chain, hence timeout=0 - setTimeout(function() { - input.setSelectionRange(end, end); - input.setSelectionRange(match.index, end) - }, 0); - return true; - } - }); - } - } + function searchAppliesTo(cm) { + var inputs = [].slice.call(cm.getSection().querySelectorAll(".applies-value")); + if (reverse) { + inputs = inputs.reverse(); + } + inputs.splice(0, inputs.indexOf(document.activeElement) + 1); + return inputs.some(function(input) { + var match = rxQuery.exec(input.value); + if (match) { + input.focus(); + var end = match.index + match[0].length; + // scroll selected part into view in long inputs, + // works only outside of current event handlers chain, hence timeout=0 + setTimeout(function() { + input.setSelectionRange(end, end); + input.setSelectionRange(match.index, end) + }, 0); + return true; + } + }); + } + } - function findPrev(cm) { - findNext(cm, true); - } + function findPrev(cm) { + findNext(cm, true); + } - function replace(activeCM, all) { - var queue, query, replacement; - activeCM = focusClosestCM(activeCM); - customizeOpenDialog(activeCM, template[all ? "replaceAll" : "replace"], function(txt) { - query = txt; - customizeOpenDialog(activeCM, template.replaceWith, function(txt) { - replacement = txt; - queue = editors.rotate(-editors.indexOf(activeCM)); - all ? editors.forEach(doReplace) : doReplace(); - }); - this(query); - }); - originalCommand.replace(activeCM, all); + function replace(activeCM, all) { + var queue, query, replacement; + activeCM = focusClosestCM(activeCM); + customizeOpenDialog(activeCM, template[all ? "replaceAll" : "replace"], function(txt) { + query = txt; + customizeOpenDialog(activeCM, template.replaceWith, function(txt) { + replacement = txt; + queue = editors.rotate(-editors.indexOf(activeCM)); + all ? editors.forEach(doReplace) : doReplace(); + }); + this(query); + }); + originalCommand.replace(activeCM, all); - function doReplace() { - var cm = queue.shift(); - if (!cm) { - if (!all) { - editors.lastActive.focus(); - } - return; - } - // hide the first two dialogs (replace, replaceWith) - cm.openDialog = function(tmpl, callback, opt) { - cm.openDialog = function(tmpl, callback, opt) { - cm.openDialog = originalOpenDialog; - if (all) { - callback(replacement); - } else { - doConfirm(cm); - callback(replacement); - if (!cm.getWrapperElement().querySelector(".CodeMirror-dialog")) { - // no dialog == nothing found in the current CM, move to the next - doReplace(); - } - } - }; - callback(query); - }; - originalCommand.replace(cm, all); - } - function doConfirm(cm) { - var wrapAround = false; - var origPos = cm.getCursor(); - cm.openConfirm = function overrideConfirm(tmpl, callbacks, opt) { - var ovrCallbacks = callbacks.map(function(callback) { - return function() { - makeSectionVisible(cm); - cm.openConfirm = overrideConfirm; - setTimeout(function() { cm.openConfirm = originalOpenConfirm; }, 0); + function doReplace() { + var cm = queue.shift(); + if (!cm) { + if (!all) { + editors.lastActive.focus(); + } + return; + } + // hide the first two dialogs (replace, replaceWith) + cm.openDialog = function(tmpl, callback, opt) { + cm.openDialog = function(tmpl, callback, opt) { + cm.openDialog = originalOpenDialog; + if (all) { + callback(replacement); + } else { + doConfirm(cm); + callback(replacement); + if (!cm.getWrapperElement().querySelector(".CodeMirror-dialog")) { + // no dialog == nothing found in the current CM, move to the next + doReplace(); + } + } + }; + callback(query); + }; + originalCommand.replace(cm, all); + } + function doConfirm(cm) { + var wrapAround = false; + var origPos = cm.getCursor(); + cm.openConfirm = function overrideConfirm(tmpl, callbacks, opt) { + var ovrCallbacks = callbacks.map(function(callback) { + return function() { + makeSectionVisible(cm); + cm.openConfirm = overrideConfirm; + setTimeout(function() { cm.openConfirm = originalOpenConfirm; }, 0); - var pos = cm.getCursor(); - callback(); - var cmp = CodeMirror.cmpPos(cm.getCursor(), pos); - wrapAround |= cmp <= 0; + var pos = cm.getCursor(); + callback(); + var cmp = CodeMirror.cmpPos(cm.getCursor(), pos); + wrapAround |= cmp <= 0; - var dlg = cm.getWrapperElement().querySelector(".CodeMirror-dialog"); - if (!dlg || cmp == 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) { - if (dlg) { - dlg.remove(); - } - doReplace(); - } - } - }); - originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt); - }; - } - } + var dlg = cm.getWrapperElement().querySelector(".CodeMirror-dialog"); + if (!dlg || cmp == 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) { + if (dlg) { + dlg.remove(); + } + doReplace(); + } + } + }); + originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt); + }; + } + } - function replaceAll(cm) { - replace(cm, true); - } + function replaceAll(cm) { + replace(cm, true); + } - CodeMirror.commands.find = find; - CodeMirror.commands.findNext = findNext; - CodeMirror.commands.findPrev = findPrev; - CodeMirror.commands.replace = replace; - CodeMirror.commands.replaceAll = replaceAll; + CodeMirror.commands.find = find; + CodeMirror.commands.findNext = findNext; + CodeMirror.commands.findPrev = findPrev; + CodeMirror.commands.replace = replace; + CodeMirror.commands.replaceAll = replaceAll; } function jumpToLine(cm) { - var cur = cm.getCursor(); - refocusMinidialog(cm); - cm.openDialog(template.jumpToLine.innerHTML, function(str) { - var m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/); - if (m) { - cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch); - } - }, {value: cur.line+1}); + var cur = cm.getCursor(); + refocusMinidialog(cm); + cm.openDialog(template.jumpToLine.innerHTML, function(str) { + var m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/); + if (m) { + cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch); + } + }, {value: cur.line+1}); } function toggleStyle() { - $('#enabled').checked = !$('#enabled').checked; - save(); + $('#enabled').checked = !$('#enabled').checked; + save(); } function toggleSectionHeight(cm) { - if (cm.state.toggleHeightSaved) { - // restore previous size - cm.setSize(null, cm.state.toggleHeightSaved); - cm.state.toggleHeightSaved = 0; - } else { - // maximize - const wrapper = cm.display.wrapper; - const allBounds = $('#sections').getBoundingClientRect(); - const pageExtrasHeight = allBounds.top + window.scrollY + - parseFloat(getComputedStyle($('#sections')).paddingBottom); - const sectionExtrasHeight = cm.getSection().clientHeight - wrapper.offsetHeight; - cm.state.toggleHeightSaved = wrapper.clientHeight; - cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight); - const bounds = cm.getSection().getBoundingClientRect(); - if (bounds.top < 0 || bounds.bottom > window.innerHeight) { - window.scrollBy(0, bounds.top); - } - } + if (cm.state.toggleHeightSaved) { + // restore previous size + cm.setSize(null, cm.state.toggleHeightSaved); + cm.state.toggleHeightSaved = 0; + } else { + // maximize + const wrapper = cm.display.wrapper; + const allBounds = $('#sections').getBoundingClientRect(); + const pageExtrasHeight = allBounds.top + window.scrollY + + parseFloat(getComputedStyle($('#sections')).paddingBottom); + const sectionExtrasHeight = cm.getSection().clientHeight - wrapper.offsetHeight; + cm.state.toggleHeightSaved = wrapper.clientHeight; + cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight); + const bounds = cm.getSection().getBoundingClientRect(); + if (bounds.top < 0 || bounds.bottom > window.innerHeight) { + window.scrollBy(0, bounds.top); + } + } } function autocompleteOnTyping(cm, info, debounced) { - if (cm.state.completionActive - || info.origin && !info.origin.includes('input') - || !info.text.last) { - return; - } - if (cm.state.autocompletePicked) { - cm.state.autocompletePicked = false; - return; - } - if (!debounced) { - debounce(autocompleteOnTyping, 100, cm, info, true); - return; - } - if (info.text.last.match(/[-\w!]+$/)) { - cm.state.autocompletePicked = false; - cm.options.hintOptions.completeSingle = false; - cm.execCommand('autocomplete'); - setTimeout(() => { - cm.options.hintOptions.completeSingle = true; - }); - } + if (cm.state.completionActive + || info.origin && !info.origin.includes('input') + || !info.text.last) { + return; + } + if (cm.state.autocompletePicked) { + cm.state.autocompletePicked = false; + return; + } + if (!debounced) { + debounce(autocompleteOnTyping, 100, cm, info, true); + return; + } + if (info.text.last.match(/[-\w!]+$/)) { + cm.state.autocompletePicked = false; + cm.options.hintOptions.completeSingle = false; + cm.execCommand('autocomplete'); + setTimeout(() => { + cm.options.hintOptions.completeSingle = true; + }); + } } function autocompletePicked(cm) { - cm.state.autocompletePicked = true; + cm.state.autocompletePicked = true; } function refocusMinidialog(cm) { - var section = cm.getSection(); - if (!section.querySelector(".CodeMirror-dialog")) { - return; - } - // close the currently opened minidialog - cm.focus(); - // make sure to focus the input in newly opened minidialog - setTimeout(function() { - section.querySelector(".CodeMirror-dialog").focus(); - }, 0); + var section = cm.getSection(); + if (!section.querySelector(".CodeMirror-dialog")) { + return; + } + // close the currently opened minidialog + cm.focus(); + // make sure to focus the input in newly opened minidialog + setTimeout(function() { + section.querySelector(".CodeMirror-dialog").focus(); + }, 0); } function nextPrevEditor(cm, direction) { - cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length]; - makeSectionVisible(cm); - cm.focus(); + cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length]; + makeSectionVisible(cm); + cm.focus(); } function getEditorInSight(nearbyElement) { - // priority: 1. associated CM for applies-to element 2. last active if visible 3. first visible - var cm; - if (nearbyElement && nearbyElement.className.indexOf("applies-") >= 0) { - cm = getSectionForChild(nearbyElement).CodeMirror; - } else { - cm = editors.lastActive; - } - if (!cm || offscreenDistance(cm) > 0) { - var sorted = editors - .map(function(cm, index) { return {cm: cm, distance: offscreenDistance(cm), index: index} }) - .sort(function(a, b) { return a.distance - b.distance || a.index - b.index }); - cm = sorted[0].cm; - if (sorted[0].distance > 0) { - makeSectionVisible(cm) - } - } - return cm; + // priority: 1. associated CM for applies-to element 2. last active if visible 3. first visible + var cm; + if (nearbyElement && nearbyElement.className.indexOf("applies-") >= 0) { + cm = getSectionForChild(nearbyElement).CodeMirror; + } else { + cm = editors.lastActive; + } + if (!cm || offscreenDistance(cm) > 0) { + var sorted = editors + .map(function(cm, index) { return {cm: cm, distance: offscreenDistance(cm), index: index} }) + .sort(function(a, b) { return a.distance - b.distance || a.index - b.index }); + cm = sorted[0].cm; + if (sorted[0].distance > 0) { + makeSectionVisible(cm) + } + } + return cm; - function offscreenDistance(cm) { - var LINES_VISIBLE = 2; // closest editor should have at least # lines visible - var bounds = cm.getSection().getBoundingClientRect(); - if (bounds.top < 0) { - return -bounds.top; - } else if (bounds.top < window.innerHeight - cm.defaultTextHeight() * LINES_VISIBLE) { - return 0; - } else { - return bounds.top - bounds.height; - } - } + function offscreenDistance(cm) { + var LINES_VISIBLE = 2; // closest editor should have at least # lines visible + var bounds = cm.getSection().getBoundingClientRect(); + if (bounds.top < 0) { + return -bounds.top; + } else if (bounds.top < window.innerHeight - cm.defaultTextHeight() * LINES_VISIBLE) { + return 0; + } else { + return bounds.top - bounds.height; + } + } } function updateLintReport(cm, delay) { - if (delay == 0) { - // immediately show pending csslint messages in onbeforeunload and save - update(cm); - return; - } - if (delay > 0) { - setTimeout(cm => { cm.performLint(); update(cm) }, delay, cm); - return; - } - var state = cm.state.lint; - if (!state) { - return; - } - // user is editing right now: postpone updating the report for the new issues (default: 500ms lint + 4500ms) - // or update it as soon as possible (default: 500ms lint + 100ms) in case an existing issue was just fixed - clearTimeout(state.reportTimeout); - state.reportTimeout = setTimeout(update, state.options.delay + 100, cm); - state.postponeNewIssues = delay == undefined || delay == null; + if (delay == 0) { + // immediately show pending csslint messages in onbeforeunload and save + update(cm); + return; + } + if (delay > 0) { + setTimeout(cm => { cm.performLint(); update(cm) }, delay, cm); + return; + } + var state = cm.state.lint; + if (!state) { + return; + } + // user is editing right now: postpone updating the report for the new issues (default: 500ms lint + 4500ms) + // or update it as soon as possible (default: 500ms lint + 100ms) in case an existing issue was just fixed + clearTimeout(state.reportTimeout); + state.reportTimeout = setTimeout(update, state.options.delay + 100, cm); + state.postponeNewIssues = delay == undefined || delay == null; - function update(cm) { - var scope = cm ? [cm] : editors; - var changed = false; - var fixedOldIssues = false; - scope.forEach(function(cm) { - var state = cm.state.lint || {}; - var oldMarkers = state.markedLast || {}; - var newMarkers = {}; - var html = !state.marked || state.marked.length == 0 ? "" : "" + - state.marked.map(function(mark) { - var info = mark.__annotation; - var isActiveLine = info.from.line == cm.getCursor().line; - var pos = isActiveLine ? "cursor" : (info.from.line + "," + info.from.ch); - var message = escapeHtml(info.message.replace(/ at line \d.+$/, "")); - if (message.length > 100) { - message = message.substr(0, 100) + "..."; - } - if (isActiveLine || oldMarkers[pos] == message) { - delete oldMarkers[pos]; - } - newMarkers[pos] = message; - return "" + - "" + - info.severity + "" + - "" + (info.from.line+1) + "" + - ":" + - "" + (info.from.ch+1) + "" + - "" + message + ""; - }).join("") + ""; - state.markedLast = newMarkers; - fixedOldIssues |= state.reportDisplayed && Object.keys(oldMarkers).length > 0; - if (state.html != html) { - state.html = html; - changed = true; - } - }); - if (changed) { - clearTimeout(state ? state.renderTimeout : undefined); - if (!state || !state.postponeNewIssues || fixedOldIssues) { - renderLintReport(true); - } else { - state.renderTimeout = setTimeout(function() { - renderLintReport(true); - }, CodeMirror.defaults.lintReportDelay); - } - } - } - function escapeHtml(html) { - var chars = {"&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/'}; - return html.replace(/[&<>"'\/]/g, function(char) { return chars[char] }); - } + function update(cm) { + var scope = cm ? [cm] : editors; + var changed = false; + var fixedOldIssues = false; + scope.forEach(function(cm) { + var state = cm.state.lint || {}; + var oldMarkers = state.markedLast || {}; + var newMarkers = {}; + var html = !state.marked || state.marked.length == 0 ? "" : "" + + state.marked.map(function(mark) { + var info = mark.__annotation; + var isActiveLine = info.from.line == cm.getCursor().line; + var pos = isActiveLine ? "cursor" : (info.from.line + "," + info.from.ch); + var message = escapeHtml(info.message.replace(/ at line \d.+$/, "")); + if (message.length > 100) { + message = message.substr(0, 100) + "..."; + } + if (isActiveLine || oldMarkers[pos] == message) { + delete oldMarkers[pos]; + } + newMarkers[pos] = message; + return "" + + "" + + info.severity + "" + + "" + (info.from.line+1) + "" + + ":" + + "" + (info.from.ch+1) + "" + + "" + message + ""; + }).join("") + ""; + state.markedLast = newMarkers; + fixedOldIssues |= state.reportDisplayed && Object.keys(oldMarkers).length > 0; + if (state.html != html) { + state.html = html; + changed = true; + } + }); + if (changed) { + clearTimeout(state ? state.renderTimeout : undefined); + if (!state || !state.postponeNewIssues || fixedOldIssues) { + renderLintReport(true); + } else { + state.renderTimeout = setTimeout(function() { + renderLintReport(true); + }, CodeMirror.defaults.lintReportDelay); + } + } + } + function escapeHtml(html) { + var chars = {"&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/'}; + return html.replace(/[&<>"'\/]/g, function(char) { return chars[char] }); + } } function renderLintReport(someBlockChanged) { - var container = document.getElementById("lint"); - var content = container.children[1]; - var label = t("sectionCode"); - var newContent = content.cloneNode(false); - var issueCount = 0; - editors.forEach(function(cm, index) { - if (cm.state.lint && cm.state.lint.html) { - var newBlock = newContent.appendChild(document.createElement("table")); - var html = "" + label + " " + (index+1) + "" + cm.state.lint.html; - newBlock.innerHTML = html; - newBlock.cm = cm; - issueCount += newBlock.rows.length; + var container = document.getElementById("lint"); + var content = container.children[1]; + var label = t("sectionCode"); + var newContent = content.cloneNode(false); + var issueCount = 0; + editors.forEach(function(cm, index) { + if (cm.state.lint && cm.state.lint.html) { + var newBlock = newContent.appendChild(document.createElement("table")); + var html = "" + label + " " + (index+1) + "" + cm.state.lint.html; + newBlock.innerHTML = html; + newBlock.cm = cm; + issueCount += newBlock.rows.length; - var block = content.children[newContent.children.length - 1]; - var blockChanged = !block || cm != block.cm || html != block.innerHTML; - someBlockChanged |= blockChanged; - cm.state.lint.reportDisplayed = blockChanged; - } - }); - if (someBlockChanged || newContent.children.length != content.children.length) { - document.getElementById('issue-count').textContent = issueCount; - container.replaceChild(newContent, content); - container.style.display = newContent.children.length ? "block" : "none"; - resizeLintReport(null, newContent); - } + var block = content.children[newContent.children.length - 1]; + var blockChanged = !block || cm != block.cm || html != block.innerHTML; + someBlockChanged |= blockChanged; + cm.state.lint.reportDisplayed = blockChanged; + } + }); + if (someBlockChanged || newContent.children.length != content.children.length) { + document.getElementById('issue-count').textContent = issueCount; + container.replaceChild(newContent, content); + container.style.display = newContent.children.length ? "block" : "none"; + resizeLintReport(null, newContent); + } } function resizeLintReport(event, content) { - content = content || document.getElementById("lint").children[1]; - if (content.children.length) { - var bounds = content.getBoundingClientRect(); - var newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + "px"; - if (newMaxHeight != content.style.maxHeight) { - content.style.maxHeight = newMaxHeight; - } - } + content = content || document.getElementById("lint").children[1]; + if (content.children.length) { + var bounds = content.getBoundingClientRect(); + var newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + "px"; + if (newMaxHeight != content.style.maxHeight) { + content.style.maxHeight = newMaxHeight; + } + } } function gotoLintIssue(event) { - var issue = event.target.closest("tr"); - if (!issue) { - return; - } - var block = issue.closest("table"); - makeSectionVisible(block.cm); - block.cm.focus(); - block.cm.setSelection({ - line: parseInt(issue.querySelector("td[role='line']").textContent) - 1, - ch: parseInt(issue.querySelector("td[role='col']").textContent) - 1 - }); + var issue = event.target.closest("tr"); + if (!issue) { + return; + } + var block = issue.closest("table"); + makeSectionVisible(block.cm); + block.cm.focus(); + block.cm.setSelection({ + line: parseInt(issue.querySelector("td[role='line']").textContent) - 1, + ch: parseInt(issue.querySelector("td[role='col']").textContent) - 1 + }); } function toggleLintReport() { - document.getElementById("lint").classList.toggle("collapsed"); + document.getElementById("lint").classList.toggle("collapsed"); } function beautify(event) { - if (exports.css_beautify) { // thanks to csslint's definition of 'exports' - doBeautify(); - } else { - var script = document.head.appendChild(document.createElement("script")); - script.src = "vendor-overwrites/beautify/beautify-css-mod.js"; - script.onload = doBeautify; - } - function doBeautify() { - var tabs = prefs.get("editor.indentWithTabs"); - var options = prefs.get("editor.beautify"); - options.indent_size = tabs ? 1 : prefs.get("editor.tabSize"); - options.indent_char = tabs ? "\t" : " "; + if (exports.css_beautify) { // thanks to csslint's definition of 'exports' + doBeautify(); + } else { + var script = document.head.appendChild(document.createElement("script")); + script.src = "vendor-overwrites/beautify/beautify-css-mod.js"; + script.onload = doBeautify; + } + function doBeautify() { + var tabs = prefs.get("editor.indentWithTabs"); + var options = prefs.get("editor.beautify"); + options.indent_size = tabs ? 1 : prefs.get("editor.tabSize"); + options.indent_char = tabs ? "\t" : " "; - var section = getSectionForChild(event.target); - var scope = section ? [section.CodeMirror] : editors; + var section = getSectionForChild(event.target); + var scope = section ? [section.CodeMirror] : editors; - showHelp(t("styleBeautify"), "
" + - optionHtml(".selector1,", "selector_separator_newline") + - optionHtml(".selector2,", "newline_before_open_brace") + - optionHtml("{", "newline_after_open_brace") + - optionHtml("border: none;", "newline_between_properties", true) + - optionHtml("display: block;", "newline_before_close_brace", true) + - optionHtml("}", "newline_between_rules") + - `' + - "
" + - "
"); + showHelp(t("styleBeautify"), "
" + + optionHtml(".selector1,", "selector_separator_newline") + + optionHtml(".selector2,", "newline_before_open_brace") + + optionHtml("{", "newline_after_open_brace") + + optionHtml("border: none;", "newline_between_properties", true) + + optionHtml("display: block;", "newline_before_close_brace", true) + + optionHtml("}", "newline_between_rules") + + `' + + "
" + + "
"); - var undoButton = document.querySelector("#help-popup button[role='undo']"); - undoButton.textContent = t(scope.length == 1 ? "undo" : "undoGlobal"); - undoButton.addEventListener("click", function() { - var undoable = false; - scope.forEach(function(cm) { - if (cm.beautifyChange && cm.beautifyChange[cm.changeGeneration()]) { - delete cm.beautifyChange[cm.changeGeneration()]; - cm.undo(); - cm.scrollIntoView(cm.getCursor()); - undoable |= cm.beautifyChange[cm.changeGeneration()]; - } - }); - undoButton.disabled = !undoable; - }); + var undoButton = document.querySelector("#help-popup button[role='undo']"); + undoButton.textContent = t(scope.length == 1 ? "undo" : "undoGlobal"); + undoButton.addEventListener("click", function() { + var undoable = false; + scope.forEach(function(cm) { + if (cm.beautifyChange && cm.beautifyChange[cm.changeGeneration()]) { + delete cm.beautifyChange[cm.changeGeneration()]; + cm.undo(); + cm.scrollIntoView(cm.getCursor()); + undoable |= cm.beautifyChange[cm.changeGeneration()]; + } + }); + undoButton.disabled = !undoable; + }); - scope.forEach(function(cm) { - setTimeout(function() { - const pos = options.translate_positions = - [].concat.apply([], cm.doc.sel.ranges.map(r => - [Object.assign({}, r.anchor), Object.assign({}, r.head)])); - var text = cm.getValue(); - var newText = exports.css_beautify(text, options); - if (newText != text) { - if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) { - // clear the list if last change wasn't a css-beautify - cm.beautifyChange = {}; - } - cm.setValue(newText); - const selections = []; - for (let i = 0; i < pos.length; i += 2) { - selections.push({anchor: pos[i], head: pos[i + 1]}); - } - cm.setSelections(selections); - cm.beautifyChange[cm.changeGeneration()] = true; - undoButton.disabled = false; - } - }, 0); - }); + scope.forEach(function(cm) { + setTimeout(function() { + const pos = options.translate_positions = + [].concat.apply([], cm.doc.sel.ranges.map(r => + [Object.assign({}, r.anchor), Object.assign({}, r.head)])); + var text = cm.getValue(); + var newText = exports.css_beautify(text, options); + if (newText != text) { + if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) { + // clear the list if last change wasn't a css-beautify + cm.beautifyChange = {}; + } + cm.setValue(newText); + const selections = []; + for (let i = 0; i < pos.length; i += 2) { + selections.push({anchor: pos[i], head: pos[i + 1]}); + } + cm.setSelections(selections); + cm.beautifyChange[cm.changeGeneration()] = true; + undoButton.disabled = false; + } + }, 0); + }); - document.querySelector('.beautify-options').onchange = ({target}) => { - const value = target.type == 'checkbox' ? target.checked : target.selectedIndex > 0; - prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value})); - if (target.parentNode.hasAttribute('newline')) { - target.parentNode.setAttribute('newline', value.toString()); - } - doBeautify(); - }; + document.querySelector('.beautify-options').onchange = ({target}) => { + const value = target.type == 'checkbox' ? target.checked : target.selectedIndex > 0; + prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value})); + if (target.parentNode.hasAttribute('newline')) { + target.parentNode.setAttribute('newline', value.toString()); + } + doBeautify(); + }; - function optionHtml(label, optionName, indent) { - var value = options[optionName]; - return "
" + - "" + label + "" + - "
"; - } - } + function optionHtml(label, optionName, indent) { + var value = options[optionName]; + return "
" + + "" + label + "" + + "
"; + } + } } document.addEventListener("DOMContentLoaded", init); function init() { - initCodeMirror(); - var params = getParams(); - if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses - // This is an add - tE("heading", "addStyleTitle"); - var section = {code: ""} - for (var i in CssToProperty) { - if (params[i]) { - section[CssToProperty[i]] = [params[i]]; - } - } - window.onload = () => { - window.onload = null; - addSection(null, section); - editors[0].setOption('lint', CodeMirror.defaults.lint); - // default to enabled - document.getElementById("enabled").checked = true - initHooks(); - }; - return; - } - // This is an edit - tE("heading", "editStyleHeading", null, false); - getStylesSafe({id: params.id}).then(styles => { - let style = styles[0]; - if (!style) { - style = {id: null, sections: []}; - history.replaceState({}, document.title, location.pathname); - } - styleId = style.id; - setStyleMeta(style); - window.onload = () => { - window.onload = null; - initWithStyle({style}); - }; - if (document.readyState != 'loading') { - window.onload(); - } - }); + initCodeMirror(); + var params = getParams(); + if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses + // This is an add + tE("heading", "addStyleTitle"); + var section = {code: ""} + for (var i in CssToProperty) { + if (params[i]) { + section[CssToProperty[i]] = [params[i]]; + } + } + window.onload = () => { + window.onload = null; + addSection(null, section); + editors[0].setOption('lint', CodeMirror.defaults.lint); + // default to enabled + document.getElementById("enabled").checked = true + initHooks(); + }; + return; + } + // This is an edit + tE("heading", "editStyleHeading", null, false); + getStylesSafe({id: params.id}).then(styles => { + let style = styles[0]; + if (!style) { + style = {id: null, sections: []}; + history.replaceState({}, document.title, location.pathname); + } + styleId = style.id; + setStyleMeta(style); + window.onload = () => { + window.onload = null; + initWithStyle({style}); + }; + if (document.readyState != 'loading') { + window.onload(); + } + }); } function setStyleMeta(style) { - document.getElementById("name").value = style.name; - document.getElementById("enabled").checked = style.enabled; - document.getElementById("url").href = style.url; + document.getElementById("name").value = style.name; + document.getElementById("enabled").checked = style.enabled; + document.getElementById("url").href = style.url; } function initWithStyle({style, codeIsUpdated}) { - setStyleMeta(style); + setStyleMeta(style); - if (codeIsUpdated === false) { - setCleanGlobal(); - updateTitle(); - return; - } + if (codeIsUpdated === false) { + setCleanGlobal(); + updateTitle(); + return; + } - // if this was done in response to an update, we need to clear existing sections - getSections().forEach(function(div) { div.remove(); }); - var queue = style.sections.length ? style.sections.slice() : [{code: ""}]; - var queueStart = new Date().getTime(); - // after 100ms the sections will be added asynchronously - while (new Date().getTime() - queueStart <= 100 && queue.length) { - add(); - } - (function processQueue() { - if (queue.length) { - add(); - setTimeout(processQueue, 0); - } - })(); - initHooks(); + // if this was done in response to an update, we need to clear existing sections + getSections().forEach(function(div) { div.remove(); }); + var queue = style.sections.length ? style.sections.slice() : [{code: ""}]; + var queueStart = new Date().getTime(); + // after 100ms the sections will be added asynchronously + while (new Date().getTime() - queueStart <= 100 && queue.length) { + add(); + } + (function processQueue() { + if (queue.length) { + add(); + setTimeout(processQueue, 0); + } + })(); + initHooks(); - function add() { - var sectionDiv = addSection(null, queue.shift()); - maximizeCodeHeight(sectionDiv, !queue.length); - const cm = sectionDiv.CodeMirror; - setTimeout(() => { - cm.setOption('lint', CodeMirror.defaults.lint); - updateLintReport(cm, 0); - }, prefs.get("editor.lintDelay")); - } + function add() { + var sectionDiv = addSection(null, queue.shift()); + maximizeCodeHeight(sectionDiv, !queue.length); + const cm = sectionDiv.CodeMirror; + setTimeout(() => { + cm.setOption('lint', CodeMirror.defaults.lint); + updateLintReport(cm, 0); + }, prefs.get("editor.lintDelay")); + } } function initHooks() { - document.querySelectorAll("#header .style-contributor").forEach(function(node) { - node.addEventListener("change", onChange); - node.addEventListener("input", onChange); - }); - document.getElementById("toggle-style-help").addEventListener("click", showToggleStyleHelp); - document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false); - document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false); - document.getElementById("from-mozilla").addEventListener("click", fromMozillaFormat); - document.getElementById("beautify").addEventListener("click", beautify); - document.getElementById("save-button").addEventListener("click", save, false); - document.getElementById("sections-help").addEventListener("click", showSectionHelp, false); - document.getElementById("keyMap-help").addEventListener("click", showKeyMapHelp, false); - document.getElementById("cancel-button").addEventListener("click", goBackToManage); - document.getElementById("lint-help").addEventListener("click", showLintHelp); - document.getElementById("lint").addEventListener("click", gotoLintIssue); - window.addEventListener("resize", resizeLintReport); + document.querySelectorAll("#header .style-contributor").forEach(function(node) { + node.addEventListener("change", onChange); + node.addEventListener("input", onChange); + }); + document.getElementById("toggle-style-help").addEventListener("click", showToggleStyleHelp); + document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false); + document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false); + document.getElementById("from-mozilla").addEventListener("click", fromMozillaFormat); + document.getElementById("beautify").addEventListener("click", beautify); + document.getElementById("save-button").addEventListener("click", save, false); + document.getElementById("sections-help").addEventListener("click", showSectionHelp, false); + document.getElementById("keyMap-help").addEventListener("click", showKeyMapHelp, false); + document.getElementById("cancel-button").addEventListener("click", goBackToManage); + document.getElementById("lint-help").addEventListener("click", showLintHelp); + document.getElementById("lint").addEventListener("click", gotoLintIssue); + window.addEventListener("resize", resizeLintReport); - // touch devices don't have onHover events so the element we'll be toggled via clicking (touching) - if ("ontouchstart" in document.body) { - document.querySelector("#lint h2").addEventListener("click", toggleLintReport); - } + // touch devices don't have onHover events so the element we'll be toggled via clicking (touching) + if ("ontouchstart" in document.body) { + document.querySelector("#lint h2").addEventListener("click", toggleLintReport); + } - document.querySelectorAll( - 'input:not([type]), input[type="text"], input[type="search"], input[type="number"]') - .forEach(e => e.addEventListener('mousedown', toggleContextMenuDelete)); + document.querySelectorAll( + 'input:not([type]), input[type="text"], input[type="search"], input[type="number"]') + .forEach(e => e.addEventListener('mousedown', toggleContextMenuDelete)); - setupGlobalSearch(); - setCleanGlobal(); - updateTitle(); + setupGlobalSearch(); + setCleanGlobal(); + updateTitle(); } function toggleContextMenuDelete(event) { - if (event.button == 2 && prefs.get('editor.contextDelete')) { - chrome.contextMenus.update('editor.contextDelete', { - enabled: Boolean( - this.selectionStart != this.selectionEnd || - this.somethingSelected && this.somethingSelected() - ), - }, ignoreChromeError); - } + if (event.button == 2 && prefs.get('editor.contextDelete')) { + chrome.contextMenus.update('editor.contextDelete', { + enabled: Boolean( + this.selectionStart != this.selectionEnd || + this.somethingSelected && this.somethingSelected() + ), + }, ignoreChromeError); + } } function maximizeCodeHeight(sectionDiv, isLast) { - var cm = sectionDiv.CodeMirror; - var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []}; - if (!stats.cmActualHeight) { - stats.cmActualHeight = getComputedHeight(cm.display.wrapper); - } - if (!stats.sectionMarginTop) { - stats.sectionMarginTop = parseFloat(getComputedStyle(sectionDiv).marginTop); - } - var sectionTop = sectionDiv.getBoundingClientRect().top - stats.sectionMarginTop; - if (!stats.firstSectionTop) { - stats.firstSectionTop = sectionTop; - } - var extrasHeight = getComputedHeight(sectionDiv) - stats.cmActualHeight; - var cmMaxHeight = window.innerHeight - extrasHeight - sectionTop - stats.sectionMarginTop; - var cmDesiredHeight = cm.display.sizer.clientHeight + 2*cm.defaultTextHeight(); - var cmGrantableHeight = Math.max(stats.cmActualHeight, Math.min(cmMaxHeight, cmDesiredHeight)); - stats.deltas.push(cmGrantableHeight - stats.cmActualHeight); - stats.totalHeight += cmGrantableHeight + extrasHeight; - if (!isLast) { - return; - } - stats.totalHeight += stats.firstSectionTop; - if (stats.totalHeight <= window.innerHeight) { - editors.forEach(function(cm, index) { - cm.setSize(null, stats.deltas[index] + stats.cmActualHeight); - }); - return; - } - // scale heights to fill the gap between last section and bottom edge of the window - var sections = document.getElementById("sections"); - var available = window.innerHeight - sections.getBoundingClientRect().bottom - - parseFloat(getComputedStyle(sections).marginBottom); - if (available <= 0) { - return; - } - var totalDelta = stats.deltas.reduce(function(sum, d) { return sum + d; }, 0); - var q = available / totalDelta; - var baseHeight = stats.cmActualHeight - stats.sectionMarginTop; - stats.deltas.forEach(function(delta, index) { - editors[index].setSize(null, baseHeight + Math.floor(q * delta)); - }); + var cm = sectionDiv.CodeMirror; + var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []}; + if (!stats.cmActualHeight) { + stats.cmActualHeight = getComputedHeight(cm.display.wrapper); + } + if (!stats.sectionMarginTop) { + stats.sectionMarginTop = parseFloat(getComputedStyle(sectionDiv).marginTop); + } + var sectionTop = sectionDiv.getBoundingClientRect().top - stats.sectionMarginTop; + if (!stats.firstSectionTop) { + stats.firstSectionTop = sectionTop; + } + var extrasHeight = getComputedHeight(sectionDiv) - stats.cmActualHeight; + var cmMaxHeight = window.innerHeight - extrasHeight - sectionTop - stats.sectionMarginTop; + var cmDesiredHeight = cm.display.sizer.clientHeight + 2*cm.defaultTextHeight(); + var cmGrantableHeight = Math.max(stats.cmActualHeight, Math.min(cmMaxHeight, cmDesiredHeight)); + stats.deltas.push(cmGrantableHeight - stats.cmActualHeight); + stats.totalHeight += cmGrantableHeight + extrasHeight; + if (!isLast) { + return; + } + stats.totalHeight += stats.firstSectionTop; + if (stats.totalHeight <= window.innerHeight) { + editors.forEach(function(cm, index) { + cm.setSize(null, stats.deltas[index] + stats.cmActualHeight); + }); + return; + } + // scale heights to fill the gap between last section and bottom edge of the window + var sections = document.getElementById("sections"); + var available = window.innerHeight - sections.getBoundingClientRect().bottom - + parseFloat(getComputedStyle(sections).marginBottom); + if (available <= 0) { + return; + } + var totalDelta = stats.deltas.reduce(function(sum, d) { return sum + d; }, 0); + var q = available / totalDelta; + var baseHeight = stats.cmActualHeight - stats.sectionMarginTop; + stats.deltas.forEach(function(delta, index) { + editors[index].setSize(null, baseHeight + Math.floor(q * delta)); + }); } function updateTitle() { - var DIRTY_TITLE = "* $"; + var DIRTY_TITLE = "* $"; - var name = document.getElementById("name").savedValue; - var clean = isCleanGlobal(); - var title = styleId === null ? t("addStyleTitle") : t('editStyleTitle', [name]); - document.title = clean ? title : DIRTY_TITLE.replace("$", title); + var name = document.getElementById("name").savedValue; + var clean = isCleanGlobal(); + var title = styleId === null ? t("addStyleTitle") : t('editStyleTitle', [name]); + document.title = clean ? title : DIRTY_TITLE.replace("$", title); } function validate() { - var name = document.getElementById("name").value; - if (name == "") { - return t("styleMissingName"); - } - // validate the regexps - if (document.querySelectorAll(".applies-to-list").some(function(list) { - return list.childNodes.some(function(li) { - if (li.className == template.appliesToEverything.className) { - return false; - } - var valueElement = li.querySelector("[name=applies-value]"); - var type = li.querySelector("[name=applies-type]").value; - var value = valueElement.value; - if (type && value) { - if (type == "regexp") { - try { - new RegExp(value); - } catch (ex) { - valueElement.focus(); - return true; - } - } - } - return false; - }); - })) { - return t("styleBadRegexp"); - } - return null; + var name = document.getElementById("name").value; + if (name == "") { + return t("styleMissingName"); + } + // validate the regexps + if (document.querySelectorAll(".applies-to-list").some(function(list) { + return list.childNodes.some(function(li) { + if (li.className == template.appliesToEverything.className) { + return false; + } + var valueElement = li.querySelector("[name=applies-value]"); + var type = li.querySelector("[name=applies-type]").value; + var value = valueElement.value; + if (type && value) { + if (type == "regexp") { + try { + new RegExp(value); + } catch (ex) { + valueElement.focus(); + return true; + } + } + } + return false; + }); + })) { + return t("styleBadRegexp"); + } + return null; } function save() { - updateLintReport(null, 0); + updateLintReport(null, 0); - // save the contents of the CodeMirror editors back into the textareas - for (var i=0; i < editors.length; i++) { - editors[i].save(); - } + // save the contents of the CodeMirror editors back into the textareas + for (var i=0; i < editors.length; i++) { + editors[i].save(); + } - var error = validate(); - if (error) { - alert(error); - return; - } - var name = document.getElementById("name").value; - var enabled = document.getElementById("enabled").checked; - saveStyleSafe({ - id: styleId, - name: name, - enabled: enabled, - reason: 'editSave', - sections: getSectionsHashes() - }) - .then(saveComplete); + var error = validate(); + if (error) { + alert(error); + return; + } + var name = document.getElementById("name").value; + var enabled = document.getElementById("enabled").checked; + saveStyleSafe({ + id: styleId, + name: name, + enabled: enabled, + reason: 'editSave', + sections: getSectionsHashes() + }) + .then(saveComplete); } function getSectionsHashes() { - var sections = []; - getSections().forEach(function(div) { - var meta = getMeta(div); - var code = div.CodeMirror.getValue(); - if (/^\s*$/.test(code) && Object.keys(meta).length == 0) { - return; - } - meta.code = code; - sections.push(meta); - }); - return sections; + var sections = []; + getSections().forEach(function(div) { + var meta = getMeta(div); + var code = div.CodeMirror.getValue(); + if (/^\s*$/.test(code) && Object.keys(meta).length == 0) { + return; + } + meta.code = code; + sections.push(meta); + }); + return sections; } function getMeta(e) { - var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []}; - e.querySelector(".applies-to-list").childNodes.forEach(function(li) { - if (li.className == template.appliesToEverything.className) { - return; - } - var type = li.querySelector("[name=applies-type]").value; - var value = li.querySelector("[name=applies-value]").value; - if (type && value) { - var property = CssToProperty[type]; - meta[property].push(value); - } - }); - return meta; + var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []}; + e.querySelector(".applies-to-list").childNodes.forEach(function(li) { + if (li.className == template.appliesToEverything.className) { + return; + } + var type = li.querySelector("[name=applies-type]").value; + var value = li.querySelector("[name=applies-value]").value; + if (type && value) { + var property = CssToProperty[type]; + meta[property].push(value); + } + }); + return meta; } function saveComplete(style) { - styleId = style.id; - setCleanGlobal(); + styleId = style.id; + setCleanGlobal(); - // Go from new style URL to edit style URL - if (location.href.indexOf("id=") == -1) { - history.replaceState({}, document.title, "edit.html?id=" + style.id); - tE("heading", "editStyleHeading", null, false); - } - updateTitle(); + // Go from new style URL to edit style URL + if (location.href.indexOf("id=") == -1) { + history.replaceState({}, document.title, "edit.html?id=" + style.id); + tE("heading", "editStyleHeading", null, false); + } + updateTitle(); } function showMozillaFormat() { - var popup = showCodeMirrorPopup(t("styleToMozillaFormatTitle"), "", {readOnly: true}); - popup.codebox.setValue(toMozillaFormat()); - popup.codebox.execCommand("selectAll"); + var popup = showCodeMirrorPopup(t("styleToMozillaFormatTitle"), "", {readOnly: true}); + popup.codebox.setValue(toMozillaFormat()); + popup.codebox.execCommand("selectAll"); } function toMozillaFormat() { - return getSectionsHashes().map(function(section) { - var cssMds = []; - for (var i in propertyToCss) { - if (section[i]) { - cssMds = cssMds.concat(section[i].map(function (v){ - return propertyToCss[i] + "(\"" + v.replace(/\\/g, "\\\\") + "\")"; - })); - } - } - return cssMds.length ? "@-moz-document " + cssMds.join(", ") + " {\n" + section.code + "\n}" : section.code; - }).join("\n\n"); + return getSectionsHashes().map(function(section) { + var cssMds = []; + for (var i in propertyToCss) { + if (section[i]) { + cssMds = cssMds.concat(section[i].map(function (v){ + return propertyToCss[i] + "(\"" + v.replace(/\\/g, "\\\\") + "\")"; + })); + } + } + return cssMds.length ? "@-moz-document " + cssMds.join(", ") + " {\n" + section.code + "\n}" : section.code; + }).join("\n\n"); } function fromMozillaFormat() { - var popup = showCodeMirrorPopup(t("styleFromMozillaFormatPrompt"), tHTML("
\ - \ - \ -
").innerHTML); + var popup = showCodeMirrorPopup(t("styleFromMozillaFormatPrompt"), tHTML("
\ + \ + \ +
").innerHTML); - var contents = popup.querySelector(".contents"); - contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild); - popup.codebox.focus(); + var contents = popup.querySelector(".contents"); + contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild); + popup.codebox.focus(); - popup.querySelector("[name='import-append']").addEventListener("click", doImport); - popup.querySelector("[name='import-replace']").addEventListener("click", doImport); + popup.querySelector("[name='import-append']").addEventListener("click", doImport); + popup.querySelector("[name='import-replace']").addEventListener("click", doImport); - popup.codebox.on("change", function() { - clearTimeout(popup.mozillaTimeout); - popup.mozillaTimeout = setTimeout(function() { - popup.classList.toggle("ready", trimNewLines(popup.codebox.getValue())); - }, 100); - }); + popup.codebox.on("change", function() { + clearTimeout(popup.mozillaTimeout); + popup.mozillaTimeout = setTimeout(function() { + popup.classList.toggle("ready", trimNewLines(popup.codebox.getValue())); + }, 100); + }); - function doImport() { - var replaceOldStyle = this.name == "import-replace"; - popup.querySelector(".dismiss").onclick(); - var mozStyle = trimNewLines(popup.codebox.getValue()); - var parser = new parserlib.css.Parser(), lines = mozStyle.split("\n"); - var sectionStack = [{code: "", start: {line: 1, col: 1}}]; - var errors = "", oldSectionCount = editors.length; - var firstAddedCM; + function doImport() { + var replaceOldStyle = this.name == "import-replace"; + popup.querySelector(".dismiss").onclick(); + var mozStyle = trimNewLines(popup.codebox.getValue()); + var parser = new parserlib.css.Parser(), lines = mozStyle.split("\n"); + var sectionStack = [{code: "", start: {line: 1, col: 1}}]; + var errors = "", oldSectionCount = editors.length; + var firstAddedCM; - parser.addListener("startdocument", function(e) { - var outerText = getRange(sectionStack.last.start, (--e.col, e)); - var gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/); - var section = {code: "", start: backtrackTo(this, parserlib.css.Tokens.LBRACE, "end")}; - // move last comment before @-moz-document inside the section - if (gapComment && !gapComment[1].match(/\/\*\s*AGENT_SHEET\s*\*\//)) { - section.code = gapComment[1] + "\n"; - outerText = trimNewLines(outerText.substring(0, gapComment.index)); - } - if (outerText.trim()) { - sectionStack.last.code = outerText; - doAddSection(sectionStack.last); - sectionStack.last.code = ""; - } - e.functions.forEach(function(f) { - var m = f.match(/^(url|url-prefix|domain|regexp)\((['"]?)(.+?)\2?\)$/); - var aType = CssToProperty[m[1]]; - var aValue = aType != "regexps" ? m[3] : m[3].replace(/\\\\/g, "\\"); - (section[aType] = section[aType] || []).push(aValue); - }); - sectionStack.push(section); - }); + parser.addListener("startdocument", function(e) { + var outerText = getRange(sectionStack.last.start, (--e.col, e)); + var gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/); + var section = {code: "", start: backtrackTo(this, parserlib.css.Tokens.LBRACE, "end")}; + // move last comment before @-moz-document inside the section + if (gapComment && !gapComment[1].match(/\/\*\s*AGENT_SHEET\s*\*\//)) { + section.code = gapComment[1] + "\n"; + outerText = trimNewLines(outerText.substring(0, gapComment.index)); + } + if (outerText.trim()) { + sectionStack.last.code = outerText; + doAddSection(sectionStack.last); + sectionStack.last.code = ""; + } + e.functions.forEach(function(f) { + var m = f.match(/^(url|url-prefix|domain|regexp)\((['"]?)(.+?)\2?\)$/); + var aType = CssToProperty[m[1]]; + var aValue = aType != "regexps" ? m[3] : m[3].replace(/\\\\/g, "\\"); + (section[aType] = section[aType] || []).push(aValue); + }); + sectionStack.push(section); + }); - parser.addListener("enddocument", function(e) { - var end = backtrackTo(this, parserlib.css.Tokens.RBRACE, "start"); - var section = sectionStack.pop(); - section.code += getRange(section.start, end); - sectionStack.last.start = (++end.col, end); - doAddSection(section); - }); + parser.addListener("enddocument", function(e) { + var end = backtrackTo(this, parserlib.css.Tokens.RBRACE, "start"); + var section = sectionStack.pop(); + section.code += getRange(section.start, end); + sectionStack.last.start = (++end.col, end); + doAddSection(section); + }); - parser.addListener("endstylesheet", function() { - // add nonclosed outer sections (either broken or the last global one) - var endOfText = {line: lines.length, col: lines.last.length + 1}; - sectionStack.last.code += getRange(sectionStack.last.start, endOfText); - sectionStack.forEach(doAddSection); + parser.addListener("endstylesheet", function() { + // add nonclosed outer sections (either broken or the last global one) + var endOfText = {line: lines.length, col: lines.last.length + 1}; + sectionStack.last.code += getRange(sectionStack.last.start, endOfText); + sectionStack.forEach(doAddSection); - delete maximizeCodeHeight.stats; - editors.forEach(function(cm) { - maximizeCodeHeight(cm.getSection(), cm == editors.last); - }); + delete maximizeCodeHeight.stats; + editors.forEach(function(cm) { + maximizeCodeHeight(cm.getSection(), cm == editors.last); + }); - makeSectionVisible(firstAddedCM); - firstAddedCM.focus(); + makeSectionVisible(firstAddedCM); + firstAddedCM.focus(); - if (errors) { - showHelp(t("issues"), errors); - } - }); + if (errors) { + showHelp(t("issues"), errors); + } + }); - parser.addListener("error", function(e) { - errors += e.line + ":" + e.col + " " + e.message.replace(/ at line \d.+$/, "") + "
"; - }); + parser.addListener("error", function(e) { + errors += e.line + ":" + e.col + " " + e.message.replace(/ at line \d.+$/, "") + "
"; + }); - parser.parse(mozStyle); + parser.parse(mozStyle); - function getRange( start, end) { - const L1 = start.line - 1, C1 = start.col - 1; - const L2 = end.line - 1, C2 = end.col - 1; - if (L1 == L2) { - return lines[L1].substr(C1, C2 - C1 + 1); - } else { - const middle = lines.slice(L1 + 1, L2).join('\n'); - return lines[L1].substr(C1) + '\n' + middle + - (L2 >= lines.length ? '' : ((middle ? '\n' : '') + lines[L2].substring(0, C2))); - } - } - function doAddSection(section) { - section.code = section.code.trim(); - // don't add empty sections - if (!section.code - && !section.urls - && !section.urlPrefixes - && !section.domains - && !section.regexps) { - return; - } - if (!firstAddedCM) { - if (!initFirstSection(section)) { - return; - } - } - setCleanItem(addSection(null, section), false); - firstAddedCM = firstAddedCM || editors.last; - } - // do onetime housekeeping as the imported text is confirmed to be a valid style - function initFirstSection(section) { - // skip adding the first global section when there's no code/comments - if (!section.code.replace("@namespace url(http://www.w3.org/1999/xhtml);", "") /* ignore boilerplate NS */ - .replace(/[\s\n]/g, "")) { /* ignore all whitespace including new lines */ - return false; - } - if (replaceOldStyle) { - editors.slice(0).reverse().forEach(function(cm) { - removeSection({target: cm.getSection().firstElementChild}); - }); - } else if (!editors.last.getValue()) { - // nuke the last blank section - if (editors.last.getSection().querySelector(".applies-to-everything")) { - removeSection({target: editors.last.getSection()}); - } - } - return true; - } - } - function backtrackTo(parser, tokenType, startEnd) { - var tokens = parser._tokenStream._lt; - for (var i = parser._tokenStream._ltIndex - 1; i >= 0; --i) { - if (tokens[i].type == tokenType) { - return {line: tokens[i][startEnd+"Line"], col: tokens[i][startEnd+"Col"]}; - } - } - } - function trimNewLines(s) { - return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, ""); - } + function getRange( start, end) { + const L1 = start.line - 1, C1 = start.col - 1; + const L2 = end.line - 1, C2 = end.col - 1; + if (L1 == L2) { + return lines[L1].substr(C1, C2 - C1 + 1); + } else { + const middle = lines.slice(L1 + 1, L2).join('\n'); + return lines[L1].substr(C1) + '\n' + middle + + (L2 >= lines.length ? '' : ((middle ? '\n' : '') + lines[L2].substring(0, C2))); + } + } + function doAddSection(section) { + section.code = section.code.trim(); + // don't add empty sections + if (!section.code + && !section.urls + && !section.urlPrefixes + && !section.domains + && !section.regexps) { + return; + } + if (!firstAddedCM) { + if (!initFirstSection(section)) { + return; + } + } + setCleanItem(addSection(null, section), false); + firstAddedCM = firstAddedCM || editors.last; + } + // do onetime housekeeping as the imported text is confirmed to be a valid style + function initFirstSection(section) { + // skip adding the first global section when there's no code/comments + if (!section.code.replace("@namespace url(http://www.w3.org/1999/xhtml);", "") /* ignore boilerplate NS */ + .replace(/[\s\n]/g, "")) { /* ignore all whitespace including new lines */ + return false; + } + if (replaceOldStyle) { + editors.slice(0).reverse().forEach(function(cm) { + removeSection({target: cm.getSection().firstElementChild}); + }); + } else if (!editors.last.getValue()) { + // nuke the last blank section + if (editors.last.getSection().querySelector(".applies-to-everything")) { + removeSection({target: editors.last.getSection()}); + } + } + return true; + } + } + function backtrackTo(parser, tokenType, startEnd) { + var tokens = parser._tokenStream._lt; + for (var i = parser._tokenStream._ltIndex - 1; i >= 0; --i) { + if (tokens[i].type == tokenType) { + return {line: tokens[i][startEnd+"Line"], col: tokens[i][startEnd+"Col"]}; + } + } + } + function trimNewLines(s) { + return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, ""); + } } function showSectionHelp() { - showHelp(t("styleSectionsTitle"), t("sectionHelp")); + showHelp(t("styleSectionsTitle"), t("sectionHelp")); } function showAppliesToHelp() { - showHelp(t("appliesLabel"), t("appliesHelp")); + showHelp(t("appliesLabel"), t("appliesHelp")); } function showToMozillaHelp() { - showHelp(t("styleMozillaFormatHeading"), t("styleToMozillaFormatHelp")); + showHelp(t("styleMozillaFormatHeading"), t("styleToMozillaFormatHelp")); } function showToggleStyleHelp() { - showHelp(t("helpAlt"), t("styleEnabledToggleHint")); + showHelp(t("helpAlt"), t("styleEnabledToggleHint")); } function showKeyMapHelp() { - var keyMap = mergeKeyMaps({}, prefs.get("editor.keyMap"), CodeMirror.defaults.extraKeys); - var keyMapSorted = Object.keys(keyMap) - .map(function(key) { return {key: key, cmd: keyMap[key]} }) - .concat([{key: "Shift-Ctrl-Wheel", cmd: "scrollWindow"}]) - .sort(function(a, b) { return a.cmd < b.cmd || (a.cmd == b.cmd && a.key < b.key) ? -1 : 1 }); - showHelp(t("cm_keyMap") + ": " + prefs.get("editor.keyMap"), - '' + - '' + - '' + - "" + keyMapSorted.map(function(value) { - return ""; - }).join("") + - "" + - "
" + value.key + "" + value.cmd + "
"); + var keyMap = mergeKeyMaps({}, prefs.get("editor.keyMap"), CodeMirror.defaults.extraKeys); + var keyMapSorted = Object.keys(keyMap) + .map(function(key) { return {key: key, cmd: keyMap[key]} }) + .concat([{key: "Shift-Ctrl-Wheel", cmd: "scrollWindow"}]) + .sort(function(a, b) { return a.cmd < b.cmd || (a.cmd == b.cmd && a.key < b.key) ? -1 : 1 }); + showHelp(t("cm_keyMap") + ": " + prefs.get("editor.keyMap"), + '' + + '' + + '' + + "" + keyMapSorted.map(function(value) { + return ""; + }).join("") + + "" + + "
" + value.key + "" + value.cmd + "
"); - var table = document.querySelector("#help-popup table"); - table.addEventListener("input", filterTable); + var table = document.querySelector("#help-popup table"); + table.addEventListener("input", filterTable); - var inputs = table.querySelectorAll("input"); - inputs[0].addEventListener("keydown", hotkeyHandler); - inputs[1].focus(); + var inputs = table.querySelectorAll("input"); + inputs[0].addEventListener("keydown", hotkeyHandler); + inputs[1].focus(); - function hotkeyHandler(event) { - var keyName = CodeMirror.keyName(event); - if (keyName == "Esc" || keyName == "Tab" || keyName == "Shift-Tab") { - return; - } - event.preventDefault(); - event.stopPropagation(); - // normalize order of modifiers, - // for modifier-only keys ("Ctrl-Shift") a dummy main key has to be temporarily added - var keyMap = {}; - keyMap[keyName.replace(/(Shift|Ctrl|Alt|Cmd)$/, "$&-dummy")] = ""; - var normalizedKey = Object.keys(CodeMirror.normalizeKeyMap(keyMap))[0]; - this.value = normalizedKey.replace("-dummy", ""); - filterTable(event); - } - function filterTable(event) { - var input = event.target; - var query = stringAsRegExp(input.value, "gi"); - var col = input.parentNode.cellIndex; - inputs[1 - col].value = ""; - table.tBodies[0].childNodes.forEach(function(row) { - var cell = row.children[col]; - cell.innerHTML = cell.textContent.replace(query, "$&"); - row.style.display = query.test(cell.textContent) ? "" : "none"; - // clear highlight from the other column - cell = row.children[1 - col]; - cell.innerHTML = cell.textContent; - }); - } - function mergeKeyMaps(merged, ...more) { - more.forEach(keyMap => { - if (typeof keyMap == "string") { - keyMap = CodeMirror.keyMap[keyMap]; - } - Object.keys(keyMap).forEach(function(key) { - var cmd = keyMap[key]; - // filter out '...', 'attach', etc. (hotkeys start with an uppercase letter) - if (!merged[key] && !key.match(/^[a-z]/) && cmd != "...") { - if (typeof cmd == "function") { - // for 'emacs' keymap: provide at least something meaningful (hotkeys and the function body) - // for 'vim*' keymaps: almost nothing as it doesn't rely on CM keymap mechanism - cmd = cmd.toString().replace(/^function.*?\{[\s\r\n]*([\s\S]+?)[\s\r\n]*\}$/, "$1"); - merged[key] = cmd.length <= 200 ? cmd : cmd.substr(0, 200) + "..."; - } else { - merged[key] = cmd; - } - } - }); - if (keyMap.fallthrough) { - merged = mergeKeyMaps(merged, keyMap.fallthrough); - } - }); - return merged; - } + function hotkeyHandler(event) { + var keyName = CodeMirror.keyName(event); + if (keyName == "Esc" || keyName == "Tab" || keyName == "Shift-Tab") { + return; + } + event.preventDefault(); + event.stopPropagation(); + // normalize order of modifiers, + // for modifier-only keys ("Ctrl-Shift") a dummy main key has to be temporarily added + var keyMap = {}; + keyMap[keyName.replace(/(Shift|Ctrl|Alt|Cmd)$/, "$&-dummy")] = ""; + var normalizedKey = Object.keys(CodeMirror.normalizeKeyMap(keyMap))[0]; + this.value = normalizedKey.replace("-dummy", ""); + filterTable(event); + } + function filterTable(event) { + var input = event.target; + var query = stringAsRegExp(input.value, "gi"); + var col = input.parentNode.cellIndex; + inputs[1 - col].value = ""; + table.tBodies[0].childNodes.forEach(function(row) { + var cell = row.children[col]; + cell.innerHTML = cell.textContent.replace(query, "$&"); + row.style.display = query.test(cell.textContent) ? "" : "none"; + // clear highlight from the other column + cell = row.children[1 - col]; + cell.innerHTML = cell.textContent; + }); + } + function mergeKeyMaps(merged, ...more) { + more.forEach(keyMap => { + if (typeof keyMap == "string") { + keyMap = CodeMirror.keyMap[keyMap]; + } + Object.keys(keyMap).forEach(function(key) { + var cmd = keyMap[key]; + // filter out '...', 'attach', etc. (hotkeys start with an uppercase letter) + if (!merged[key] && !key.match(/^[a-z]/) && cmd != "...") { + if (typeof cmd == "function") { + // for 'emacs' keymap: provide at least something meaningful (hotkeys and the function body) + // for 'vim*' keymaps: almost nothing as it doesn't rely on CM keymap mechanism + cmd = cmd.toString().replace(/^function.*?\{[\s\r\n]*([\s\S]+?)[\s\r\n]*\}$/, "$1"); + merged[key] = cmd.length <= 200 ? cmd : cmd.substr(0, 200) + "..."; + } else { + merged[key] = cmd; + } + } + }); + if (keyMap.fallthrough) { + merged = mergeKeyMaps(merged, keyMap.fallthrough); + } + }); + return merged; + } } function showLintHelp() { - showHelp(t("issues"), t("issuesHelp") + "" - ); + showHelp(t("issues"), t("issuesHelp") + "" + ); } function showRegExpTester(event, section = getSectionForChild(this)) { - const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain='; - const OWN_ICON = chrome.runtime.getManifest().icons['16']; - const cachedRegexps = showRegExpTester.cachedRegexps = - showRegExpTester.cachedRegexps || new Map(); - const regexps = [...section.querySelector('.applies-to-list').children] - .map(item => - !item.matches('.applies-to-everything') && - item.querySelector('.applies-type').value == 'regexp' && - item.querySelector('.applies-value').value.trim()) - .filter(item => item) - .map(text => { - const rxData = Object.assign({text}, cachedRegexps.get(text)); - if (!rxData.urls) { - cachedRegexps.set(text, Object.assign(rxData, { - rx: tryRegExp(text), - urls: new Map(), - })); - } - return rxData; - }); - chrome.tabs.onUpdated.addListener(function _(tabId, info) { - if (document.querySelector('.regexp-report')) { - if (info.url) { - showRegExpTester(event, section); - } - } else { - chrome.tabs.onUpdated.removeListener(_); - } - }); - queryTabs().then(tabs => { - const supported = tabs.map(tab => tab.url) - .filter(url => URLS.supported.test(url)); - const unique = [...new Set(supported).values()]; - for (const rxData of regexps) { - const {rx, urls} = rxData; - if (rx) { - const urlsNow = new Map(); - for (const url of unique) { - const match = urls.get(url) || (url.match(rx) || [])[0]; - if (match) { - urlsNow.set(url, match); - } - } - rxData.urls = urlsNow; - } - } - const moreInfoLink = template.regexpTestPartial.outerHTML; - const stats = { - full: {data: [], label: t('styleRegexpTestFull')}, - partial: {data: [], label: t('styleRegexpTestPartial') + moreInfoLink}, - none: {data: [], label: t('styleRegexpTestNone')}, - invalid: {data: [], label: t('styleRegexpTestInvalid')}, - }; - for (const {text, rx, urls} of regexps) { - if (!rx) { - stats.invalid.data.push({text}); - continue; - } - if (!urls.size) { - stats.none.data.push({text}); - continue; - } - const full = []; - const partial = []; - for (const [url, match] of urls.entries()) { - const faviconUrl = url.startsWith(URLS.ownOrigin) - ? OWN_ICON - : GET_FAVICON_URL + new URL(url).hostname; - const icon = ``; - if (match.length == url.length) { - full.push(`
${icon + url}
`); - } else { - partial.push(`
${icon}${match}` + - url.substr(match.length) + '
'); - } - } - if (full.length) { - stats.full.data.push({text, urls: full}); - } - if (partial.length) { - stats.partial.data.push({text, urls: partial}); - } - } - showHelp(t('styleRegexpTestTitle'), - '
' + - Object.keys(stats).map(type => (!stats[type].data.length ? '' : - `
- ${stats[type].label}` + - stats[type].data.map(({text, urls}) => (!urls ? text : - `
${text}${urls.join('')}
` - )).join('
') + - '
' - )).join('') + - '
'); - document.querySelector('.regexp-report').onclick = event => { - const target = event.target.closest('a, .regexp-report div'); - if (target) { - openURL({url: target.href || target.textContent}); - event.preventDefault(); - } - }; - }); + const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain='; + const OWN_ICON = chrome.runtime.getManifest().icons['16']; + const cachedRegexps = showRegExpTester.cachedRegexps = + showRegExpTester.cachedRegexps || new Map(); + const regexps = [...section.querySelector('.applies-to-list').children] + .map(item => + !item.matches('.applies-to-everything') && + item.querySelector('.applies-type').value == 'regexp' && + item.querySelector('.applies-value').value.trim()) + .filter(item => item) + .map(text => { + const rxData = Object.assign({text}, cachedRegexps.get(text)); + if (!rxData.urls) { + cachedRegexps.set(text, Object.assign(rxData, { + rx: tryRegExp(text), + urls: new Map(), + })); + } + return rxData; + }); + chrome.tabs.onUpdated.addListener(function _(tabId, info) { + if (document.querySelector('.regexp-report')) { + if (info.url) { + showRegExpTester(event, section); + } + } else { + chrome.tabs.onUpdated.removeListener(_); + } + }); + queryTabs().then(tabs => { + const supported = tabs.map(tab => tab.url) + .filter(url => URLS.supported.test(url)); + const unique = [...new Set(supported).values()]; + for (const rxData of regexps) { + const {rx, urls} = rxData; + if (rx) { + const urlsNow = new Map(); + for (const url of unique) { + const match = urls.get(url) || (url.match(rx) || [])[0]; + if (match) { + urlsNow.set(url, match); + } + } + rxData.urls = urlsNow; + } + } + const moreInfoLink = template.regexpTestPartial.outerHTML; + const stats = { + full: {data: [], label: t('styleRegexpTestFull')}, + partial: {data: [], label: t('styleRegexpTestPartial') + moreInfoLink}, + none: {data: [], label: t('styleRegexpTestNone')}, + invalid: {data: [], label: t('styleRegexpTestInvalid')}, + }; + for (const {text, rx, urls} of regexps) { + if (!rx) { + stats.invalid.data.push({text}); + continue; + } + if (!urls.size) { + stats.none.data.push({text}); + continue; + } + const full = []; + const partial = []; + for (const [url, match] of urls.entries()) { + const faviconUrl = url.startsWith(URLS.ownOrigin) + ? OWN_ICON + : GET_FAVICON_URL + new URL(url).hostname; + const icon = ``; + if (match.length == url.length) { + full.push(`
${icon + url}
`); + } else { + partial.push(`
${icon}${match}` + + url.substr(match.length) + '
'); + } + } + if (full.length) { + stats.full.data.push({text, urls: full}); + } + if (partial.length) { + stats.partial.data.push({text, urls: partial}); + } + } + showHelp(t('styleRegexpTestTitle'), + '
' + + Object.keys(stats).map(type => (!stats[type].data.length ? '' : + `
+ ${stats[type].label}` + + stats[type].data.map(({text, urls}) => (!urls ? text : + `
${text}${urls.join('')}
` + )).join('
') + + '
' + )).join('') + + '
'); + document.querySelector('.regexp-report').onclick = event => { + const target = event.target.closest('a, .regexp-report div'); + if (target) { + openURL({url: target.href || target.textContent}); + event.preventDefault(); + } + }; + }); } function showHelp(title, text) { - var div = document.getElementById("help-popup"); - div.classList.remove("big"); - div.querySelector(".contents").innerHTML = text; - div.querySelector(".title").innerHTML = title; + var div = document.getElementById("help-popup"); + div.classList.remove("big"); + div.querySelector(".contents").innerHTML = text; + div.querySelector(".title").innerHTML = title; - if (getComputedStyle(div).display == "none") { - document.addEventListener("keydown", closeHelp); - div.querySelector(".dismiss").onclick = closeHelp; // avoid chaining on multiple showHelp() calls - } + if (getComputedStyle(div).display == "none") { + document.addEventListener("keydown", closeHelp); + div.querySelector(".dismiss").onclick = closeHelp; // avoid chaining on multiple showHelp() calls + } - div.style.display = "block"; - return div; + div.style.display = "block"; + return div; - function closeHelp(e) { - if (!e - || e.type == "click" - || ((e.keyCode || e.which) == 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey)) { - div.style.display = ""; - document.querySelector(".contents").innerHTML = ""; - document.removeEventListener("keydown", closeHelp); - } - } + function closeHelp(e) { + if (!e + || e.type == "click" + || ((e.keyCode || e.which) == 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey)) { + div.style.display = ""; + document.querySelector(".contents").innerHTML = ""; + document.removeEventListener("keydown", closeHelp); + } + } } function showCodeMirrorPopup(title, html, options) { - var popup = showHelp(title, html); - popup.classList.add("big"); + var popup = showHelp(title, html); + popup.classList.add("big"); - popup.codebox = CodeMirror(popup.querySelector(".contents"), Object.assign({ - mode: "css", - lineNumbers: true, - lineWrapping: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], - matchBrackets: true, - lint: {getAnnotations: CodeMirror.lint.css, delay: 0}, - styleActiveLine: true, - theme: prefs.get("editor.theme"), - keyMap: prefs.get("editor.keyMap") - }, options)); - popup.codebox.focus(); - popup.codebox.on("focus", function() { hotkeyRerouter.setState(false) }); - popup.codebox.on("blur", function() { hotkeyRerouter.setState(true) }); - return popup; + popup.codebox = CodeMirror(popup.querySelector(".contents"), Object.assign({ + mode: "css", + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], + matchBrackets: true, + lint: {getAnnotations: CodeMirror.lint.css, delay: 0}, + styleActiveLine: true, + theme: prefs.get("editor.theme"), + keyMap: prefs.get("editor.keyMap") + }, options)); + popup.codebox.focus(); + popup.codebox.on("focus", function() { hotkeyRerouter.setState(false) }); + popup.codebox.on("blur", function() { hotkeyRerouter.setState(true) }); + return popup; } function getParams() { - var params = {}; - var urlParts = location.href.split("?", 2); - if (urlParts.length == 1) { - return params; - } - urlParts[1].split("&").forEach(function(keyValue) { - var splitKeyValue = keyValue.split("=", 2); - params[decodeURIComponent(splitKeyValue[0])] = decodeURIComponent(splitKeyValue[1]); - }); - return params; + var params = {}; + var urlParts = location.href.split("?", 2); + if (urlParts.length == 1) { + return params; + } + urlParts[1].split("&").forEach(function(keyValue) { + var splitKeyValue = keyValue.split("=", 2); + params[decodeURIComponent(splitKeyValue[0])] = decodeURIComponent(splitKeyValue[1]); + }); + return params; } chrome.runtime.onMessage.addListener(onRuntimeMessage); function onRuntimeMessage(request) { - switch (request.method) { - case "styleUpdated": - if (styleId && styleId == request.style.id && request.reason != 'editSave') { - if ((request.style.sections[0] || {}).code === null) { - // the code-less style came from notifyAllTabs - onBackgroundReady().then(() => { - request.style = BG.cachedStyles.byId.get(request.style.id); - initWithStyle(request); - }); - } else { - initWithStyle(request); - } - } - break; - case "styleDeleted": - if (styleId && styleId == request.id) { - window.onbeforeunload = function() {}; - window.close(); - break; - } - break; - case "prefChanged": - if ('editor.smartIndent' in request.prefs) { - CodeMirror.setOption('smartIndent', request.prefs['editor.smartIndent']); - } - break; - case 'editDeleteText': - document.execCommand('delete'); - break; - } + switch (request.method) { + case "styleUpdated": + if (styleId && styleId == request.style.id && request.reason != 'editSave') { + if ((request.style.sections[0] || {}).code === null) { + // the code-less style came from notifyAllTabs + onBackgroundReady().then(() => { + request.style = BG.cachedStyles.byId.get(request.style.id); + initWithStyle(request); + }); + } else { + initWithStyle(request); + } + } + break; + case "styleDeleted": + if (styleId && styleId == request.id) { + window.onbeforeunload = function() {}; + window.close(); + break; + } + break; + case "prefChanged": + if ('editor.smartIndent' in request.prefs) { + CodeMirror.setOption('smartIndent', request.prefs['editor.smartIndent']); + } + break; + case 'editDeleteText': + document.execCommand('delete'); + break; + } } function getComputedHeight(el) { - var compStyle = getComputedStyle(el); - return el.getBoundingClientRect().height + - parseFloat(compStyle.marginTop) + parseFloat(compStyle.marginBottom); + var compStyle = getComputedStyle(el); + return el.getBoundingClientRect().height + + parseFloat(compStyle.marginTop) + parseFloat(compStyle.marginBottom); } function getCodeMirrorThemes() { - if (!chrome.runtime.getPackageDirectoryEntry) { - const themes = [ - chrome.i18n.getMessage('defaultTheme'), - '3024-day', - '3024-night', - 'abcdef', - 'ambiance', - 'ambiance-mobile', - 'base16-dark', - 'base16-light', - 'bespin', - 'blackboard', - 'cobalt', - 'colorforth', - 'dracula', - 'duotone-dark', - 'duotone-light', - 'eclipse', - 'elegant', - 'erlang-dark', - 'hopscotch', - 'icecoder', - 'isotope', - 'lesser-dark', - 'liquibyte', - 'material', - 'mbo', - 'mdn-like', - 'midnight', - 'monokai', - 'neat', - 'neo', - 'night', - 'panda-syntax', - 'paraiso-dark', - 'paraiso-light', - 'pastel-on-dark', - 'railscasts', - 'rubyblue', - 'seti', - 'solarized', - 'the-matrix', - 'tomorrow-night-bright', - 'tomorrow-night-eighties', - 'ttcn', - 'twilight', - 'vibrant-ink', - 'xq-dark', - 'xq-light', - 'yeti', - 'zenburn', - ]; - localStorage.codeMirrorThemes = themes.join(' '); - return Promise.resolve(themes); - } - return new Promise(resolve => { - chrome.runtime.getPackageDirectoryEntry(rootDir => { - rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => { - themeDir.createReader().readEntries(entries => { - const themes = [ - chrome.i18n.getMessage('defaultTheme') - ].concat( - entries.filter(entry => entry.isFile) - .sort((a, b) => (a.name < b.name ? -1 : 1)) - .map(entry => entry.name.replace(/\.css$/, '')) - ); - localStorage.codeMirrorThemes = themes.join(' '); - resolve(themes); - }); - }); - }); - }); + if (!chrome.runtime.getPackageDirectoryEntry) { + const themes = [ + chrome.i18n.getMessage('defaultTheme'), + '3024-day', + '3024-night', + 'abcdef', + 'ambiance', + 'ambiance-mobile', + 'base16-dark', + 'base16-light', + 'bespin', + 'blackboard', + 'cobalt', + 'colorforth', + 'dracula', + 'duotone-dark', + 'duotone-light', + 'eclipse', + 'elegant', + 'erlang-dark', + 'hopscotch', + 'icecoder', + 'isotope', + 'lesser-dark', + 'liquibyte', + 'material', + 'mbo', + 'mdn-like', + 'midnight', + 'monokai', + 'neat', + 'neo', + 'night', + 'panda-syntax', + 'paraiso-dark', + 'paraiso-light', + 'pastel-on-dark', + 'railscasts', + 'rubyblue', + 'seti', + 'solarized', + 'the-matrix', + 'tomorrow-night-bright', + 'tomorrow-night-eighties', + 'ttcn', + 'twilight', + 'vibrant-ink', + 'xq-dark', + 'xq-light', + 'yeti', + 'zenburn', + ]; + localStorage.codeMirrorThemes = themes.join(' '); + return Promise.resolve(themes); + } + return new Promise(resolve => { + chrome.runtime.getPackageDirectoryEntry(rootDir => { + rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => { + themeDir.createReader().readEntries(entries => { + const themes = [ + chrome.i18n.getMessage('defaultTheme') + ].concat( + entries.filter(entry => entry.isFile) + .sort((a, b) => (a.name < b.name ? -1 : 1)) + .map(entry => entry.name.replace(/\.css$/, '')) + ); + localStorage.codeMirrorThemes = themes.join(' '); + resolve(themes); + }); + }); + }); + }); }