From 426b4d37ba5eab9c9fcd64cf0d4dec16e30c38e7 Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 2 Feb 2016 08:18:23 +0200 Subject: [PATCH 1/2] Simplify getCodeMirrorForSection/getSectionForCodeMirror --- edit.js | 59 ++++++++++++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/edit.js b/edit.js index 43685dea..137e2ca7 100644 --- a/edit.js +++ b/edit.js @@ -84,9 +84,9 @@ function setCleanItem(node, isClean) { if (isClean) { delete dirty[node.id]; - // A div would indicate a section - if (node.nodeName.toLowerCase() == "div") { - node.savedValue = getCodeMirrorForSection(node).changeGeneration(); + // code sections have .CodeMirror property + if (node.CodeMirror) { + node.savedValue = node.CodeMirror.changeGeneration(); } else { node.savedValue = "checkbox" === node.type ? node.checked : node.value; } @@ -113,7 +113,7 @@ function setCleanSection(section) { section.querySelectorAll(".style-contributor").forEach(function(node) { setCleanItem(node, true) }); // #header section has no codemirror - var cm = getCodeMirrorForSection(section) + var cm = section.CodeMirror; if (cm) { section.savedValue = cm.changeGeneration(); indicateCodeChange(cm); @@ -219,6 +219,10 @@ function initCodeMirror() { }); } + CM.prototype.getSection = function() { + return this.display.wrapper.parentNode; + } + // preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs() var theme = prefs.get("editor.theme"); document.getElementById("cm-theme").href = theme == "default" ? "" : "codemirror/theme/" + theme + ".css"; @@ -352,16 +356,12 @@ function setupCodeMirror(textarea, index) { } function indicateCodeChange(cm) { - var section = getSectionForCodeMirror(cm); + var section = cm.getSection(); setCleanItem(section, cm.isClean(section.savedValue)); updateTitle(); updateLintReport(cm); } -function getSectionForCodeMirror(cm) { - return cm.display.wrapper.parentNode; -} - function getSectionForChild(e) { return e.closest("#sections > div"); } @@ -370,12 +370,6 @@ function getSections() { return document.querySelectorAll("#sections > div"); } -function getCodeMirrorForSection(section) { - // #header section has no codemirror - var wrapper = section.querySelector(".CodeMirror"); - return wrapper && wrapper.CodeMirror; -} - // 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) { @@ -499,7 +493,7 @@ function addSection(event, section) { var sections = document.getElementById("sections"); if (event) { - var clickedSection = event.target.parentNode; + var clickedSection = getSectionForChild(event.target); sections.insertBefore(div, clickedSection.nextElementSibling); var newIndex = getSections().indexOf(clickedSection) + 1; var cm = setupCodeMirror(codeElement, newIndex); @@ -508,9 +502,10 @@ function addSection(event, section) { renderLintReport(); } else { sections.appendChild(div); - setupCodeMirror(codeElement); + var cm = setupCodeMirror(codeElement); } + div.CodeMirror = cm; setCleanSection(div); return div; } @@ -525,8 +520,8 @@ function removeAppliesTo(event) { } function removeSection(event) { - var section = event.target.parentNode; - var cm = getCodeMirrorForSection(section); + var section = getSectionForChild(event.target); + var cm = section.CodeMirror; removeAreaAndSetDirty(section); editors.splice(editors.indexOf(cm), 1); renderLintReport(); @@ -549,7 +544,7 @@ function removeAreaAndSetDirty(area) { } function makeSectionVisible(cm) { - var section = getSectionForCodeMirror(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) { @@ -680,7 +675,7 @@ function setupGlobalSearch() { originalCommand[reverse ? "findPrev" : "findNext"](activeCM); function searchAppliesTo(cm) { - var inputs = [].slice.call(getSectionForCodeMirror(cm).querySelectorAll(".applies-value")); + var inputs = [].slice.call(cm.getSection().querySelectorAll(".applies-value")); if (reverse) { inputs = inputs.reverse(); } @@ -799,7 +794,7 @@ function jumpToLine(cm) { } function refocusMinidialog(cm) { - var section = getSectionForCodeMirror(cm); + var section = cm.getSection(); if (!section.querySelector(".CodeMirror-dialog")) { return; } @@ -821,7 +816,7 @@ 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 = getCodeMirrorForSection(getSectionForChild(nearbyElement)); + cm = getSectionForChild(nearbyElement).CodeMirror; } else { cm = editors.lastActive; } @@ -838,7 +833,7 @@ function getEditorInSight(nearbyElement) { function offscreenDistance(cm) { var LINES_VISIBLE = 2; // closest editor should have at least # lines visible - var bounds = getSectionForCodeMirror(cm).getBoundingClientRect(); + var bounds = cm.getSection().getBoundingClientRect(); if (bounds.top < 0) { return -bounds.top; } else if (bounds.top < window.innerHeight - cm.defaultTextHeight() * LINES_VISIBLE) { @@ -994,7 +989,7 @@ function beautify(event) { options.indent_char = tabs ? "\t" : " "; var section = getSectionForChild(event.target); - var scope = section ? [getCodeMirrorForSection(section)] : editors; + var scope = section ? [section.CodeMirror] : editors; showHelp(t("styleBeautify"), "
" + optionHtml(".selector1,", "selector_separator_newline") + @@ -1114,7 +1109,7 @@ function initWithStyle(style) { function add() { var sectionDiv = addSection(null, queue.shift()); maximizeCodeHeight(sectionDiv, !queue.length); - updateLintReport(getCodeMirrorForSection(sectionDiv), prefs.get("editor.lintDelay")); + updateLintReport(sectionDiv.CodeMirror, prefs.get("editor.lintDelay")); } } @@ -1141,7 +1136,7 @@ function initHooks() { } function maximizeCodeHeight(sectionDiv, isLast) { - var cm = getCodeMirrorForSection(sectionDiv); + var cm = sectionDiv.CodeMirror; var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []}; if (!stats.cmActualHeight) { stats.cmActualHeight = getComputedHeight(cm.display.wrapper); @@ -1254,7 +1249,7 @@ function getSectionsHashes() { var sections = []; getSections().forEach(function(div) { var meta = getMeta(div); - var code = getCodeMirrorForSection(div).getValue(); + var code = div.CodeMirror.getValue(); if (/^\s*$/.test(code) && Object.keys(meta).length == 0) { return; } @@ -1373,7 +1368,7 @@ function fromMozillaFormat() { if (!replaceOldStyle) { var lastOldCM = editors[oldSectionCount - 1]; - var lastOldSection = getSectionForCodeMirror(lastOldCM); + var lastOldSection = lastOldCM.getSection(); var addAfter = {target: lastOldSection.querySelector(".add-section")}; if (oldSectionCount < editors.length @@ -1383,7 +1378,7 @@ function fromMozillaFormat() { oldSectionCount--; } } else { - var addAfter = {target: getSectionForCodeMirror(editors[0]).previousElementSibling.firstElementChild}; + var addAfter = {target: editors[0].getSection().previousElementSibling.firstElementChild}; } var globalSection = sectionStack[0]; @@ -1399,7 +1394,7 @@ function fromMozillaFormat() { delete maximizeCodeHeight.stats; editors.forEach(function(cm, i) { - maximizeCodeHeight(getSectionForCodeMirror(cm), i == editors.length - 1); + maximizeCodeHeight(cm.getSection(), i == editors.length - 1); }); makeSectionVisible(editors[oldSectionCount]); @@ -1428,7 +1423,7 @@ function fromMozillaFormat() { if (replaceOldStyle && oldSectionCount > 0) { oldSectionCount = 0; editors.slice(0).reverse().forEach(function(cm) { - removeSection({target: getSectionForCodeMirror(cm).firstElementChild}); + removeSection({target: cm.getSection().firstElementChild}); }); } setCleanItem(addSection(null, section), false); From ad8706e107055994dec9b94f7cdf4df73a1a3dc8 Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 2 Feb 2016 08:30:08 +0200 Subject: [PATCH 2/2] Editor: preserve intermediary global sections on import Because the order of sections influences which rules will apply when several matching rules are present both in a global section and previously declared scoped one. --- edit.js | 97 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/edit.js b/edit.js index 137e2ca7..eca104e4 100644 --- a/edit.js +++ b/edit.js @@ -1332,19 +1332,24 @@ function fromMozillaFormat() { popup.querySelector(".close-icon").click(); var mozStyle = trimNewLines(popup.codebox.getValue()); var parser = new exports.css.Parser(), lines = mozStyle.split("\n"); - var sectionStack = [{code: "", cursor: {line: 1, col: 1}}]; + 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.cursor, (--e.col, e)); + var outerText = getRange(sectionStack.last.start, (--e.col, e)); var gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/); - var section = {code: "", cursor: backtrackTo(this, exports.css.Tokens.LBRACE, "end")}; + var section = {code: "", start: backtrackTo(this, exports.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)); } - addContinuation(sectionStack.last, outerText); + if (outerText) { + 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]]; @@ -1355,50 +1360,27 @@ function fromMozillaFormat() { }); parser.addListener("enddocument", function(e) { - var cursor = backtrackTo(this, exports.css.Tokens.RBRACE, "start"); + var end = backtrackTo(this, exports.css.Tokens.RBRACE, "start"); var section = sectionStack.pop(); - addContinuation(section, getRange(section.cursor, cursor)); - sectionStack.last.cursor = (++cursor.col, cursor); + section.code += getRange(section.start, end); + sectionStack.last.start = (++end.col, end); doAddSection(section); }); parser.addListener("endstylesheet", function() { - // add nonclosed (broken) outer sections except for the global one - sectionStack.slice(1).forEach(doAddSection); - - if (!replaceOldStyle) { - var lastOldCM = editors[oldSectionCount - 1]; - var lastOldSection = lastOldCM.getSection(); - var addAfter = {target: lastOldSection.querySelector(".add-section")}; - - if (oldSectionCount < editors.length - && lastOldCM.getValue() == "" - && lastOldSection.querySelector(".applies-to-everything")) { - removeSection(addAfter); - oldSectionCount--; - } - } else { - var addAfter = {target: editors[0].getSection().previousElementSibling.firstElementChild}; - } - - var globalSection = sectionStack[0]; - addContinuation(globalSection, - getRange(sectionStack.last.cursor, {line: lines.length, col: lines.last.length + 1})); - // only add global section if it contains actual code - if (globalSection.code - .replace("@namespace url(http://www.w3.org/1999/xhtml);", "") /* strip boilerplate NS */ - .replace(/\/\*[\s\S]*?\*\//g, "") /* strip comments */ - .replace(/[\s\n]/g, "")) { /* strip all whitespace including new lines */ - setCleanItem(addSection(addAfter, {code: globalSection.code}), false); - } + // 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, i) { - maximizeCodeHeight(cm.getSection(), i == editors.length - 1); + editors.forEach(function(cm) { + maximizeCodeHeight(cm.getSection(), cm == editors.last); }); - makeSectionVisible(editors[oldSectionCount]); - editors[oldSectionCount].focus(); + makeSectionVisible(firstAddedCM); + firstAddedCM.focus(); + if (errors) { showHelp(t("issues"), errors); } @@ -1412,7 +1394,7 @@ function fromMozillaFormat() { function getRange(start, end) { if (start.line == end.line) { - return lines[start.line - 1].substring(start.col - 1, end.col - 1).trim(); + return lines[start.line - 1].substr(start.col - 1, end.col - start.col + 1).trim(); } else { return trimNewLines(lines[start.line - 1].substr(start.col - 1) + "\n" + lines.slice(start.line, end.line - 1).join("\n") + @@ -1420,13 +1402,36 @@ function fromMozillaFormat() { } } function doAddSection(section) { - if (replaceOldStyle && oldSectionCount > 0) { - oldSectionCount = 0; + if (!firstAddedCM) { + if (!initFirstSection(section)) { + return; + } + } + // don't add empty sections + if (!(section.code || section.urls || section.urlPrefixes || section.domains || section.regexps)) { + 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()}); + } } - setCleanItem(addSection(null, section), false); + return true; } } function backtrackTo(parser, tokenType, startEnd) { @@ -1440,12 +1445,6 @@ function fromMozillaFormat() { function trimNewLines(s) { return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, ""); } - function addContinuation(section, addendum) { - section.code = section.code && addendum - ? section.code + "\n/**************************/\n" + addendum - : section.code || addendum; - return section.code; - } } function showSectionHelp() {