From 901000101ab69fad5cb8794613de01c3658dc825 Mon Sep 17 00:00:00 2001 From: tophf Date: Sat, 23 May 2015 15:25:59 +0300 Subject: [PATCH 1/7] Add beautify-css.js --- beautify/beautify-css.js | 457 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 457 insertions(+) create mode 100644 beautify/beautify-css.js diff --git a/beautify/beautify-css.js b/beautify/beautify-css.js new file mode 100644 index 00000000..5e45c2b6 --- /dev/null +++ b/beautify/beautify-css.js @@ -0,0 +1,457 @@ +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2013 Einar Lielmanis and contributors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + CSS Beautifier +--------------- + + Written by Harutyun Amirjanyan, (amirjanyan@gmail.com) + + Based on code initially developed by: Einar Lielmanis, + http://jsbeautifier.org/ + + Usage: + css_beautify(source_text); + css_beautify(source_text, options); + + The options are (default in brackets): + indent_size (4) — indentation size, + indent_char (space) — character to indent with, + selector_separator_newline (true) - separate selectors with newline or + not (e.g. "a,\nbr" or "a, br") + end_with_newline (false) - end with a newline + newline_between_rules (true) - add a new line after every css rule + + e.g + + css_beautify(css_source_text, { + 'indent_size': 1, + 'indent_char': '\t', + 'selector_separator': ' ', + 'end_with_newline': false, + 'newline_between_rules': true + }); +*/ + +// http://www.w3.org/TR/CSS21/syndata.html#tokenization +// http://www.w3.org/TR/css3-syntax/ + +(function() { + function css_beautify(source_text, options) { + options = options || {}; + var indentSize = options.indent_size || 4; + var indentCharacter = options.indent_char || ' '; + var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; + var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; + var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; + + // compatibility + if (typeof indentSize === "string") { + indentSize = parseInt(indentSize, 10); + } + + + // tokenizer + var whiteRe = /^\s+$/; + var wordRe = /[\w$\-_]/; + + var pos = -1, + ch; + var parenLevel = 0; + + function next() { + ch = source_text.charAt(++pos); + return ch || ''; + } + + function peek(skipWhitespace) { + var prev_pos = pos; + if (skipWhitespace) { + eatWhitespace(); + } + result = source_text.charAt(pos + 1) || ''; + pos = prev_pos - 1; + next(); + return result; + } + + function eatString(endChars) { + var start = pos; + while (next()) { + if (ch === "\\") { + next(); + } else if (endChars.indexOf(ch) !== -1) { + break; + } else if (ch === "\n") { + break; + } + } + return source_text.substring(start, pos + 1); + } + + function peekString(endChar) { + var prev_pos = pos; + var str = eatString(endChar); + pos = prev_pos - 1; + next(); + return str; + } + + function eatWhitespace() { + var result = ''; + while (whiteRe.test(peek())) { + next(); + result += ch; + } + return result; + } + + function skipWhitespace() { + var result = ''; + if (ch && whiteRe.test(ch)) { + result = ch; + } + while (whiteRe.test(next())) { + result += ch; + } + return result; + } + + function eatComment(singleLine) { + var start = pos; + singleLine = peek() === "/"; + next(); + while (next()) { + if (!singleLine && ch === "*" && peek() === "/") { + next(); + break; + } else if (singleLine && ch === "\n") { + return source_text.substring(start, pos); + } + } + + return source_text.substring(start, pos) + ch; + } + + + function lookBack(str) { + return source_text.substring(pos - str.length, pos).toLowerCase() === + str; + } + + // Nested pseudo-class if we are insideRule + // and the next special character found opens + // a new block + function foundNestedPseudoClass() { + for (var i = pos + 1; i < source_text.length; i++) { + var ch = source_text.charAt(i); + if (ch === "{") { + return true; + } else if (ch === ";" || ch === "}" || ch === ")") { + return false; + } + } + return false; + } + + // printer + var basebaseIndentString = source_text.match(/^[\t ]*/)[0]; + var singleIndent = new Array(indentSize + 1).join(indentCharacter); + var indentLevel = 0; + var nestedLevel = 0; + + function indent() { + indentLevel++; + basebaseIndentString += singleIndent; + } + + function outdent() { + indentLevel--; + basebaseIndentString = basebaseIndentString.slice(0, -indentSize); + } + + var print = {}; + print["{"] = function(ch) { + print.singleSpace(); + output.push(ch); + print.newLine(); + }; + print["}"] = function(ch) { + print.newLine(); + output.push(ch); + print.newLine(); + }; + + print._lastCharWhitespace = function() { + return whiteRe.test(output[output.length - 1]); + }; + + print.newLine = function(keepWhitespace) { + if (!keepWhitespace) { + print.trim(); + } + + if (output.length) { + output.push('\n'); + } + if (basebaseIndentString) { + output.push(basebaseIndentString); + } + }; + print.singleSpace = function() { + if (output.length && !print._lastCharWhitespace()) { + output.push(' '); + } + }; + + print.trim = function() { + while (print._lastCharWhitespace()) { + output.pop(); + } + }; + + + var output = []; + if (basebaseIndentString) { + output.push(basebaseIndentString); + } + /*_____________________--------------------_____________________*/ + + var insideRule = false; + var enteringConditionalGroup = false; + var top_ch = ''; + var last_top_ch = ''; + + while (true) { + var whitespace = skipWhitespace(); + var isAfterSpace = whitespace !== ''; + var isAfterNewline = whitespace.indexOf('\n') !== -1; + last_top_ch = top_ch; + top_ch = ch; + + if (!ch) { + break; + } else if (ch === '/' && peek() === '*') { /* css comment */ + var header = lookBack(""); + print.newLine(); + output.push(eatComment()); + print.newLine(); + if (header) { + print.newLine(true); + } + } else if (ch === '/' && peek() === '/') { // single line comment + if (!isAfterNewline && last_top_ch !== '{') { + print.trim(); + } + print.singleSpace(); + output.push(eatComment()); + print.newLine(); + } else if (ch === '@') { + // pass along the space we found as a separate item + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + + // strip trailing space, if present, for hash property checks + var variableOrRule = peekString(": ,;{}()[]/='\""); + + if (variableOrRule.match(/[ :]$/)) { + // we have a variable or pseudo-class, add it and insert one space before continuing + next(); + variableOrRule = eatString(": ").replace(/\s$/, ''); + output.push(variableOrRule); + print.singleSpace(); + } + + variableOrRule = variableOrRule.replace(/\s$/, '') + + // might be a nesting at-rule + if (variableOrRule in css_beautify.NESTED_AT_RULE) { + nestedLevel += 1; + if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) { + enteringConditionalGroup = true; + } + } + } else if (ch === '{') { + if (peek(true) === '}') { + eatWhitespace(); + next(); + print.singleSpace(); + output.push("{}"); + print.newLine(); + if (newline_between_rules && indentLevel === 0) { + print.newLine(true); + } + } else { + indent(); + print["{"](ch); + // when entering conditional groups, only rulesets are allowed + if (enteringConditionalGroup) { + enteringConditionalGroup = false; + insideRule = (indentLevel > nestedLevel); + } else { + // otherwise, declarations are also allowed + insideRule = (indentLevel >= nestedLevel); + } + } + } else if (ch === '}') { + outdent(); + print["}"](ch); + insideRule = false; + if (nestedLevel) { + nestedLevel--; + } + if (newline_between_rules && indentLevel === 0) { + print.newLine(true); + } + } else if (ch === ":") { + eatWhitespace(); + if ((insideRule || enteringConditionalGroup) && + !(lookBack("&") || foundNestedPseudoClass())) { + // 'property: value' delimiter + // which could be in a conditional group query + output.push(':'); + print.singleSpace(); + } else { + // sass/less parent reference don't use a space + // sass nested pseudo-class don't use a space + if (peek() === ":") { + // pseudo-element + next(); + output.push("::"); + } else { + // pseudo-class + output.push(':'); + } + } + } else if (ch === '"' || ch === '\'') { + if (isAfterSpace) { + print.singleSpace(); + } + output.push(eatString(ch)); + } else if (ch === ';') { + output.push(ch); + print.newLine(); + } else if (ch === '(') { // may be a url + if (lookBack("url")) { + output.push(ch); + eatWhitespace(); + if (next()) { + if (ch !== ')' && ch !== '"' && ch !== '\'') { + output.push(eatString(')')); + } else { + pos--; + } + } + } else { + parenLevel++; + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + eatWhitespace(); + } + } else if (ch === ')') { + output.push(ch); + parenLevel--; + } else if (ch === ',') { + output.push(ch); + eatWhitespace(); + if (!insideRule && selectorSeparatorNewline && parenLevel < 1) { + print.newLine(); + } else { + print.singleSpace(); + } + } else if (ch === ']') { + output.push(ch); + } else if (ch === '[') { + if (isAfterSpace) { + print.singleSpace(); + } + output.push(ch); + } else if (ch === '=') { // no whitespace before or after + eatWhitespace() + ch = '='; + output.push(ch); + } else { + if (isAfterSpace) { + print.singleSpace(); + } + + output.push(ch); + } + } + + + var sweetCode = output.join('').replace(/[\r\n\t ]+$/, ''); + + // establish end_with_newline + if (end_with_newline) { + sweetCode += "\n"; + } + + return sweetCode; + } + + // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule + css_beautify.NESTED_AT_RULE = { + "@page": true, + "@font-face": true, + "@keyframes": true, + // also in CONDITIONAL_GROUP_RULE below + "@media": true, + "@supports": true, + "@document": true + }; + css_beautify.CONDITIONAL_GROUP_RULE = { + "@media": true, + "@supports": true, + "@document": true + }; + + /*global define */ + if (typeof define === "function" && define.amd) { + // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) + define([], function() { + return { + css_beautify: css_beautify + }; + }); + } else if (typeof exports !== "undefined") { + // Add support for CommonJS. Just put this file somewhere on your require.paths + // and you will be able to `var html_beautify = require("beautify").html_beautify`. + exports.css_beautify = css_beautify; + } else if (typeof window !== "undefined") { + // If we're running a web page and don't have either of the above, add our one global + window.css_beautify = css_beautify; + } else if (typeof global !== "undefined") { + // If we don't even have window, try global. + global.css_beautify = css_beautify; + } + +}()); From 0ebb464e5d397220245437b7c54b7ed338b4c4b5 Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 26 May 2015 20:35:29 +0300 Subject: [PATCH 2/7] Add more options to beautify-css.js --- beautify/beautify-css.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/beautify/beautify-css.js b/beautify/beautify-css.js index 5e45c2b6..c9f021e7 100644 --- a/beautify/beautify-css.js +++ b/beautify/beautify-css.js @@ -62,12 +62,19 @@ (function() { function css_beautify(source_text, options) { + function defaultOption(opt, defaultValue) { + return opt === undefined ? defaultValue : opt; + } options = options || {}; var indentSize = options.indent_size || 4; var indentCharacter = options.indent_char || ' '; - var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; - var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; - var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; + var selectorSeparatorNewline = defaultOption(options.selector_separator_newline, true); + var end_with_newline = defaultOption(options.end_with_newline, false); + var newline_between_rules = defaultOption(options.newline_between_rules, true); + var newline_between_properties = defaultOption(options.newline_between_properties, true); + var newline_before_open_brace = defaultOption(options.newline_before_open_brace, false); + var newline_after_open_brace = defaultOption(options.newline_after_open_brace, true); + var newline_before_close_brace = defaultOption(options.newline_before_close_brace, true); // compatibility if (typeof indentSize === "string") { @@ -196,12 +203,12 @@ var print = {}; print["{"] = function(ch) { - print.singleSpace(); + newline_before_open_brace ? output.push('\n') : print.singleSpace(); output.push(ch); - print.newLine(); + newline_after_open_brace ? print.newLine() : print.singleSpace(); }; print["}"] = function(ch) { - print.newLine(); + newline_before_close_brace ? print.newLine() : print.singleSpace(); output.push(ch); print.newLine(); }; @@ -356,7 +363,7 @@ output.push(eatString(ch)); } else if (ch === ';') { output.push(ch); - print.newLine(); + newline_between_properties ? print.newLine() : print.singleSpace(); } else if (ch === '(') { // may be a url if (lookBack("url")) { output.push(ch); From b5df8802a0f789c026f378a8cd9b20d15595b063 Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 23 Jun 2015 19:24:53 +0300 Subject: [PATCH 3/7] Editor: CSS-beautifier --- _locales/en/messages.json | 4 +++ edit.html | 26 ++++++++++++++++-- edit.js | 58 +++++++++++++++++++++++++++++++++++++++ storage.js | 9 ++++++ 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 3c601a09..4b1ee918 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -261,6 +261,10 @@ "message": "Regexp is invalid.", "description": "Validation message for a bad regexp in a style" }, + "styleBeautify": { + "message": "Beautify", + "description": "Label for the CSS-beautifier button on the edit style page" + }, "styleCancelEditLabel": { "message": "Back to manage", "description": "Label for cancel button for style editing" diff --git a/edit.html b/edit.html index 1cc4fe9c..d7ae3780 100644 --- a/edit.html +++ b/edit.html @@ -124,7 +124,7 @@ #sections > div:only-of-type .remove-section { display: none; } - #sections > div .add-section { + #sections > div > button:not(:first-of-type) { margin-left: 0.4rem; } .dirty > label::before { @@ -316,6 +316,28 @@ text-align: left; } + /************ CSS beautifier ************/ + .beautify-options { + white-space: nowrap; + font-family: monospace; + } + .beautify-options div { + float: left; + } + .beautify-options div[newline="true"] + div { + clear: left; + } + .beautify-options div[newline="true"] + div span[indent] { + padding-left: 2rem; + } + .beautify-options span { + font-weight: bold; + } + .beautify-options select { + border: none; + background-color: rgba(0, 0, 0, 0.05); + } + /************ reponsive layouts ************/ @media(max-width:737px) { #header { @@ -425,7 +447,7 @@
-
+
diff --git a/edit.js b/edit.js index 21be4680..492c4e1b 100644 --- a/edit.js +++ b/edit.js @@ -44,6 +44,7 @@ var sectionTemplate = tHTML('\ \ \ \ + \ \ '); @@ -483,6 +484,7 @@ function addSection(event, section) { 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"); @@ -825,6 +827,61 @@ function gotoLintIssue(event) { }); } +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 = "beautify/beautify-css.js"; + script.onload = doBeautify; + } + function doBeautify() { + var tabs = prefs.getPref("editor.indentWithTabs"); + var options = prefs.getPref("editor.beautify"); + options.indent_size = tabs ? 1 : prefs.getPref("editor.tabSize"); + options.indent_char = tabs ? "\t" : " "; + + var section = querySelectorParent(event.target, "#sections > div"); + var scope = section ? [getCodeMirrorForSection(section)] : editors; + scope.forEach(function(cm) { + setTimeout(function() { + var text = cm.getValue(); + var newText = exports.css_beautify(text, options); + if (newText != text) { + cm.setValue(newText); + } + }, 0); + }); + + 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") + + "
"); + + document.querySelector(".beautify-options").addEventListener("change", function(event) { + var value = event.target.selectedIndex > 0; + options[event.target.dataset.option] = value; + prefs.setPref("editor.beautify", options); + event.target.parentNode.setAttribute("newline", value.toString()); + doBeautify(); + }); + + function optionHtml(label, optionName, indent) { + var value = options[optionName]; + return "
" + + "" + label + "" + + "
"; + } + } +} + window.addEventListener("load", init, false); function init() { @@ -883,6 +940,7 @@ function initHooks() { }); document.getElementById("to-mozilla").addEventListener("click", showMozillaFormat, false); document.getElementById("to-mozilla-help").addEventListener("click", showToMozillaHelp, false); + 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); diff --git a/storage.js b/storage.js index f7f03cdc..6cddff58 100644 --- a/storage.js +++ b/storage.js @@ -180,6 +180,15 @@ var prefs = { "editor.tabSize": 4, // tab width, in spaces "editor.keyMap": navigator.appVersion.indexOf("Windows") > 0 ? "sublime" : "default", "editor.theme": "default", // CSS theme + "editor.beautify": { // CSS beautifier + selector_separator_newline: true, + newline_before_open_brace: false, + newline_after_open_brace: true, + newline_between_properties: true, + newline_before_close_brace: true, + newline_between_rules: false, + end_with_newline: false + }, NO_DEFAULT_PREFERENCE: "No default preference for '%s'", UNHANDLED_DATA_TYPE: "Default '%s' is of type '%s' - what should be done with it?", From fa6b0f4477869c13635bf47685a245618163b86e Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 10 Jun 2015 20:15:50 +0300 Subject: [PATCH 4/7] prefs: handle object type --- storage.js | 60 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/storage.js b/storage.js index 6cddff58..44376022 100644 --- a/storage.js +++ b/storage.js @@ -173,7 +173,7 @@ var prefs = { "manage.onlyEnabled": false, // display only enabled styles "manage.onlyEdited": false, // display only styles created locally - "editor.options": null, // CodeMirror.defaults.* + "editor.options": {}, // CodeMirror.defaults.* "editor.lineWrapping": true, // word wrap "editor.smartIndent": true, // "smart" indent "editor.indentWithTabs": false,// smart indent with tabs @@ -193,39 +193,31 @@ var prefs = { NO_DEFAULT_PREFERENCE: "No default preference for '%s'", UNHANDLED_DATA_TYPE: "Default '%s' is of type '%s' - what should be done with it?", - getPref: function(key, ifUndefined) { - // Returns localStorage[key], ifUndefined, this[key], or undefined - // as type of ifUndefined, this[key], or localStorage[key] - if (ifUndefined === undefined) ifUndefined = this[key]; // default value + getPref: function(key, defaultValue) { + // Returns localStorage[key], defaultValue, this[key], or undefined + // as type of defaultValue, this[key], or localStorage[key] var value = localStorage[key]; - if (undefined === value) { // no user preference - if (ifUndefined === undefined) console.error(this.NO_DEFAULT_PREFERENCE, key); - return ifUndefined; + if (value === undefined) { + return defaultValue === undefined ? shallowCopy(this[key]) : defaultValue; } - switch (typeof ifUndefined) { + switch (typeof (defaultValue === undefined ? this[key] : defaultValue)) { case "boolean": return value.toLowerCase() === "true"; case "number": return Number(value); case "object": return JSON.parse(value); case "string": break; case "undefined": console.warn(this.NO_DEFAULT_PREFERENCE, key); break; - default: console.error(UNHANDLED_DATA_TYPE, key, typeof ifUndefined); + default: console.error(UNHANDLED_DATA_TYPE, key, typeof defaultValue); } return value; }, setPref: function(key, value) { - if (!(key in this)) console.warn(this.NO_DEFAULT_PREFERENCE, key); - var oldValue = this.getPref(key); - - if (undefined === value || this[key] === value) { - localStorage.removeItem(key); // (deleted || default) + var oldValue = localStorage[key]; + if (value === undefined || equal(value, this[key])) { + delete localStorage[key]; } else { - var strValue = ("string" === typeof value || - undefined === value) ? value : JSON.stringify(value); - localStorage.setItem(key, strValue); + localStorage[key] = typeof value == "string" ? value : JSON.stringify(value); } - - var newValue = this.getPref(key); - if (newValue !== oldValue) { + if (!equal(value, oldValue === undefined ? this[key] : oldValue)) { var message = {method: "prefChanged", prefName: key, value: value}; notifyAllTabs(message); chrome.extension.sendMessage(message); @@ -266,3 +258,29 @@ function sessionStorageHash(name) { Object.defineProperty(hash, "name", {value: name}); return hash; } + +function shallowCopy(obj) { + if (typeof obj != "object") { + return obj; + } + var copy = {}; + for (var k in obj) { + copy[k] = obj[k]; + } + return copy; +} + +function equal(a, b) { + if (!a || !b || typeof a != "object") { + return a === b; + } + if (Object.keys(a).length != Object.keys(b).length) { + return false; + } + for (var k in a) { + if (a[k] !== b[k]) { + return false; + } + } + return true; +} From 5caab7ab4704102b9621d5989a6f7e9016789241 Mon Sep 17 00:00:00 2001 From: tophf Date: Mon, 22 Jun 2015 21:18:33 +0300 Subject: [PATCH 5/7] Editor: better async adding of sections in initWithStyle --- edit.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/edit.js b/edit.js index 492c4e1b..52cdd3bc 100644 --- a/edit.js +++ b/edit.js @@ -925,11 +925,20 @@ function initWithStyle(style) { document.querySelectorAll("#sections > div").forEach(function(div) { div.parentNode.removeChild(div); }); - (style.sections.length == 0 ? [{code: ""}] : style.sections).forEach(function(section) { - setTimeout(function() { - maximizeCodeHeight(addSection(null, section), editors.length == style.sections.length); + var queue = style.sections.length ? style.sections : [{code: ""}]; + var queueStart = new Date().getTime(); + // after 200ms the sections will be added asynchronously + while (new Date().getTime() - queueStart <= 200 && queue.length) { + maximizeCodeHeight(addSection(null, queue.shift()), !queue.length); + } + if (queue.length) { + setTimeout(function processQueue() { + maximizeCodeHeight(addSection(null, queue.shift()), !queue.length); + if (queue.length) { + setTimeout(processQueue, 0); + } }, 0); - }); + } initHooks(); } From c3472207bfc1c72223373ca455723f2236fa0edf Mon Sep 17 00:00:00 2001 From: tophf Date: Mon, 22 Jun 2015 22:03:28 +0300 Subject: [PATCH 6/7] Editor: add Undo button to CSS-beautifier --- _locales/en/messages.json | 8 ++++++++ edit.html | 8 +++++++- edit.js | 13 ++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4b1ee918..b594d71f 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -319,6 +319,14 @@ "message": "(Stylish does not work on pages like this.)", "description": "Note in the toolbar pop-up when on a URL Stylish can't affect" }, + "undo": { + "message": "Undo", + "description": "Button label" + }, + "undoGlobal": { + "message": "Undo (global)", + "description": "CSS-beautify global Undo button label" + }, "updateCheckFailBadResponseCode": { "message": "Update failed - server responded with code $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", diff --git a/edit.html b/edit.html index d7ae3780..c387fe53 100644 --- a/edit.html +++ b/edit.html @@ -330,6 +330,12 @@ .beautify-options div[newline="true"] + div span[indent] { padding-left: 2rem; } + .beautify-options:after { + clear: both; + display: block; + content: " "; + height: 1rem; + } .beautify-options span { font-weight: bold; } @@ -447,7 +453,7 @@
-
+
diff --git a/edit.js b/edit.js index 52cdd3bc..974714dc 100644 --- a/edit.js +++ b/edit.js @@ -828,6 +828,7 @@ function gotoLintIssue(event) { } function beautify(event) { + var undoCount = 1; if (exports.css_beautify) { // thanks to csslint's definition of 'exports' doBeautify(); } else { @@ -860,7 +861,15 @@ function beautify(event) { 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() { + scope.forEach(CodeMirror.commands.undo); + undoButton.disabled = --undoCount == 0; + }); document.querySelector(".beautify-options").addEventListener("change", function(event) { var value = event.target.selectedIndex > 0; @@ -868,6 +877,8 @@ function beautify(event) { prefs.setPref("editor.beautify", options); event.target.parentNode.setAttribute("newline", value.toString()); doBeautify(); + undoCount++; + undoButton.disabled = false; }); function optionHtml(label, optionName, indent) { From 7fdc69fac64e6eaed012c77d205997d6f472bda7 Mon Sep 17 00:00:00 2001 From: tophf Date: Wed, 24 Jun 2015 11:55:24 +0300 Subject: [PATCH 7/7] Editor: enlarge click region of close button in help popup --- edit.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/edit.html b/edit.html index c387fe53..680fb56e 100644 --- a/edit.html +++ b/edit.html @@ -248,10 +248,11 @@ cursor: pointer; width: 8px; height: 8px; + border: 8px solid transparent; position: absolute; - right: 0.5rem; - top: 0.75rem; - background: linear-gradient(-45deg, transparent 5px, black 5px, black 6px, transparent 6.5px), linear-gradient(45deg, transparent 5px, black 5px, black 6px, transparent 6.5px); + right: 4px; + top: 4px; + background: linear-gradient(-45deg, transparent 5px, black 5px, black 6px, transparent 6.5px) no-repeat, linear-gradient(45deg, transparent 5px, black 5px, black 6px, transparent 6.5px) no-repeat; } .keymap-list {