Merge pull request #150 from Mottie/stylelint

Include both CSSLint and stylelint
This commit is contained in:
tophf 2017-08-28 08:12:10 +03:00 committed by GitHub
commit da565a5fa5
33 changed files with 4769 additions and 430 deletions

View File

@ -27,9 +27,9 @@
"message": "Изнасяне",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Проблеми",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Цвят на фона",
@ -664,9 +664,14 @@
"message": "Прозорец за настройките",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Проблеми, намерени от <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> при следните правила:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Проблеми, намерени от $link$ при следните правила:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Значка на иконката на лентата",
@ -764,4 +769,4 @@
"message": "Пресъздайте стила на Мрежата със Стайлус, разширението за стилове. То ви позволява лесно да инсталиране теми за много сайтове.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "Exportovat",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Problémy",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Barva pozadí",
@ -652,9 +652,14 @@
"message": "Možnosti rozhraní",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Problémy nalezené aplikací <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> s těmito povolenými pravidly:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Problémy nalezené aplikací $link$ s těmito povolenými pravidly:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Ikona tlačítka na panelu",
@ -752,4 +757,4 @@
"message": "Změňte vzhled webů pomocí správce uživatelských stylů. Stylus umožňuje snadnou instalaci vzhledů a modifikací pro spoustu webů.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "Exportieren",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Probleme",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Hintergrundfarbe",
@ -632,9 +632,14 @@
"message": "Optionen",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Die von <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> gefunden Fehler haben die folgenden Einstellungen:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Die von $link$ gefunden Fehler haben die folgenden Einstellungen:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Badge auf dem Toolbar-Icon",
@ -728,4 +733,4 @@
"message": "Gestalte das Web neu mit Stylus, dem Style Manager. Stylus ermöglicht dir ganz einfach Themes und Designs für viele populäre Websites zu installieren.",
"description": "Extension description"
}
}
}

View File

@ -107,6 +107,26 @@
"message": "Word wrap",
"description": "Label for the checkbox controlling word wrap option for the style editor."
},
"cm_linter": {
"message": "CSS Linter",
"description": "Select the linter to check for CSS issues"
},
"cm_matchHighlight": {
"message": "Highlight",
"description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor."
},
"cm_matchHighlightSelection": {
"message": "Selection only",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text"
},
"cm_matchHighlightToken": {
"message": "Token under cursor",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected"
},
"cm_resizeGripHint": {
"message": "Double-click to maximize/restore the height",
"description": "Tooltip for the resize grip in style editor"
},
"cm_smartIndent": {
"message": "Use smart indentation",
"description": "Label for the checkbox controlling smart indentation option for the style editor."
@ -119,22 +139,6 @@
"message": "Theme",
"description": "Label for the style editor's CSS theme."
},
"cm_matchHighlight": {
"message": "Highlight",
"description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor."
},
"cm_matchHighlightToken": {
"message": "Token under cursor",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected"
},
"cm_matchHighlightSelection": {
"message": "Selection only",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text"
},
"cm_resizeGripHint": {
"message": "Double-click to maximize/restore the height",
"description": "Tooltip for the resize grip in style editor"
},
"dysfunctional": {
"message": "Stylus cannot function because Firefox is either in private mode or is applying its website cookies policy to IndexedDB storage used by Stylus, which erroneously marks the secure moz-extension:// origin as insecure even though WebExtensions aren't websites and Stylus doesn't use cookies.\n\n1. Open Firefox options\n2. Go to 'Privacy & Security'\n3. Set 'History' mode to 'Use custom settings'\n4. Click 'Exceptions'\n5. Paste our manifest URL and click 'Allow'\n6. Click 'Save settings'\n7. Uncheck 'Always use private browsing mode'\n\nThe actual manifest URL is shown below.\nYou can also find it on about:debugging page.",
"description": "Displayed in Firefox when its settings make Stylus dysfunctional"
@ -151,6 +155,14 @@
"message": "History",
"description": "Used in various places to show a history log of something"
},
"genericResetLabel": {
"message": "Reset",
"description": "Used in various parts of UI to indicate that something may be reset to its original state"
},
"genericSavedMessage": {
"message": "Saved",
"description": "Used in various parts of the UI to indicate that something was saved"
},
"confirmNo": {
"message": "No",
"description": "'No' button in a confirm dialog"
@ -337,13 +349,51 @@
"message": "Install update",
"description": "Label for the button to install an update for a single style"
},
"issues": {
"message": "Issues",
"description": "Label for the CSSLint issues block on the style edit page"
"linterConfigPopupTitle": {
"message": "Set $linter$ rules configuration",
"description": "Stylelint or CSSLint popup header",
"placeholders": {
"linter": {
"content": "$1"
}
}
},
"issuesHelp": {
"message": "The issues found by <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> with these rules enabled:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterConfigTooltip": {
"message": "Click to configure this linter",
"description": "Icon tooltip to indicate that it opens a popup with the selected linter configuration"
},
"linterCSSLintSettings": {
"message": "(Set rule as: 0 = disabled; 1 = warning; 2 = error)",
"description": "CSSLint rule config values"
},
"linterInvalidConfigError": {
"message": "Not saved due to these invalid configuration settings:",
"description": "Invalid linter config will show a message followed by a list of invalid entries"
},
"linterIssues": {
"message": "Issues",
"description": "Label for the CSS linter issues block on the style edit page"
},
"linterIssuesHelp": {
"message": "These issues were found by $link$:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"linterJSONError": {
"message": "Invalid JSON format",
"description": "Setting linter config with invalid JSON"
},
"linterResetMessage": {
"message": "To undo accidental reset, press Ctrl-Z (or Cmd-Z) in the text box",
"description": "Reset button tooltip to inform user on how to undo an accidental reset"
},
"linterRulesLink": {
"message": "See a full list of rules",
"description": "Stylelint or CSSLint rules label added immediately before a link"
},
"manageFilters": {
"message": "Filters",

View File

@ -27,9 +27,9 @@
"message": "Exportar",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Problemas",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Color de fondo",
@ -656,9 +656,14 @@
"message": "Interfaz de opciones",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Problemas encontrados por <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> con estas reglas aplicadas:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Problemas encontrados por $link$ con estas reglas aplicadas:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Distintivo en el icono de barra de herramientas",
@ -752,4 +757,4 @@
"message": "Rediseñe la web con Stylus, un administrador de estilos de usuario. Stylus le permite instalar fácilmente temas y coberturas para muchos sitios populares.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "Ekspordi",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Vead",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Taustavärv",
@ -652,9 +652,14 @@
"message": "Valikute liides",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "<a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> poolt leitud vead nende lubatud reeglitega:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "$link$ poolt leitud vead nende lubatud reeglitega:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Number tööriistaribaikoonil",
@ -752,4 +757,4 @@
"message": "Disaini veeb ümber Stylus'ega - kasutajastiilide haldur. Stylus võimaldab sul lihtsalt installida teemasid ja välimusi mitmetele populaarsetele saitidele.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "エクスポート",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "問題点",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "背景色",
@ -648,9 +648,14 @@
"message": "オプション UI",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "これらのルールを有効にして <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> で見つかった問題:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "これらのルールを有効にして $link$ で見つかった問題:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "ツールバーアイコンのバッジ",
@ -748,4 +753,4 @@
"message": "Stylus はユーザー スタイルを管理するツールで、ウェブのスタイルを変更することができます。Stylus を利用すると、さまざまなサイトに対応したテーマやスキンを簡単にインストールできます。",
"description": "Extension description"
}
}
}

View File

@ -11,9 +11,9 @@
"message": "Exporteren",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Problemen",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"cm_tabSize": {
"message": "Tabgrootte",
@ -344,9 +344,14 @@
"message": "Controleer alle stijlen op updates",
"description": "Label for the button to check all styles for updates"
},
"issuesHelp": {
"message": "De door CSSLint gevonden problemen, <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a>, met deze ingeschakelde regels:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "De door stylelint gevonden problemen, $link$, met deze ingeschakelde regels:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"confirmNo": {
"message": "Nee",
@ -396,4 +401,4 @@
"message": "Voorzie het web van een ander uiterlijk met Stylus, een gebruikersstijlbeheerder. Stylus stelt u in staat om eenvoudig thema's en stijlen te installeren voor vele populaire websites.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "Eksportuj",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Problemy",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Kolor tła",
@ -656,9 +656,14 @@
"message": "Opcje interfejsu",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Problemy znalezione przez <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> z tymi włączonymi regułami:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Problemy znalezione przez $link$ z tymi włączonymi regułami:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Emblemat na ikonie paska narzędzi",
@ -756,4 +761,4 @@
"message": "Przeprojektuj sieć za pomocą Stylusa - menedżera stylów użytkownika. Stylus umożliwia łatwe instalowanie motywów i skórek dla wielu popularnych stron.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "Экспорт",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Проблемы",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "Цвет фона",
@ -656,9 +656,14 @@
"message": "Настройки",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "Проблемы и предупреждения по версии <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> с данными включенными правилами:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Проблемы и предупреждения по версии $link$ с данными включенными правилами:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "Бейдж на пиктограмме в тулбаре",
@ -756,4 +761,4 @@
"message": "Настраивайте стили веб-сайтов с помощью менеджера стилей Stylus. Он позволяет легко установить темы и изменить внешний вид сайтов Google, Facebook, YouTube, Orkut и множества других веб-страниц.",
"description": "Extension description"
}
}
}

View File

@ -11,9 +11,9 @@
"message": "Извези",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "Проблеми",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"cm_tabSize": {
"message": "Величина картице",
@ -356,9 +356,14 @@
"message": "Проверите ажурирања за све стилове",
"description": "Label for the button to check all styles for updates"
},
"issuesHelp": {
"message": "Проблем пронађен од стране <a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> са овим омогућеним правилима:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "Проблем пронађен од стране $link$ са овим омогућеним правилима:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"confirmNo": {
"message": "Не",
@ -408,4 +413,4 @@
"message": "Измените стил интернет мреже управљачем корисничких стилова. Stylus вам омогућава да лако инсталирате теме и скинове за многе популарне сајтове.",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "导出",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "问题",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "背景颜色",
@ -656,9 +656,14 @@
"message": "设置用户界面",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "<a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a> 在已启用的这些规则中找到问题:",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "$link$ 在已启用的这些规则中找到问题:",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "样式计数器",
@ -756,4 +761,4 @@
"message": "Stylus 是一个调整网页外观的用户样式管理器。它可让您轻松地为许多热门网站网站安装主题和皮肤。",
"description": "Extension description"
}
}
}

View File

@ -27,9 +27,9 @@
"message": "導出",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
},
"issues": {
"linterIssues": {
"message": "問題",
"description": "Label for the CSSLint issues block on the style edit page"
"description": "Label for the CSS linter issues block on the style edit page"
},
"optionsBadgeNormal": {
"message": "背景顏色",
@ -660,9 +660,14 @@
"message": "選項介面",
"description": "Go to Options UI"
},
"issuesHelp": {
"message": "由<a href='https://github.com/CSSLint/csslint' target='_blank'>CSSLint</a>發現啟用這些規則會產生衝突",
"description": "Help popup message for the CSSLint issues block on the style edit page"
"linterIssuesHelp": {
"message": "由$link$發現啟用這些規則會產生衝突",
"description": "Help popup message for the selected CSS linter issues block on the style edit page",
"placeholders": {
"link": {
"content": "$1"
}
}
},
"optionsCustomizeBadge": {
"message": "在工具列圖示上的徽章",
@ -760,4 +765,4 @@
"message": "用Stylus一個用戶樣式管理器重塑網頁。 Stylus 讓你能為诸多主流網站輕鬆的安裝主題和皮膚。",
"description": "Extension description"
}
}
}

View File

@ -1,3 +1,4 @@
/* global LZString */
'use strict';
const RX_NAMESPACE = new RegExp([/[\s\r\n]*/,
@ -41,6 +42,26 @@ var chromeLocal = {
},
};
// eslint-disable-next-line no-var
var chromeSync = {
get(options) {
return new Promise(resolve => {
chrome.storage.sync.get(options, data => resolve(data));
});
},
set(data) {
return new Promise(resolve => {
chrome.storage.sync.set(data, () => resolve(data));
});
},
getValue(key) {
return chromeSync.get(key).then(data => tryJSONparse(LZString.decompressFromUTF16(data[key])));
},
setValue(key, value) {
return chromeSync.set({[key]: LZString.compressToUTF16(JSON.stringify(value))});
}
};
function dbExec(method, data) {
return new Promise((resolve, reject) => {

View File

@ -9,6 +9,7 @@
<script src="content/apply.js"></script>
<link rel="stylesheet" href="edit/edit.css">
<script src="edit/edit.js"></script>
<script src="edit/lint.js"></script>
<script src="vendor/codemirror/lib/codemirror.js"></script>
<link rel="stylesheet" href="vendor/codemirror/lib/codemirror.css">
@ -33,11 +34,6 @@
<script src="vendor/codemirror/addon/edit/matchbrackets.js"></script>
<link rel="stylesheet" href="vendor/codemirror/addon/lint/lint.css" />
<script src="vendor/csslint/csslint-worker.js"></script>
<script src="vendor/codemirror/addon/lint/lint.js"></script>
<script src="vendor-overwrites/codemirror/addon/lint/css-lint.js"></script>
<link rel="stylesheet" href="vendor/codemirror/addon/hint/show-hint.css" />
<script src="vendor/codemirror/addon/hint/show-hint.js"></script>
<script src="vendor/codemirror/addon/hint/css-hint.js"></script>
@ -186,8 +182,20 @@
<option i18n-text="genericDisabledLabel" value="">
</select>
</div>
<div class="option aligned">
<label id="linter-label" for="editor.linter" i18n-text="cm_linter"></label>
<select id="editor.linter">
<option value="csslint" selected>CSSLint</option>
<option value="stylelint">Stylelint</option>
<option value="null" i18n-text="genericDisabledLabel"></option>
</select>
<span class="linter-settings" i18n-title="linterConfigTooltip">
<svg id="linter-settings" class="svg-icon settings">
<use xlink:href="#svg-icon-settings"/>
</svg>&nbsp;
</span>
</section>
<section id="lint"><h2 i18n-text="issues">: <span id="issue-count"></span><svg id="lint-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2><div></div></section>
<section id="lint"><h2 i18n-text="linterIssues">: <span id="issue-count"></span><svg id="lint-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2><div></div></section>
</div>
<section id="sections">
<h2><span id="sections-heading" i18n-text="styleSectionsTitle"></span><svg id="sections-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2>
@ -207,6 +215,9 @@
<symbol id="svg-icon-close" height="16" width="12" viewBox="0 0 12 16">
<path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"></path>
</symbol>
<symbol id="svg-icon-settings" height="12" width="12" viewBox="0 0 16 16">
<path d="M16 9.45V6.52l-1.8-.3c-.14-.5-.34-.95-.56-1.35L14.7 3.4l-2.07-2.1-1.5 1.03c-.43-.24-.9-.43-1.37-.56L9.46 0H6.54l-.3 1.8c-.5.13-.94.33-1.36.55L3.4 1.32 1.3 3.4l1.07 1.45c-.24.45-.44.92-.57 1.42L0 6.53v2.95l1.8.3c.14.52.33.95.57 1.37l-1.07 1.5 2.06 2.08 1.5-1.07c.44.24.9.45 1.4.57l.3 1.77H9.5l.32-1.8c.47-.13.95-.32 1.34-.56l1.5 1.06 2.07-2.05-1.03-1.5c.24-.45.44-.9.56-1.36L16 9.44v-.02zm-8 1.6C6.3 11.05 4.93 9.7 4.93 8S6.33 4.9 8 4.9s3.06 1.4 3.06 3.1S9.7 11.04 8 11.04z"/>
</symbol>
</svg>
</body>

49
edit/csslint-config.js Normal file
View File

@ -0,0 +1,49 @@
'use strict';
/**
* CSSLint Config values
* 0 = disabled; 1 = warning; 2 = error
*/
window.csslintDefaultConfig = {
// Default warnings
'display-property-grouping': 1,
'duplicate-properties': 1,
'empty-rules': 1,
'errors': 1,
'known-properties': 1,
// Default disabled
'adjoining-classes': 0,
'box-model': 0,
'box-sizing': 0,
'bulletproof-font-face': 0,
'compatible-vendor-prefixes': 0,
'duplicate-background-images': 0,
'fallback-colors': 0,
'floats': 0,
'font-faces': 0,
'font-sizes': 0,
'gradients': 0,
'ids': 0,
'import': 0,
'import-ie-limit': 0,
'important': 0,
'order-alphabetical': 0,
'outline-none': 0,
'overqualified-elements': 0,
'qualified-headings': 0,
'regex-selectors': 0,
'rules-count': 0,
'selector-max': 0,
'selector-max-approaching': 0,
'selector-newline': 0,
'shorthand': 0,
'star-property-hack': 0,
'text-indent': 0,
'underscore-property-hack': 0,
'unique-headings': 0,
'universal-selector': 0,
'unqualified-attributes': 0,
'vendor-prefix': 0,
'zero-units': 0
};

View File

@ -73,16 +73,19 @@ input[type="checkbox"] {
h2 .svg-icon, label .svg-icon {
margin-top: -1px;
}
.svg-icon.info {
.svg-icon.info,
.svg-icon.settings {
width: 14px;
height: 16px;
}
.svg-icon:hover,
.svg-icon.info {
.svg-icon.info,
.svg-icon.settings {
fill: #666;
}
.svg-icon,
.svg-icon.info:hover {
.svg-icon.info:hover,
.svg-icon.settings:hover {
fill: #000;
}
#enabled {
@ -361,11 +364,26 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
max-height: calc(100vh - 8rem);
overflow-y: auto;
}
#help-popup .settings {
min-width: 500px;
min-height: 200px;
max-width: 48vw;
}
#help-popup .dismiss {
position: absolute;
right: 4px;
top: .5em;
}
#help-popup .saved-message {
display: none;
color: #090;
margin-left: 10px;
font-weight: bold;
}
#help-popup .saved-message.show,
#options .linter-settings {
display: inline-block;
}
.keymap-list {
font-size: 85%;
@ -397,6 +415,12 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
pointer-events: all;
opacity: 1.0;
}
#help-popup .rules {
padding: 0 15px;
}
#help-popup button {
margin-right: 3px;
}
/************ lint ************/
#lint {
@ -428,7 +452,6 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
#lint td[role="severity"] {
font-size: 0;
width: 16px;
padding-right: 0.25rem;
}
#lint td[role="line"], #lint td[role="sep"] {
text-align: right;
@ -441,6 +464,9 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
#lint td[role="message"] {
text-align: left;
}
#message-box.center.lint-config #message-box-contents {
text-align: left;
}
/************ CSS beautifier ************/
.beautify-options {
@ -543,7 +569,7 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
margin-bottom: 0;
}
#lint > div {
max-height: 0;
max-height: 20vh;
}
#lint.collapsed > div {
display: none;
@ -552,6 +578,12 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
margin-top: 1em;
max-height: 30vh;
}
#lint table {
width: 100%;
}
#lint td[role="message"] {
max-width: none;
}
#sections {
padding-left: 0;
}

View File

@ -1,12 +1,17 @@
/* eslint brace-style: 0, operator-linebreak: 0 */
/* global CodeMirror exports parserlib CSSLint */
/* global CodeMirror parserlib */
/* global exports css_beautify onDOMscripted */
/* global CSSLint initLint getLinterConfigForCodeMirror updateLintReport renderLintReport updateLinter */
'use strict';
let styleId = null;
let dirty = {}; // only the actually dirty items here
const editors = []; // array of all CodeMirror instances
// only the actually dirty items here
let dirty = {};
// array of all CodeMirror instances
const editors = [];
let saveSizeOnClose;
let useHistoryBack; // use browser history back when 'back to manage' is clicked
// use browser history back when 'back to manage' is clicked
let useHistoryBack;
// direct & reverse mapping of @-moz-document keywords and internal property names
const propertyToCss = {urls: 'url', urlPrefixes: 'url-prefix', domains: 'domain', regexps: 'regexp'};
@ -32,7 +37,8 @@ Element.prototype.closest = Element.prototype.closest || function (selector) {
};
// eslint-disable-next-line no-extend-native
Array.prototype.rotate = function (amount) { // negative amount == rotate left
Array.prototype.rotate = function (amount) {
// negative amount == rotate left
const r = this.slice(-amount, this.length);
Array.prototype.push.apply(r, this.slice(0, this.length - r.length));
return r;
@ -43,7 +49,7 @@ Object.defineProperty(Array.prototype, 'last', {get: function () { return this[t
// preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs()
new MutationObserver((mutations, observer) => {
const themeElement = document.getElementById('cm-theme');
const themeElement = $('#cm-theme');
if (themeElement) {
themeElement.href = prefs.get('editor.theme') === 'default' ? ''
: 'vendor/codemirror/theme/' + prefs.get('editor.theme') + '.css';
@ -91,7 +97,8 @@ function onChange(event) {
} 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
// only valid when actually saved
delete node.savedValue;
}
updateTitle();
}
@ -124,7 +131,7 @@ function setCleanItem(node, isClean) {
function isCleanGlobal() {
const clean = Object.keys(dirty).length === 0;
setDirtyClass(document.body, !clean);
// let saveBtn = document.getElementById('save-button')
// let saveBtn = $('#save-button')
// if (clean){
// //saveBtn.removeAttribute('disabled');
// }else{
@ -134,12 +141,13 @@ function isCleanGlobal() {
}
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
$$('#header, #sections > div').forEach(setCleanSection);
// forget the dirty applies-to ids from a deleted section after the style was saved
dirty = {};
}
function setCleanSection(section) {
section.querySelectorAll('.style-contributor').forEach(node => { setCleanItem(node, true); });
$$('.style-contributor', section).forEach(node => { setCleanItem(node, true); });
// #header section has no codemirror
const cm = section.CodeMirror;
@ -152,6 +160,9 @@ function setCleanSection(section) {
function initCodeMirror() {
const CM = CodeMirror;
const isWindowsOS = navigator.appVersion.indexOf('Windows') > 0;
// lint.js is not loaded initially
const hasLinter = typeof getLinterConfigForCodeMirror !== 'undefined' ?
getLinterConfigForCodeMirror(prefs.get('editor.linter')) : false;
// CodeMirror miserably fails on keyMap='' so let's ensure it's not
if (!prefs.get('editor.keyMap')) {
@ -168,12 +179,13 @@ function initCodeMirror() {
matchBrackets: true,
highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true},
hintOptions: {},
lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get('editor.lintDelay')},
lint: hasLinter,
lintReportDelay: prefs.get('editor.lintReportDelay'),
styleActiveLine: true,
theme: 'default',
keyMap: prefs.get('editor.keyMap'),
extraKeys: { // independent of current keyMap
extraKeys: {
// independent of current keyMap
'Alt-Enter': 'toggleStyle',
'Alt-PageDown': 'nextEditor',
'Alt-PageUp': 'prevEditor'
@ -203,9 +215,12 @@ function initCodeMirror() {
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
// will be used by 'sublime' on PC via fallthrough
CM.keyMap.pcDefault['Ctrl-Space'] = 'autocomplete';
// OSX uses Ctrl-Space and Cmd-Space for something else
CM.keyMap.macDefault['Alt-Space'] = 'autocomplete';
// copied from 'emacs' keymap
CM.keyMap.emacsy['Alt-/'] = 'autocomplete';
// 'vim' and 'emacs' define their own autocomplete hotkeys
}
if (!extraKeysCommands.blockComment) {
@ -223,8 +238,10 @@ function initCodeMirror() {
// try to remap non-interceptable Ctrl-(Shift-)N/T/W hotkeys
['N', 'T', 'W'].forEach(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
[
{from: 'Ctrl-', to: ['Alt-', 'Ctrl-Alt-']},
// Note: modifier order in CM is S-C-A
{from: 'Shift-Ctrl-', to: ['Ctrl-Alt-', 'Shift-Ctrl-Alt-']}
].forEach(remap => {
const oldKey = remap.from + char;
Object.keys(CM.keyMap).forEach(keyMapName => {
@ -267,7 +284,8 @@ function initCodeMirror() {
}
parent.appendChild(fragment);
}
const themeControl = document.getElementById('editor.theme');
// no need to escape the period in the id
const themeControl = $('#editor.theme');
const themeList = localStorage.codeMirrorThemes;
if (themeList) {
optionsFromArray(themeControl, themeList.split(/\s+/));
@ -282,7 +300,7 @@ function initCodeMirror() {
});
}
optionsFromArray($('#editor.keyMap'), Object.keys(CM.keyMap).sort());
document.getElementById('options').addEventListener('change', acmeEventListener, false);
$('#options').addEventListener('change', acmeEventListener, false);
setupLivePrefs();
hotkeyRerouter.setState(true);
@ -302,7 +320,7 @@ function acmeEventListener(event) {
CodeMirror.setOption('indentUnit', Number(value));
break;
case 'theme': {
const themeLink = document.getElementById('cm-theme');
const themeLink = $('#cm-theme');
// use non-localized 'default' internally
if (!value || value === 'default' || value === t('defaultTheme')) {
value = 'default';
@ -314,7 +332,8 @@ function acmeEventListener(event) {
break;
}
const url = chrome.runtime.getURL('vendor/codemirror/theme/' + value + '.css');
if (themeLink.href === url) { // preloaded in initCodeMirror()
if (themeLink.href === url) {
// preloaded in initCodeMirror()
break;
}
// avoid flicker: wait for the second stylesheet to load, then apply the theme
@ -348,6 +367,10 @@ function acmeEventListener(event) {
default:
value = null;
}
break;
case 'linter':
updateLinter(value);
break;
}
CodeMirror.setOption(option, value);
}
@ -391,8 +414,10 @@ function setupCodeMirror(textarea, index) {
}
lastClickTime = Date.now();
const minHeight = cm.defaultTextHeight() +
cm.display.lineDiv.offsetParent.offsetTop + /* .CodeMirror-lines padding */
wrapper.offsetHeight - wrapper.clientHeight; /* borders */
/* .CodeMirror-lines padding */
cm.display.lineDiv.offsetParent.offsetTop +
/* borders */
wrapper.offsetHeight - wrapper.clientHeight;
wrapper.style.pointerEvents = 'none';
document.body.style.cursor = 's-resize';
function resize(e) {
@ -419,7 +444,7 @@ function indicateCodeChange(cm) {
const section = cm.getSection();
setCleanItem(section, cm.isClean(section.savedValue));
updateTitle();
updateLintReport(cm);
updateLintReportIfEnabled(cm);
}
function getSectionForChild(e) {
@ -427,13 +452,13 @@ function getSectionForChild(e) {
}
function getSections() {
return document.querySelectorAll('#sections > div');
return $$('#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', () => {
const style = document.getElementById('name').style;
const style = $('#name').style;
style.webkitTransform = style.webkitTransform ? '' : 'scale(1)';
});
@ -546,12 +571,12 @@ window.onbeforeunload = () => {
if (isCleanGlobal()) {
return;
}
updateLintReport(null, 0);
updateLintReportIfEnabled(null, 0);
return confirm(t('styleChangesNotSaved'));
};
function addAppliesTo(list, name, value) {
const showingEverything = list.querySelector('.applies-to-everything') !== null;
const showingEverything = $('.applies-to-everything', list) !== null;
// blow away 'Everything' if it's there
if (showingEverything) {
list.removeChild(list.firstChild);
@ -559,19 +584,19 @@ function addAppliesTo(list, name, value) {
let 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);
$('[name=applies-type]', e).value = name;
$('[name=applies-value]', e).value = value;
$('.remove-applies-to', e).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;
$('[name=applies-type]', e).value = $('li:last-child [name="applies-type"]', list).value;
}
e.querySelector('.remove-applies-to').addEventListener('click', removeAppliesTo, false);
$('.remove-applies-to', e).addEventListener('click', removeAppliesTo, false);
} else {
e = template.appliesToEverything.cloneNode(true);
}
e.querySelector('.add-applies-to').addEventListener('click', function () {
$('.add-applies-to', e).addEventListener('click', function () {
addAppliesTo(this.parentNode.parentNode);
}, false);
list.appendChild(e);
@ -579,13 +604,13 @@ function addAppliesTo(list, name, value) {
function addSection(event, section) {
const 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);
$('.applies-to-help', div).addEventListener('click', showAppliesToHelp, false);
$('.remove-section', div).addEventListener('click', removeSection, false);
$('.add-section', div).addEventListener('click', addSection, false);
$('.beautify-section', div).addEventListener('click', beautify);
const codeElement = div.querySelector('.code');
const appliesTo = div.querySelector('.applies-to-list');
const codeElement = $('.code', div);
const appliesTo = $('.applies-to-list', div);
let appliesToAdded = false;
if (section) {
@ -608,24 +633,25 @@ function addSection(event, section) {
toggleTestRegExpVisibility();
appliesTo.addEventListener('change', toggleTestRegExpVisibility);
div.querySelector('.test-regexp').onclick = showRegExpTester;
$('.test-regexp', div).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());
$('.applies-type', item).value === 'regexp' &&
$('.applies-value', item).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'
$('.applies-type', event.target.parentElement).value === 'regexp'
) {
showRegExpTester(null, div);
}
});
}
const sections = document.getElementById('sections');
const sections = $('#sections');
let cm;
if (event) {
const clickedSection = getSectionForChild(event.target);
@ -639,7 +665,6 @@ function addSection(event, section) {
sections.appendChild(div);
cm = setupCodeMirror(codeElement);
}
div.CodeMirror = cm;
setCleanSection(div);
return div;
@ -663,7 +688,7 @@ function removeSection(event) {
}
function removeAreaAndSetDirty(area) {
const contributors = area.querySelectorAll('.style-contributor');
const contributors = $$('.style-contributor', area);
if (!contributors.length) {
setCleanItem(area, false);
}
@ -707,9 +732,11 @@ function setupGlobalSearch() {
const originalOpenDialog = CodeMirror.prototype.openDialog;
const originalOpenConfirm = CodeMirror.prototype.openConfirm;
let curState; // cm.state.search for last used 'find'
// cm.state.search for last used 'find'
let curState;
function shouldIgnoreCase(query) { // treat all-lowercase non-regexp queries as case-insensitive
function shouldIgnoreCase(query) {
// treat all-lowercase non-regexp queries as case-insensitive
return typeof query === 'string' && query === query.toLowerCase();
}
@ -779,7 +806,8 @@ function setupGlobalSearch() {
return;
}
let pos = activeCM.getCursor(reverse ? 'from' : 'to');
activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor
// clear the selection, don't move the cursor
activeCM.setSelection(activeCM.getCursor());
const rxQuery = typeof state.query === 'object'
? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? 'i' : '');
@ -820,7 +848,7 @@ function setupGlobalSearch() {
originalCommand[reverse ? 'findPrev' : 'findNext'](activeCM);
function searchAppliesTo(cm) {
let inputs = [].slice.call(cm.getSection().querySelectorAll('.applies-value'));
let inputs = $$('.applies-value', cm.getSection());
if (reverse) {
inputs = inputs.reverse();
}
@ -883,7 +911,7 @@ function setupGlobalSearch() {
} else {
doConfirm(cm);
callback(replacement);
if (!cm.getWrapperElement().querySelector('.CodeMirror-dialog')) {
if (!$('.CodeMirror-dialog', cm.getWrapperElement())) {
// no dialog == nothing found in the current CM, move to the next
doReplace();
}
@ -907,7 +935,7 @@ function setupGlobalSearch() {
const cmp = CodeMirror.cmpPos(cm.getCursor(), pos);
wrapAround |= cmp <= 0;
const dlg = cm.getWrapperElement().querySelector('.CodeMirror-dialog');
const dlg = $('.CodeMirror-dialog', cm.getWrapperElement());
if (!dlg || cmp === 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) {
if (dlg) {
dlg.remove();
@ -1000,14 +1028,14 @@ function autocompletePicked(cm) {
function refocusMinidialog(cm) {
const section = cm.getSection();
if (!section.querySelector('.CodeMirror-dialog')) {
if (!$('.CodeMirror-dialog', section)) {
return;
}
// close the currently opened minidialog
cm.focus();
// make sure to focus the input in newly opened minidialog
setTimeout(() => {
section.querySelector('.CodeMirror-dialog').focus();
$('.CodeMirror-dialog', section).focus();
}, 0);
}
@ -1037,7 +1065,8 @@ function getEditorInSight(nearbyElement) {
return cm;
function offscreenDistance(cm) {
const LINES_VISIBLE = 2; // closest editor should have at least # lines visible
// closest editor should have at least # lines visible
const LINES_VISIBLE = 2;
const bounds = cm.getSection().getBoundingClientRect();
if (bounds.top < 0) {
return -bounds.top;
@ -1049,145 +1078,11 @@ function getEditorInSight(nearbyElement) {
}
}
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;
}
// eslint-disable-next-line no-var
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) {
const scope = cm ? [cm] : editors;
let changed = false;
let fixedOldIssues = false;
scope.forEach(cm => {
const scopedState = cm.state.lint || {};
const oldMarkers = scopedState.markedLast || {};
const newMarkers = {};
const html = !scopedState.marked || scopedState.marked.length === 0 ? '' : '<tbody>' +
scopedState.marked.map(mark => {
const info = mark.__annotation;
const isActiveLine = info.from.line === cm.getCursor().line;
const pos = isActiveLine ? 'cursor' : (info.from.line + ',' + info.from.ch);
let 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 '<tr class="' + info.severity + '">' +
'<td role="severity" class="CodeMirror-lint-marker-' + info.severity + '">' +
info.severity + '</td>' +
'<td role="line">' + (info.from.line + 1) + '</td>' +
'<td role="sep">:</td>' +
'<td role="col">' + (info.from.ch + 1) + '</td>' +
'<td role="message">' + message + '</td></tr>';
}).join('') + '</tbody>';
scopedState.markedLast = newMarkers;
fixedOldIssues |= scopedState.reportDisplayed && Object.keys(oldMarkers).length > 0;
if (scopedState.html !== html) {
scopedState.html = html;
changed = true;
}
});
if (changed) {
clearTimeout(state ? state.renderTimeout : undefined);
if (!state || !state.postponeNewIssues || fixedOldIssues) {
renderLintReport(true);
} else {
state.renderTimeout = setTimeout(() => {
renderLintReport(true);
}, CodeMirror.defaults.lintReportDelay);
}
}
}
function escapeHtml(html) {
const chars = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;'};
return html.replace(/[&<>"'/]/g, char => chars[char]);
}
}
function renderLintReport(someBlockChanged) {
const container = document.getElementById('lint');
const content = container.children[1];
const label = t('sectionCode');
const newContent = content.cloneNode(false);
let issueCount = 0;
editors.forEach((cm, index) => {
if (cm.state.lint && cm.state.lint.html) {
const html = '<caption>' + label + ' ' + (index + 1) + '</caption>' + cm.state.lint.html;
const newBlock = newContent.appendChild(tHTML(html, 'table'));
newBlock.cm = cm;
issueCount += newBlock.rows.length;
const block = content.children[newContent.children.length - 1];
const 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) {
const bounds = content.getBoundingClientRect();
const newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + 'px';
if (newMaxHeight !== content.style.maxHeight) {
content.style.maxHeight = newMaxHeight;
}
}
}
function gotoLintIssue(event) {
const issue = event.target.closest('tr');
if (!issue) {
return;
}
const 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');
}
function beautify(event) {
if (exports.css_beautify) { // thanks to csslint's definition of 'exports'
doBeautify();
} else {
const script = document.head.appendChild(document.createElement('script'));
script.src = 'vendor-overwrites/beautify/beautify-css-mod.js';
script.onload = doBeautify;
}
const script = $('script[src*="beautify-css-mod"]') ?
[] : ['vendor-overwrites/beautify/beautify-css-mod.js'];
onDOMscripted(script).then(doBeautify);
function doBeautify() {
const tabs = prefs.get('editor.indentWithTabs');
const options = prefs.get('editor.beautify');
@ -1210,7 +1105,7 @@ function beautify(event) {
'</div>' +
'<div><button role="undo"></button></div>');
const undoButton = document.querySelector('#help-popup button[role="undo"]');
const undoButton = $('#help-popup button[role="undo"]');
undoButton.textContent = t(scope.length === 1 ? 'undo' : 'undoGlobal');
undoButton.addEventListener('click', () => {
let undoable = false;
@ -1231,7 +1126,7 @@ function beautify(event) {
[].concat.apply([], cm.doc.sel.ranges.map(r =>
[Object.assign({}, r.anchor), Object.assign({}, r.head)]));
const text = cm.getValue();
const newText = exports.css_beautify(text, options);
const newText = 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
@ -1249,7 +1144,7 @@ function beautify(event) {
}, 0);
});
document.querySelector('.beautify-options').onchange = ({target}) => {
$('.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')) {
@ -1275,7 +1170,8 @@ document.addEventListener('DOMContentLoaded', init);
function init() {
initCodeMirror();
const params = getParams();
if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses
if (!params.id) {
// match should be 2 - one for the whole thing, one for the parentheses
// This is an add
$('#heading').textContent = t('addStyleTitle');
const section = {code: ''};
@ -1289,7 +1185,7 @@ function init() {
addSection(null, section);
editors[0].setOption('lint', CodeMirror.defaults.lint);
// default to enabled
document.getElementById('enabled').checked = true;
$('#enabled').checked = true;
initHooks();
};
return;
@ -1316,9 +1212,9 @@ function init() {
}
function setStyleMeta(style) {
document.getElementById('name').value = style.name;
document.getElementById('enabled').checked = style.enabled;
document.getElementById('url').href = style.url;
$('#name').value = style.name;
$('#enabled').checked = style.enabled;
$('#url').href = style.url;
}
function initWithStyle({style, codeIsUpdated}) {
@ -1350,35 +1246,30 @@ function initWithStyle({style, codeIsUpdated}) {
const 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'));
if (CodeMirror.lint) {
setTimeout(() => {
cm.setOption('lint', CodeMirror.defaults.lint);
updateLintReport(cm, 0);
}, prefs.get('editor.lintDelay'));
}
}
}
function initHooks() {
document.querySelectorAll('#header .style-contributor').forEach(node => {
$$('#header .style-contributor').forEach(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);
}
$('#toggle-style-help').addEventListener('click', showToggleStyleHelp);
$('#to-mozilla').addEventListener('click', showMozillaFormat, false);
$('#to-mozilla-help').addEventListener('click', showToMozillaHelp, false);
$('#from-mozilla').addEventListener('click', fromMozillaFormat);
$('#beautify').addEventListener('click', beautify);
$('#save-button').addEventListener('click', save, false);
$('#sections-help').addEventListener('click', showSectionHelp, false);
$('#keyMap-help').addEventListener('click', showKeyMapHelp, false);
$('#cancel-button').addEventListener('click', goBackToManage);
initLint();
if (!FIREFOX) {
$$([
@ -1443,7 +1334,7 @@ function maximizeCodeHeight(sectionDiv, isLast) {
return;
}
// scale heights to fill the gap between last section and bottom edge of the window
const sections = document.getElementById('sections');
const sections = $('#sections');
const available = window.innerHeight - sections.getBoundingClientRect().bottom -
parseFloat(getComputedStyle(sections).marginBottom);
if (available <= 0) {
@ -1460,25 +1351,25 @@ function maximizeCodeHeight(sectionDiv, isLast) {
function updateTitle() {
const DIRTY_TITLE = '* $';
const name = document.getElementById('name').savedValue;
const name = $('#name').savedValue;
const clean = isCleanGlobal();
const title = styleId === null ? t('addStyleTitle') : t('editStyleTitle', [name]);
document.title = clean ? title : DIRTY_TITLE.replace('$', title);
}
function validate() {
const name = document.getElementById('name').value;
const name = $('#name').value;
if (name === '') {
return t('styleMissingName');
}
// validate the regexps
if (document.querySelectorAll('.applies-to-list').some(list => {
if ($$('.applies-to-list').some(list => {
list.childNodes.some(li => {
if (li.className === template.appliesToEverything.className) {
return false;
}
const valueElement = li.querySelector('[name=applies-value]');
const type = li.querySelector('[name=applies-type]').value;
const valueElement = $('[name=applies-value]', li);
const type = $('[name=applies-type]', li).value;
const value = valueElement.value;
if (type && value) {
if (type === 'regexp') {
@ -1498,8 +1389,14 @@ function validate() {
return null;
}
function updateLintReportIfEnabled(cm, time) {
if (CodeMirror.lint) {
updateLintReport(cm, time);
}
}
function save() {
updateLintReport(null, 0);
updateLintReportIfEnabled(null, 0);
// save the contents of the CodeMirror editors back into the textareas
for (let i = 0; i < editors.length; i++) {
@ -1511,8 +1408,8 @@ function save() {
alert(error);
return;
}
const name = document.getElementById('name').value;
const enabled = document.getElementById('enabled').checked;
const name = $('#name').value;
const enabled = $('#enabled').checked;
saveStyleSafe({
id: styleId,
name: name,
@ -1539,12 +1436,12 @@ function getSectionsHashes() {
function getMeta(e) {
const meta = {urls: [], urlPrefixes: [], domains: [], regexps: []};
e.querySelector('.applies-to-list').childNodes.forEach(li => {
$('.applies-to-list', e).childNodes.forEach(li => {
if (li.className === template.appliesToEverything.className) {
return;
}
const type = li.querySelector('[name=applies-type]').value;
const value = li.querySelector('[name=applies-value]').value;
const type = $('[name=applies-type]', li).value;
const value = $('[name=applies-value]', li).value;
if (type && value) {
const property = CssToProperty[type];
meta[property].push(value);
@ -1593,12 +1490,12 @@ function fromMozillaFormat() {
</div>`
));
const contents = popup.querySelector('.contents');
const contents = $('.contents', popup);
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);
$('[name="import-append"]', popup).addEventListener('click', doImport);
$('[name="import-replace"]', popup).addEventListener('click', doImport);
popup.codebox.on('change', () => {
clearTimeout(popup.mozillaTimeout);
@ -1607,9 +1504,15 @@ function fromMozillaFormat() {
}, 100);
});
function doImport() {
const replaceOldStyle = this.name === 'import-replace';
popup.querySelector('.dismiss').onclick();
function doImport(event) {
// parserlib contained in CSSLint-worker.js
onDOMscripted(['vendor-overwrites/csslint/csslint-worker.js'])
.then(() => doImportWhenReady(event.target));
}
function doImportWhenReady(target) {
const replaceOldStyle = target.name === 'import-replace';
$('.dismiss', popup).onclick();
const mozStyle = trimNewLines(popup.codebox.getValue());
const parser = new parserlib.css.Parser();
const lines = mozStyle.split('\n');
@ -1668,7 +1571,7 @@ function fromMozillaFormat() {
firstAddedCM.focus();
if (errors.length) {
showHelp(t('issues'), $element({
showHelp(t('linterIssues'), $element({
tag: 'pre',
textContent: errors.join('\n'),
}));
@ -1718,8 +1621,12 @@ function fromMozillaFormat() {
// 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 */
if (
/* ignore boilerplate NS */
!section.code.replace('@namespace url(http://www.w3.org/1999/xhtml);', '')
/* ignore all whitespace including new lines */
.replace(/[\s\n]/g, '')
) {
return false;
}
if (replaceOldStyle) {
@ -1728,7 +1635,7 @@ function fromMozillaFormat() {
});
} else if (!editors.last.getValue()) {
// nuke the last blank section
if (editors.last.getSection().querySelector('.applies-to-everything')) {
if ($('.applies-to-everything', editors.last.getSection())) {
removeSection({target: editors.last.getSection()});
}
}
@ -1780,10 +1687,10 @@ function showKeyMapHelp() {
'</tbody>' +
'</table>');
const table = document.querySelector('#help-popup table');
const table = $('#help-popup table');
table.addEventListener('input', filterTable);
const inputs = table.querySelectorAll('input');
const inputs = $$('input', table);
inputs[0].addEventListener('keydown', hotkeyHandler);
inputs[1].focus();
@ -1865,24 +1772,17 @@ function showKeyMapHelp() {
}
}
function showLintHelp() {
showHelp(t('issues'), t('issuesHelp') + '<ul>' +
CSSLint.getRules().map(rule =>
'<li><b>' + rule.name + '</b><br>' + rule.desc + '</li>'
).join('') + '</ul>'
);
}
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]
const regexps = [...$('.applies-to-list', section).children]
.map(item =>
!item.matches('.applies-to-everything') &&
item.querySelector('.applies-type').value === 'regexp' &&
item.querySelector('.applies-value').value.trim())
$('.applies-type', item).value === 'regexp' &&
$('.applies-value', item).value.trim()
)
.filter(item => item)
.map(text => {
const rxData = Object.assign({text}, cachedRegexps.get(text));
@ -1895,7 +1795,7 @@ function showRegExpTester(event, section = getSectionForChild(this)) {
return rxData;
});
chrome.tabs.onUpdated.addListener(function _(tabId, info) {
if (document.querySelector('.regexp-report')) {
if ($('.regexp-report')) {
if (info.url) {
showRegExpTester(event, section);
}
@ -2003,7 +1903,7 @@ function showRegExpTester(event, section = getSectionForChild(this)) {
}
showHelp(t('styleRegexpTestTitle'), report);
document.querySelector('.regexp-report').onclick = event => {
$('.regexp-report').onclick = event => {
const target = event.target.closest('a, .regexp-report div');
if (target) {
openURL({url: target.href || target.textContent});
@ -2022,7 +1922,8 @@ function showHelp(title, body) {
if (getComputedStyle(div).display === 'none') {
document.addEventListener('keydown', closeHelp);
div.querySelector('.dismiss').onclick = closeHelp; // avoid chaining on multiple showHelp() calls
// avoid chaining on multiple showHelp() calls
$('.dismiss', div).onclick = closeHelp;
}
div.style.display = 'block';
@ -2035,7 +1936,9 @@ function showHelp(title, body) {
((e.keyCode || e.which) === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey)
) {
div.style.display = '';
document.querySelector('.contents').textContent = '';
const contents = $('.contents');
contents.textContent = '';
clearTimeout(contents.timer);
document.removeEventListener('keydown', closeHelp);
}
}
@ -2045,14 +1948,14 @@ function showCodeMirrorPopup(title, html, options) {
const popup = showHelp(title, html);
popup.classList.add('big');
popup.codebox = CodeMirror(popup.querySelector('.contents'), Object.assign({
popup.codebox = CodeMirror($('.contents', popup), 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},
lint: getLinterConfigForCodeMirror(prefs.get('editor.linter')),
styleActiveLine: true,
theme: prefs.get('editor.theme'),
keyMap: prefs.get('editor.keyMap')

432
edit/lint.js Normal file
View File

@ -0,0 +1,432 @@
/* global CodeMirror messageBox */
/* global editors makeSectionVisible showCodeMirrorPopup showHelp */
/* global stylelintDefaultConfig csslintDefaultConfig onDOMscripted injectCSS require */
'use strict';
function initLint() {
$('#lint-help').addEventListener('click', showLintHelp);
$('#lint').addEventListener('click', gotoLintIssue);
window.addEventListener('resize', resizeLintReport);
$('#linter-settings').addEventListener('click', openStylelintSettings);
// touch devices don't have onHover events so the element we'll be toggled via clicking (touching)
if ('ontouchstart' in document.body) {
$('#lint h2').addEventListener('click', toggleLintReport);
}
// initialize storage of linter config
BG.chromeSync.getValue('editorStylelintConfig').then(config => setStylelintConfig(config));
BG.chromeSync.getValue('editorCSSLintConfig').then(config => setCSSLintConfig(config));
}
function setStylelintConfig(config) {
// can't use default parameters, because config may be null
if (Object.keys(config || []).length === 0 && typeof stylelintDefaultConfig !== 'undefined') {
config = deepCopy(stylelintDefaultConfig.rules);
}
BG.chromeSync.setValue('editorStylelintConfig', config);
return config;
}
function setCSSLintConfig(config) {
if (Object.keys(config || []).length === 0 && typeof csslintDefaultConfig !== 'undefined') {
config = Object.assign({}, csslintDefaultConfig);
}
BG.chromeSync.setValue('editorCSSLintConfig', config);
return config;
}
function getLinterConfigForCodeMirror(name) {
return CodeMirror.lint && CodeMirror.lint[name] ? {
getAnnotations: CodeMirror.lint[name],
delay: prefs.get('editor.lintDelay')
} : false;
}
function updateLinter(linter) {
function updateEditors() {
const options = getLinterConfigForCodeMirror(linter);
CodeMirror.defaults.lint = options === 'null' ? false : options;
editors.forEach(cm => {
// set lint to "null" to disable
cm.setOption('lint', options);
// enabling/disabling linting changes the gutter width
cm.refresh();
updateLintReport(cm, 200);
});
}
// load scripts
loadSelectedLinter(linter).then(() => {
updateEditors();
});
$('#linter-settings').style.display = linter === 'null' ? 'none' : 'inline-block';
}
function updateLintReport(cm, delay) {
if (delay === 0) {
// immediately show pending csslint/stylelint messages in onbeforeunload and save
update(cm);
return;
}
if (delay > 0) {
setTimeout(cm => {
cm.performLint();
update(cm);
}, delay, cm);
return;
}
// eslint-disable-next-line no-var
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) {
const scope = cm ? [cm] : editors;
let changed = false;
let fixedOldIssues = false;
scope.forEach(cm => {
const scopedState = cm.state.lint || {};
const oldMarkers = scopedState.markedLast || {};
const newMarkers = {};
const html = !scopedState.marked || scopedState.marked.length === 0 ? '' : '<tbody>' +
scopedState.marked.map(mark => {
const info = mark.__annotation;
const isActiveLine = info.from.line === cm.getCursor().line;
const pos = isActiveLine ? 'cursor' : (info.from.line + ',' + info.from.ch);
// stylelint rule added in parentheses at the end; extract it out for the stylelint info popup
const lintRuleName = info.message
.substring(info.message.lastIndexOf('('), info.message.length)
.replace(/[()]/g, '');
const title = escapeHtml(info.message);
const message = title.length > 100 ? title.substr(0, 100) + '...' : title;
if (isActiveLine || oldMarkers[pos] === message) {
delete oldMarkers[pos];
}
newMarkers[pos] = message;
return `<tr class="${info.severity}">
<td role="severity" data-rule="${lintRuleName}">
<div class="CodeMirror-lint-marker-${info.severity}">${info.severity}</div>
</td>
<td role="line">${info.from.line + 1}</td>
<td role="sep">:</td>
<td role="col">${info.from.ch + 1}</td>
<td role="message" title="${title}">${message}</td>
</tr>`;
}).join('') + '</tbody>';
scopedState.markedLast = newMarkers;
fixedOldIssues |= scopedState.reportDisplayed && Object.keys(oldMarkers).length > 0;
if (scopedState.html !== html) {
scopedState.html = html;
changed = true;
}
});
if (changed) {
clearTimeout(state ? state.renderTimeout : undefined);
if (!state || !state.postponeNewIssues || fixedOldIssues) {
renderLintReport(true);
} else {
state.renderTimeout = setTimeout(() => {
renderLintReport(true);
}, CodeMirror.defaults.lintReportDelay);
}
}
}
function escapeHtml(html) {
const chars = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;'};
return html.replace(/[&<>"'/]/g, char => chars[char]);
}
}
function renderLintReport(someBlockChanged) {
const container = $('#lint');
const content = container.children[1];
const label = t('sectionCode');
const newContent = content.cloneNode(false);
let issueCount = 0;
editors.forEach((cm, index) => {
if (cm.state.lint && cm.state.lint.html) {
const html = '<caption>' + label + ' ' + (index + 1) + '</caption>' + cm.state.lint.html;
const newBlock = newContent.appendChild(tHTML(html, 'table'));
newBlock.cm = cm;
issueCount += newBlock.rows.length;
const block = content.children[newContent.children.length - 1];
const blockChanged = !block || cm !== block.cm || html !== block.innerHTML;
someBlockChanged |= blockChanged;
cm.state.lint.reportDisplayed = blockChanged;
}
});
if (someBlockChanged || newContent.children.length !== content.children.length) {
$('#issue-count').textContent = issueCount;
container.replaceChild(newContent, content);
container.style.display = newContent.children.length ? 'block' : 'none';
resizeLintReport();
}
}
function resizeLintReport() {
// subtracted value to prevent scrollbar
const magicBuffer = 20;
const content = $('#lint table');
if (content) {
const bounds = content.getBoundingClientRect();
const newMaxHeight = bounds.bottom <= window.innerHeight ? '' :
// subtract out a bit of padding or the vertical scrollbar extends beyond the viewport
(window.innerHeight - bounds.top - magicBuffer) + 'px';
if (newMaxHeight !== content.style.maxHeight) {
content.parentNode.style.maxHeight = newMaxHeight;
}
}
}
function gotoLintIssue(event) {
const issue = event.target.closest('tr');
if (!issue) {
return;
}
const block = issue.closest('table');
makeSectionVisible(block.cm);
block.cm.focus();
block.cm.setSelection({
line: parseInt($('td[role="line"]', issue).textContent) - 1,
ch: parseInt($('td[role="col"]', issue).textContent) - 1
});
}
function toggleLintReport() {
$('#lint').classList.toggle('collapsed');
}
function showLintHelp() {
const makeLink = (url, txt) => `<a target="_blank" href="${url}">${txt}</a>`;
const linter = prefs.get('editor.linter');
const url = linter === 'stylelint'
? 'https://stylelint.io/user-guide/rules/'
// some CSSLint rules do not have a url
: 'https://github.com/CSSLint/csslint/issues/535';
const rules = [];
let template;
let list = '<ul class="rules">';
let header = '';
if (linter === 'csslint') {
const CSSLintRules = window.CSSLint.getRules();
const findCSSLintRule = id => CSSLintRules.find(rule => rule.id === id);
header = t('linterIssuesHelp', makeLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint'));
template = ruleID => {
const rule = findCSSLintRule(ruleID);
return rule ? `<li><b>${makeLink(rule.url || url, rule.name)}</b><br>${rule.desc}</li>` : '';
};
} else {
header = t('linterIssuesHelp', makeLink(url, 'stylelint'));
template = rule => `<li>${makeLink(url + rule, rule)}</li>`;
}
// Only show rules with issues in the popup
$$('#lint td[role="severity"]').forEach(el => {
const rule = el.dataset.rule;
if (!rules.includes(rule)) {
list += template(rule);
rules.push(rule);
}
});
return showHelp(t('linterIssues'), header + list + '</ul>');
}
function showLinterErrorMessage(title, contents) {
messageBox({
title,
contents,
className: 'danger center lint-config',
buttons: [t('confirmOK')],
});
}
function showSavedMessage() {
$('#help-popup .saved-message').classList.add('show');
clearTimeout($('#help-popup .contents').timer);
$('#help-popup .contents').timer = setTimeout(() => {
// popup may be closed at this point
const msg = $('#help-popup .saved-message');
if (msg) {
msg.classList.remove('show');
}
}, 2000);
}
function checkLinter(linter = prefs.get('editor.linter')) {
linter = linter.toLowerCase();
if (prefs.get('editor.linter') !== linter) {
prefs.set('editor.linter', linter);
}
return linter;
}
function checkConfigRules(linter, config) {
const invalid = [];
const linterRules = linter === 'stylelint'
? Object.keys(window.stylelint.rules)
: window.CSSLint.getRules().map(rule => rule.id);
Object.keys(config).forEach(setting => {
if (!linterRules.includes(setting)) {
invalid.push(setting);
}
});
return invalid;
}
function stringifyConfig(config) {
return JSON.stringify(config, null, 2)
.replace(/,\n\s+\{\n\s+("severity":\s"\w+")\n\s+\}/g, ', {$1}');
}
function setupLinterSettingsEvents(popup) {
$('.save', popup).addEventListener('click', event => {
event.preventDefault();
const linter = checkLinter(event.target.dataset.linter);
const json = tryJSONparse(popup.codebox.getValue());
if (json) {
const invalid = checkConfigRules(linter, json);
if (invalid.length) {
return showLinterErrorMessage(
linter,
t('linterInvalidConfigError') + `<ul><li>${invalid.join('</li><li>')}</li></ul>`
);
}
if (linter === 'stylelint') {
setStylelintConfig(json);
} else {
setCSSLintConfig(json);
}
updateLinter(linter);
showSavedMessage();
} else {
showLinterErrorMessage(linter, t('linterJSONError'));
}
popup.codebox.focus();
});
$('.reset', popup).addEventListener('click', event => {
event.preventDefault();
const linter = checkLinter(event.target.dataset.linter);
let config;
if (linter === 'stylelint') {
setStylelintConfig();
config = stylelintDefaultConfig.rules;
} else {
setCSSLintConfig();
config = csslintDefaultConfig;
}
popup.codebox.setValue(stringifyConfig(config));
popup.codebox.focus();
});
$('.cancel', popup).addEventListener('click', event => {
event.preventDefault();
$('.dismiss').dispatchEvent(new Event('click'));
});
}
function openStylelintSettings() {
const linter = prefs.get('editor.linter');
BG.chromeSync.getValue(
linter === 'stylelint'
? 'editorStylelintConfig'
: 'editorCSSLintConfig'
).then(config => {
if (!config || config.length === 0) {
config = linter === 'stylelint'
? setStylelintConfig(config)
: setCSSLintConfig(config);
}
const configString = stringifyConfig(config);
setupLinterPopup(configString);
});
}
function setupLinterPopup(config) {
const linter = prefs.get('editor.linter');
const linterTitle = linter === 'stylelint' ? 'Stylelint' : 'CSSLint';
function makeButton(className, text, options = {}) {
return $element(Object.assign(options, {
tag: 'button',
className,
type: 'button',
textContent: t(text),
dataset: {linter}
}));
}
function makeLink(url, textContent) {
return $element({tag: 'a', target: '_blank', href: url, textContent});
}
function setJSONMode(cm) {
cm.setOption('mode', 'application/json');
cm.setOption('lint', 'json');
}
const popup = showCodeMirrorPopup(t('linterConfigPopupTitle', linterTitle), $element({
appendChild: [
$element({
tag: 'p',
appendChild: [
t('linterRulesLink') + ' ',
makeLink(
linter === 'stylelint'
? 'https://stylelint.io/user-guide/rules/'
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
linterTitle
),
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : ''
]
}),
makeButton('save', 'styleSaveLabel'),
makeButton('cancel', 'confirmCancel'),
makeButton('reset', 'genericResetLabel', {title: t('linterResetMessage')}),
$element({
tag: 'span',
className: 'saved-message',
textContent: t('genericSavedMessage')
})
]
}));
const contents = $('.contents', popup);
const loadJSON = window.jsonlint ? [] : [
'vendor/codemirror/mode/javascript/javascript.js',
'vendor/codemirror/addon/lint/json-lint.js',
'vendor/jsonlint/jsonlint.js'
];
contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild);
popup.codebox.focus();
popup.codebox.setValue(config);
popup.codebox.clearHistory();
onDOMscripted(loadJSON).then(() => setJSONMode(popup.codebox));
setupLinterSettingsEvents(popup);
}
function loadSelectedLinter(name) {
const scripts = [];
if (name !== 'null' && !$('script[src*="css-lint.js"]')) {
// inject css
injectCSS('vendor/codemirror/addon/lint/lint.css');
injectCSS('msgbox/msgbox.css');
// load CodeMirror lint code
scripts.push(
'vendor/codemirror/addon/lint/lint.js',
'vendor-overwrites/codemirror/addon/lint/css-lint.js',
'msgbox/msgbox.js'
);
}
if (name === 'csslint' && !window.CSSLint) {
scripts.push(
'edit/csslint-config.js',
'vendor-overwrites/csslint/csslint-worker.js'
);
} else if (name === 'stylelint' && !window.stylelint) {
scripts.push(
'vendor-overwrites/stylelint/stylelint-bundle.min.js',
'edit/stylelint-config.js'
);
}
return onDOMscripted(scripts);
}

170
edit/stylelint-config.js Normal file
View File

@ -0,0 +1,170 @@
'use strict';
window.stylelintDefaultConfig = (defaultSeverity => ({
// 'sugarss' is a indent-based syntax like Sass or Stylus
// ref: https://github.com/postcss/postcss#syntaxes
syntax: 'sugarss',
// ** recommended rules **
// ref: https://github.com/stylelint/stylelint-config-recommended/blob/master/index.js
rules: {
'at-rule-no-unknown': [true, defaultSeverity],
'block-no-empty': [true, defaultSeverity],
'color-no-invalid-hex': [true, defaultSeverity],
'declaration-block-no-duplicate-properties': [true, {
'ignore': ['consecutive-duplicates-with-different-values'],
'severity': 'warning'
}],
'declaration-block-no-shorthand-property-overrides': [true, defaultSeverity],
'font-family-no-duplicate-names': [true, defaultSeverity],
'function-calc-no-unspaced-operator': [true, defaultSeverity],
'function-linear-gradient-no-nonstandard-direction': [true, defaultSeverity],
'keyframe-declaration-no-important': [true, defaultSeverity],
'media-feature-name-no-unknown': [true, defaultSeverity],
/* recommended true */
'no-empty-source': false,
'no-extra-semicolons': [true, defaultSeverity],
'no-invalid-double-slash-comments': [true, defaultSeverity],
'property-no-unknown': [true, defaultSeverity],
'selector-pseudo-class-no-unknown': [true, defaultSeverity],
'selector-pseudo-element-no-unknown': [true, defaultSeverity],
'selector-type-no-unknown': [true, defaultSeverity],
'string-no-newline': [true, defaultSeverity],
'unit-no-unknown': [true, defaultSeverity],
// ** non-essential rules
'comment-no-empty': false,
'declaration-block-no-redundant-longhand-properties': false,
'shorthand-property-no-redundant-values': false,
// ** stylistic rules **
/*
'at-rule-empty-line-before': [
'always',
{
'except': [
'blockless-after-same-name-blockless',
'first-nested'
],
'ignore': [
'after-comment'
]
}
],
'at-rule-name-case': 'lower',
'at-rule-name-space-after': 'always-single-line',
'at-rule-semicolon-newline-after': 'always',
'block-closing-brace-empty-line-before': 'never',
'block-closing-brace-newline-after': 'always',
'block-closing-brace-newline-before': 'always-multi-line',
'block-closing-brace-space-before': 'always-single-line',
'block-opening-brace-newline-after': 'always-multi-line',
'block-opening-brace-space-after': 'always-single-line',
'block-opening-brace-space-before': 'always',
'color-hex-case': 'lower',
'color-hex-length': 'short',
'comment-empty-line-before': [
'always',
{
'except': [
'first-nested'
],
'ignore': [
'stylelint-commands'
]
}
],
'comment-whitespace-inside': 'always',
'custom-property-empty-line-before': [
'always',
{
'except': [
'after-custom-property',
'first-nested'
],
'ignore': [
'after-comment',
'inside-single-line-block'
]
}
],
'declaration-bang-space-after': 'never',
'declaration-bang-space-before': 'always',
'declaration-block-semicolon-newline-after': 'always-multi-line',
'declaration-block-semicolon-space-after': 'always-single-line',
'declaration-block-semicolon-space-before': 'never',
'declaration-block-single-line-max-declarations': 1,
'declaration-block-trailing-semicolon': 'always',
'declaration-colon-newline-after': 'always-multi-line',
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'declaration-empty-line-before': [
'always',
{
'except': [
'after-declaration',
'first-nested'
],
'ignore': [
'after-comment',
'inside-single-line-block'
]
}
],
'function-comma-newline-after': 'always-multi-line',
'function-comma-space-after': 'always-single-line',
'function-comma-space-before': 'never',
'function-max-empty-lines': 0,
'function-name-case': 'lower',
'function-parentheses-newline-inside': 'always-multi-line',
'function-parentheses-space-inside': 'never-single-line',
'function-whitespace-after': 'always',
'indentation': 2,
'length-zero-no-unit': true,
'max-empty-lines': 1,
'media-feature-colon-space-after': 'always',
'media-feature-colon-space-before': 'never',
'media-feature-name-case': 'lower',
'media-feature-parentheses-space-inside': 'never',
'media-feature-range-operator-space-after': 'always',
'media-feature-range-operator-space-before': 'always',
'media-query-list-comma-newline-after': 'always-multi-line',
'media-query-list-comma-space-after': 'always-single-line',
'media-query-list-comma-space-before': 'never',
'no-eol-whitespace': true,
'no-missing-end-of-source-newline': true,
'number-leading-zero': 'always',
'number-no-trailing-zeros': true,
'property-case': 'lower',
'rule-empty-line-before': [
'always-multi-line',
{
'except': [
'first-nested'
],
'ignore': [
'after-comment'
]
}
],
'selector-attribute-brackets-space-inside': 'never',
'selector-attribute-operator-space-after': 'never',
'selector-attribute-operator-space-before': 'never',
'selector-combinator-space-after': 'always',
'selector-combinator-space-before': 'always',
'selector-descendant-combinator-no-non-space': true,
'selector-list-comma-newline-after': 'always',
'selector-list-comma-space-before': 'never',
'selector-max-empty-lines': 0,
'selector-pseudo-class-case': 'lower',
'selector-pseudo-class-parentheses-space-inside': 'never',
'selector-pseudo-element-case': 'lower',
'selector-pseudo-element-colon-notation': 'double',
'selector-type-case': 'lower',
'unit-case': 'lower',
'value-list-comma-newline-after': 'always-multi-line',
'value-list-comma-space-after': 'always-single-line',
'value-list-comma-space-before': 'never',
'value-list-max-empty-lines': 0
*/
}
}))({severity: 'warning'});

View File

@ -67,6 +67,86 @@ function onDOMready() {
}
function onDOMscripted(scripts) {
if (scripts) {
return new Promise(resolve => {
addResolver(resolve);
onDOMscripted.scriptQueue = scripts;
loadNextScript();
});
}
if (onDOMscripted.scriptQueue) {
return new Promise(resolve => addResolver(resolve));
}
if (document.readyState !== 'loading') {
if (onDOMscripted.resolveOnReady) {
onDOMscripted.resolveOnReady.forEach(r => r());
onDOMscripted.resolveOnReady = null;
}
return Promise.resolve();
}
return onDOMready().then(onDOMscripted);
function loadNextScript() {
const next = onDOMscripted.scriptQueue.shift();
if (!next) {
onDOMscripted.scriptQueue = null;
onDOMscripted();
} else if (typeof next === 'function') {
Promise.resolve(next())
.then(loadNextScript);
} else {
Promise.all(
(next instanceof Array ? next : [next]).map(next =>
typeof next === 'function'
? next()
: injectScript({src: next, async: true})
)
).then(loadNextScript);
}
}
function addResolver(r) {
if (!onDOMscripted.resolveOnReady) {
onDOMscripted.resolveOnReady = [];
}
onDOMscripted.resolveOnReady.push(r);
}
}
function injectScript(properties) {
if (typeof properties === 'string') {
properties = {src: properties};
}
if (!properties || !properties.src) {
return;
}
const script = document.head.appendChild(document.createElement('script'));
Object.assign(script, properties);
if (!properties.onload) {
return new Promise(resolve => {
script.onload = () => {
script.onload = null;
resolve();
};
});
}
}
function injectCSS(url) {
if (!url) {
return;
}
document.head.appendChild($element({
tag: 'link',
rel: 'stylesheet',
href: url
}));
}
function scrollElementIntoView(element) {
// align to the top/bottom of the visible area if wasn't visible
const bounds = element.getBoundingClientRect();

View File

@ -42,6 +42,7 @@ var prefs = new function Prefs() {
indent_conditional: true,
},
'editor.lintDelay': 500, // lint gutter marker update delay, ms
'editor.linter': 'csslint', // Choose csslint or stylelint
'editor.lintReportDelay': 4500, // lint report update delay, ms
'editor.matchHighlight': 'token', // token = token/word under cursor even if nothing is selected
// selection = only when something is selected
@ -321,11 +322,11 @@ var prefs = new function Prefs() {
// and establishes a two-way connection between the document elements and the actual prefs
function setupLivePrefs(
IDs = Object.getOwnPropertyNames(prefs.readOnlyValues)
.filter(id => document.getElementById(id))
.filter(id => $('#' + id))
) {
const checkedProps = {};
for (const id of IDs) {
const element = document.getElementById(id);
const element = $('#' + id);
checkedProps[id] = element.type === 'checkbox' ? 'checked' : 'value';
updateElement({id, element, force: true});
element.addEventListener('change', onChange);
@ -341,7 +342,7 @@ function setupLivePrefs(
function updateElement({
id,
value = prefs.get(id),
element = document.getElementById(id),
element = $('#' + id),
force,
}) {
const prop = checkedProps[id];

View File

@ -21,6 +21,7 @@
"background": {
"scripts": [
"js/messaging.js",
"vendor-overwrites/lz-string/LZString-2xspeedup.js",
"background/storage.js",
"js/prefs.js",
"background/background.js",

View File

@ -143,7 +143,8 @@ function initPopup(url) {
title: `url-prefix("${url}")`,
textContent: prefs.get('popup.breadcrumbs.usePath')
? new URL(url).pathname.slice(1)
: t('writeStyleForURL').replace(/ /g, '\u00a0'), // this&nbsp;URL
// this&nbsp;URL
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
onclick: handleEvent.openLink,
});
if (prefs.get('popup.breadcrumbs')) {

View File

@ -3,62 +3,112 @@
// Depends on csslint.js from https://github.com/stubbornella/csslint
// declare global: CSSLint
/* global CodeMirror require define */
/* global CSSLint stylelint stylelintDefaultConfig csslintDefaultConfig */
'use strict';
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
(mod => {
if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS
mod(require('../../lib/codemirror'));
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['../../lib/codemirror'], mod);
} else {
// Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "css", function(text) {
var found = [];
if (!window.CSSLint) return found;
/* STYLISH: hack start (part 1) */
var rules = CSSLint.getRules();
var allowedRules = ["display-property-grouping", "duplicate-properties", "empty-rules", "errors", "known-properties"];
CSSLint.clearRules();
rules.forEach(function(rule) {
if (allowedRules.indexOf(rule.id) >= 0) {
CSSLint.addRule(rule);
}
});
/* STYLISH: hack end */
var results = CSSLint.verify(text), messages = results.messages, message = null;
for ( var i = 0; i < messages.length; i++) {
message = messages[i];
/* STYLISH: hack start (part 2) */
if (message.type === 'warning') {
// @font-face {font-family: 'Ampersand'; unicode-range: U+26;}
if (message.message.indexOf('unicode-range') !== -1) {
continue;
}
else if ( // color: hsl(210, 100%, 2.2%); or color: hsla(210, 100%, 2.2%, 0.3);
message.message.startsWith('Expected (<color>) but found \'hsl') &&
/hsla?\(\s*(-?\d+)%?\s*,\s*(-?\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%(\s*,\s*(-?\d+|-?\d*.\d+))?\s*\)/.test(message.message)
) {
continue;
}
//
}
/* STYLISH: hack end */
var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message,
severity : message.type
});
}
return found;
});
})(CodeMirror => {
CodeMirror.registerHelper('lint', 'csslint', text => {
const found = [];
if (!window.CSSLint) {
return found;
}
/* STYLUS: hack start (part 1) */
return BG.chromeSync.getValue('editorCSSLintConfig').then(config => {
// csslintDefaultConfig stored in csslint-config.js & loaded by edit/lint.js
if (Object.keys(config || []).length === 0) {
config = Object.assign({}, csslintDefaultConfig);
}
const results = CSSLint.verify(text, config);
const messages = results.messages;
const hslRegex = /hsla?\(\s*(-?\d+)%?\s*,\s*(-?\d+)%\s*,\s*(-?\d+|-?\d*.\d+)%(\s*,\s*(-?\d+|-?\d*.\d+))?\s*\)/;
let message = null;
/* STYLUS: hack end */
for (let i = 0; i < messages.length; i++) {
message = messages[i];
/* STYLUS: hack start (part 2) */
if (message.type === 'warning') {
// @font-face {font-family: 'Ampersand'; unicode-range: U+26;}
if (message.message.indexOf('unicode-range') !== -1) {
continue;
} else if (
// color: hsl(210, 100%, 2.2%); or color: hsla(210, 100%, 2.2%, 0.3);
message.message.startsWith('Expected (<color>) but found \'hsl') &&
hslRegex.test(message.message)
) {
continue;
}
}
const startLine = message.line - 1;
const endLine = message.line - 1;
const startCol = message.col - 1;
const endCol = message.col;
/* STYLUS: hack end */
found.push({
from: CodeMirror.Pos(startLine, startCol),
to: CodeMirror.Pos(endLine, endCol),
message: message.message + ` (${message.rule.id})`,
severity : message.type
});
}
return found;
});
});
CodeMirror.registerHelper('lint', 'stylelint', text => {
const found = [];
window.stylelint = require('stylelint');
if (window.stylelint) {
return BG.chromeSync.getValue('editorStylelintConfig').then(rules => {
// stylelintDefaultConfig stored in stylelint-config.js & loaded by edit/lint.js
if (Object.keys(rules || []).length === 0) {
rules = stylelintDefaultConfig.rules;
}
return stylelint.lint({
code: text,
config: {
syntax: stylelintDefaultConfig.syntax,
rules: rules
}
}).then(output => {
const warnings = output.results.length ? output.results[0].warnings : [];
const len = warnings.length;
let warning;
let message;
if (len) {
for (let i = 0; i < len; i++) {
warning = warnings[i];
message = warning.text
.replace('Unexpected ', '')
.replace(/^./, function (firstLetter) {
return firstLetter.toUpperCase();
});
found.push({
from: CodeMirror.Pos(warning.line - 1, warning.column - 1),
to: CodeMirror.Pos(warning.line - 1, warning.column),
message,
severity : warning.severity
});
}
}
return found;
});
});
}
return found;
});
});

View File

@ -9,6 +9,9 @@
2. Apply our hacks unless supported natively
(use git history for the file as this warning may be obsolete):
* 449a27cc Add CSSLint position sticky rule
* d49e44dd CSS variables
* 2e86c958 fire startdocument on {
* bc63ecca support "i" in attribute selector
* 2468784e fix crashing on unclosed calc() at eof
* 3287b79f Support :any(), :-webkit-any(), :-moz-any()

View File

@ -3714,7 +3714,7 @@ var Properties = module.exports = {
"pitch-range" : 1,
"play-during" : 1,
"pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
"position" : "static | relative | absolute | fixed",
"position" : "static | relative | absolute | fixed | sticky",
"presentation-level" : 1,
"punctuation-trim" : 1,

View File

@ -0,0 +1,512 @@
// ==UserScript==
// @name LZString-2xspeedup
// @description 2x speedup via ES6 Map and Set
// @version 1.4.4
// ==/UserScript==
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.4
var LZString = (function() {
// private property
var f = String.fromCharCode;
var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$";
var baseReverseDic = {};
function getBaseValue(alphabet, character) {
if (!baseReverseDic[alphabet]) {
baseReverseDic[alphabet] = {};
for (var i=0 ; i<alphabet.length ; i++) {
baseReverseDic[alphabet][alphabet.charAt(i)] = i;
}
}
return baseReverseDic[alphabet][character];
}
var LZString = {
compressToBase64 : function (input) {
if (input == null) return "";
var res = LZString._compress(input, 6, function(a){return keyStrBase64.charAt(a);});
switch (res.length % 4) { // To produce valid Base64
default: // When could this happen ?
case 0 : return res;
case 1 : return res+"===";
case 2 : return res+"==";
case 3 : return res+"=";
}
},
decompressFromBase64 : function (input) {
if (input == null) return "";
if (input == "") return null;
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrBase64, input.charAt(index)); });
},
compressToUTF16 : function (input) {
if (input == null) return "";
return LZString._compress(input, 15, function(a){return f(a+32);}) + " ";
},
decompressFromUTF16: function (compressed) {
if (compressed == null) return "";
if (compressed == "") return null;
return LZString._decompress(compressed.length, 16384, function(index) { return compressed.charCodeAt(index) - 32; });
},
//compress into uint8array (UCS-2 big endian format)
compressToUint8Array: function (uncompressed) {
var compressed = LZString.compress(uncompressed);
var buf=new Uint8Array(compressed.length*2); // 2 bytes per character
for (var i=0, TotalLen=compressed.length; i<TotalLen; i++) {
var current_value = compressed.charCodeAt(i);
buf[i*2] = current_value >>> 8;
buf[i*2+1] = current_value % 256;
}
return buf;
},
//decompress from uint8array (UCS-2 big endian format)
decompressFromUint8Array:function (compressed) {
if (compressed===null || compressed===undefined){
return LZString.decompress(compressed);
} else {
var buf=new Array(compressed.length/2); // 2 bytes per character
for (var i=0, TotalLen=buf.length; i<TotalLen; i++) {
buf[i]=compressed[i*2]*256+compressed[i*2+1];
}
var result = [];
buf.forEach(function (c) {
result.push(f(c));
});
return LZString.decompress(result.join(''));
}
},
//compress into a string that is already URI encoded
compressToEncodedURIComponent: function (input) {
if (input == null) return "";
return LZString._compress(input, 6, function(a){return keyStrUriSafe.charAt(a);});
},
//decompress from an output of compressToEncodedURIComponent
decompressFromEncodedURIComponent:function (input) {
if (input == null) return "";
if (input == "") return null;
input = input.replace(/ /g, "+");
return LZString._decompress(input.length, 32, function(index) { return getBaseValue(keyStrUriSafe, input.charAt(index)); });
},
compress: function (uncompressed) {
return LZString._compress(uncompressed, 16, function(a){return f(a);});
},
_compress: function (uncompressed, bitsPerChar, getCharFromInt) {
if (uncompressed == null) return "";
var i, value,
context_dictionary= new Map(),
context_dictionaryToCreate= new Set(),
context_c="",
context_wc="",
context_w="",
context_enlargeIn= 2, // Compensate for the first entry which should not count
context_dictSize= 3,
context_numBits= 2,
context_data=[],
context_data_val=0,
context_data_position=0,
ii;
for (ii = 0; ii < uncompressed.length; ii += 1) {
context_c = uncompressed.charAt(ii);
if (!context_dictionary.has(context_c)) {
context_dictionary.set(context_c, context_dictSize++);
context_dictionaryToCreate.add(context_c);
}
context_wc = context_w + context_c;
if (context_dictionary.has(context_wc)) {
context_w = context_wc;
} else {
if (context_dictionaryToCreate.has(context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position ==bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
context_dictionaryToCreate.delete(context_w);
} else {
value = context_dictionary.get(context_w);
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
// Add wc to the dictionary.
context_dictionary.set(context_wc, context_dictSize++);
context_w = String(context_c);
}
}
// Output the code for w.
if (context_w !== "") {
if (context_dictionaryToCreate.has(context_w)) {
if (context_w.charCodeAt(0)<256) {
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
}
value = context_w.charCodeAt(0);
for (i=0 ; i<8 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
} else {
value = 1;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | value;
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = 0;
}
value = context_w.charCodeAt(0);
for (i=0 ; i<16 ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
context_dictionaryToCreate.delete(context_w);
} else {
value = context_dictionary.get(context_w);
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
}
context_enlargeIn--;
if (context_enlargeIn == 0) {
context_enlargeIn = Math.pow(2, context_numBits);
context_numBits++;
}
}
// Mark the end of the stream
value = 2;
for (i=0 ; i<context_numBits ; i++) {
context_data_val = (context_data_val << 1) | (value&1);
if (context_data_position == bitsPerChar-1) {
context_data_position = 0;
context_data.push(getCharFromInt(context_data_val));
context_data_val = 0;
} else {
context_data_position++;
}
value = value >> 1;
}
// Flush the last char
while (true) {
context_data_val = (context_data_val << 1);
if (context_data_position == bitsPerChar-1) {
context_data.push(getCharFromInt(context_data_val));
break;
}
else context_data_position++;
}
return context_data.join('');
},
decompress: function (compressed) {
if (compressed == null) return "";
if (compressed == "") return null;
return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); });
},
_decompress: function (length, resetValue, getNextValue) {
var dictionary = [],
next,
enlargeIn = 4,
dictSize = 4,
numBits = 3,
entry = "",
result = [],
i,
w,
bits, resb, maxpower, power,
c,
data = {val:getNextValue(0), position:resetValue, index:1};
for (i = 0; i < 3; i += 1) {
dictionary[i] = i;
}
bits = 0;
maxpower = Math.pow(2,2);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
switch (next = bits) {
case 0:
bits = 0;
maxpower = Math.pow(2,8);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 1:
bits = 0;
maxpower = Math.pow(2,16);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
c = f(bits);
break;
case 2:
return "";
}
dictionary[3] = c;
w = c;
result.push(c);
while (true) {
if (data.index > length) {
return "";
}
bits = 0;
maxpower = Math.pow(2,numBits);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
switch (c = bits) {
case 0:
bits = 0;
maxpower = Math.pow(2,8);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize-1;
enlargeIn--;
break;
case 1:
bits = 0;
maxpower = Math.pow(2,16);
power=1;
while (power!=maxpower) {
resb = data.val & data.position;
data.position >>= 1;
if (data.position == 0) {
data.position = resetValue;
data.val = getNextValue(data.index++);
}
bits |= (resb>0 ? 1 : 0) * power;
power <<= 1;
}
dictionary[dictSize++] = f(bits);
c = dictSize-1;
enlargeIn--;
break;
case 2:
return result.join('');
}
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
if (dictionary[c]) {
entry = dictionary[c];
} else {
if (c === dictSize) {
entry = w + w.charAt(0);
} else {
return null;
}
}
result.push(entry);
// Add w+entry[0] to the dictionary.
dictionary[dictSize++] = w + entry.charAt(0);
enlargeIn--;
w = entry;
if (enlargeIn == 0) {
enlargeIn = Math.pow(2, numBits);
numBits++;
}
}
}
};
return LZString;
})();
if (typeof define === 'function' && define.amd) {
define(function () { return LZString; });
} else if( typeof module !== 'undefined' && module != null ) {
module.exports = LZString
} else if( typeof angular !== 'undefined' && angular != null ) {
angular.module('LZString', [])
.factory('LZString', function () {
return LZString;
});
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
Stylelint bundle file from https://github.com/Mottie/stylelint/tree/mod - see the readme for details.

View File

@ -0,0 +1,37 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
// Depends on jsonlint.js from https://github.com/zaach/jsonlint
// declare global: jsonlint
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.registerHelper("lint", "json", function(text) {
var found = [];
if (!window.jsonlint) {
if (window.console) {
window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
}
return found;
}
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
});
});

View File

@ -0,0 +1,818 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
function expressionAllowed(stream, state, backUp) {
return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}
CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
var jsonldMode = parserConfig.jsonld;
var jsonMode = parserConfig.json || jsonldMode;
var isTS = parserConfig.typescript;
var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
// Tokenizer
var keywords = function(){
function kw(type) {return {type: type, style: "keyword"};}
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
var jsKeywords = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
"in": operator, "typeof": operator, "instanceof": operator,
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
"this": kw("this"), "class": kw("class"), "super": kw("atom"),
"yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
"await": C
};
// Extend the 'normal' keywords with the TypeScript language extensions
if (isTS) {
var type = {type: "variable", style: "type"};
var tsKeywords = {
// object-like things
"interface": kw("class"),
"implements": C,
"namespace": C,
"module": kw("module"),
"enum": kw("module"),
// scope modifiers
"public": kw("modifier"),
"private": kw("modifier"),
"protected": kw("modifier"),
"abstract": kw("modifier"),
// types
"string": type, "number": type, "boolean": type, "any": type
};
for (var attr in tsKeywords) {
jsKeywords[attr] = tsKeywords[attr];
}
}
return jsKeywords;
}();
var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
function readRegexp(stream) {
var escaped = false, next, inSet = false;
while ((next = stream.next()) != null) {
if (!escaped) {
if (next == "/" && !inSet) return;
if (next == "[") inSet = true;
else if (inSet && next == "]") inSet = false;
}
escaped = !escaped && next == "\\";
}
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type, content;
function ret(tp, style, cont) {
type = tp; content = cont;
return style;
}
function tokenBase(stream, state) {
var ch = stream.next();
if (ch == '"' || ch == "'") {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
} else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
return ret("number", "number");
} else if (ch == "." && stream.match("..")) {
return ret("spread", "meta");
} else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
return ret(ch);
} else if (ch == "=" && stream.eat(">")) {
return ret("=>", "operator");
} else if (ch == "0" && stream.eat(/x/i)) {
stream.eatWhile(/[\da-f]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/o/i)) {
stream.eatWhile(/[0-7]/i);
return ret("number", "number");
} else if (ch == "0" && stream.eat(/b/i)) {
stream.eatWhile(/[01]/i);
return ret("number", "number");
} else if (/\d/.test(ch)) {
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
return ret("number", "number");
} else if (ch == "/") {
if (stream.eat("*")) {
state.tokenize = tokenComment;
return tokenComment(stream, state);
} else if (stream.eat("/")) {
stream.skipToEnd();
return ret("comment", "comment");
} else if (expressionAllowed(stream, state, 1)) {
readRegexp(stream);
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
return ret("regexp", "string-2");
} else {
stream.eatWhile(isOperatorChar);
return ret("operator", "operator", stream.current());
}
} else if (ch == "`") {
state.tokenize = tokenQuasi;
return tokenQuasi(stream, state);
} else if (ch == "#") {
stream.skipToEnd();
return ret("error", "error");
} else if (isOperatorChar.test(ch)) {
if (ch != ">" || !state.lexical || state.lexical.type != ">")
stream.eatWhile(isOperatorChar);
return ret("operator", "operator", stream.current());
} else if (wordRE.test(ch)) {
stream.eatWhile(wordRE);
var word = stream.current()
if (state.lastType != ".") {
if (keywords.propertyIsEnumerable(word)) {
var kw = keywords[word]
return ret(kw.type, kw.style, word)
}
if (word == "async" && stream.match(/^\s*[\(\w]/, false))
return ret("async", "keyword", word)
}
return ret("variable", "variable", word)
}
}
function tokenString(quote) {
return function(stream, state) {
var escaped = false, next;
if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
state.tokenize = tokenBase;
return ret("jsonld-keyword", "meta");
}
while ((next = stream.next()) != null) {
if (next == quote && !escaped) break;
escaped = !escaped && next == "\\";
}
if (!escaped) state.tokenize = tokenBase;
return ret("string", "string");
};
}
function tokenComment(stream, state) {
var maybeEnd = false, ch;
while (ch = stream.next()) {
if (ch == "/" && maybeEnd) {
state.tokenize = tokenBase;
break;
}
maybeEnd = (ch == "*");
}
return ret("comment", "comment");
}
function tokenQuasi(stream, state) {
var escaped = false, next;
while ((next = stream.next()) != null) {
if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
state.tokenize = tokenBase;
break;
}
escaped = !escaped && next == "\\";
}
return ret("quasi", "string-2", stream.current());
}
var brackets = "([{}])";
// This is a crude lookahead trick to try and notice that we're
// parsing the argument patterns for a fat-arrow function before we
// actually hit the arrow token. It only works if the arrow is on
// the same line as the arguments and there's no strange noise
// (comments) in between. Fallback is to only notice when we hit the
// arrow, and not declare the arguments as locals for the arrow
// body.
function findFatArrow(stream, state) {
if (state.fatArrowAt) state.fatArrowAt = null;
var arrow = stream.string.indexOf("=>", stream.start);
if (arrow < 0) return;
if (isTS) { // Try to skip TypeScript return type declarations after the arguments
var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
if (m) arrow = m.index
}
var depth = 0, sawSomething = false;
for (var pos = arrow - 1; pos >= 0; --pos) {
var ch = stream.string.charAt(pos);
var bracket = brackets.indexOf(ch);
if (bracket >= 0 && bracket < 3) {
if (!depth) { ++pos; break; }
if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
} else if (bracket >= 3 && bracket < 6) {
++depth;
} else if (wordRE.test(ch)) {
sawSomething = true;
} else if (/["'\/]/.test(ch)) {
return;
} else if (sawSomething && !depth) {
++pos;
break;
}
}
if (sawSomething && !depth) state.fatArrowAt = pos;
}
// Parser
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
function JSLexical(indented, column, type, align, prev, info) {
this.indented = indented;
this.column = column;
this.type = type;
this.prev = prev;
this.info = info;
if (align != null) this.align = align;
}
function inScope(state, varname) {
for (var v = state.localVars; v; v = v.next)
if (v.name == varname) return true;
for (var cx = state.context; cx; cx = cx.prev) {
for (var v = cx.vars; v; v = v.next)
if (v.name == varname) return true;
}
}
function parseJS(state, style, type, content, stream) {
var cc = state.cc;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = true;
while(true) {
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
if (combinator(type, content)) {
while(cc.length && cc[cc.length - 1].lex)
cc.pop()();
if (cx.marked) return cx.marked;
if (type == "variable" && inScope(state, content)) return "variable-2";
return style;
}
}
}
// Combinator utils
var cx = {state: null, column: null, marked: null, cc: null};
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function register(varname) {
function inList(list) {
for (var v = list; v; v = v.next)
if (v.name == varname) return true;
return false;
}
var state = cx.state;
cx.marked = "def";
if (state.context) {
if (inList(state.localVars)) return;
state.localVars = {name: varname, next: state.localVars};
} else {
if (inList(state.globalVars)) return;
if (parserConfig.globalVars)
state.globalVars = {name: varname, next: state.globalVars};
}
}
// Combinators
var defaultVars = {name: "this", next: {name: "arguments"}};
function pushcontext() {
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
cx.state.localVars = defaultVars;
}
function popcontext() {
cx.state.localVars = cx.state.context.vars;
cx.state.context = cx.state.context.prev;
}
function pushlex(type, info) {
var result = function() {
var state = cx.state, indent = state.indented;
if (state.lexical.type == "stat") indent = state.lexical.indented;
else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
indent = outer.indented;
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
return result;
}
function poplex() {
var state = cx.state;
if (state.lexical.prev) {
if (state.lexical.type == ")")
state.indented = state.lexical.indented;
state.lexical = state.lexical.prev;
}
}
poplex.lex = true;
function expect(wanted) {
function exp(type) {
if (type == wanted) return cont();
else if (wanted == ";") return pass();
else return cont(exp);
};
return exp;
}
function statement(type, value) {
if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
if (type == "{") return cont(pushlex("}"), block, poplex);
if (type == ";") return cont();
if (type == "if") {
if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
cx.state.cc.pop()();
return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
}
if (type == "function") return cont(functiondef);
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
if (type == "variable") {
if (isTS && value == "type") {
cx.marked = "keyword"
return cont(typeexpr, expect("operator"), typeexpr, expect(";"));
} else {
return cont(pushlex("stat"), maybelabel);
}
}
if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"),
block, poplex, poplex);
if (type == "case") return cont(expression, expect(":"));
if (type == "default") return cont(expect(":"));
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
statement, poplex, popcontext);
if (type == "class") return cont(pushlex("form"), className, poplex);
if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
if (type == "async") return cont(statement)
if (value == "@") return cont(expression, statement)
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
return expressionInner(type, false);
}
function expressionNoComma(type) {
return expressionInner(type, true);
}
function parenExpr(type) {
if (type != "(") return pass()
return cont(pushlex(")"), expression, expect(")"), poplex)
}
function expressionInner(type, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody;
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
}
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef, maybeop);
if (type == "class") return cont(pushlex("form"), classExpression, poplex);
if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
if (type == "{") return contCommasep(objprop, "}", null, maybeop);
if (type == "quasi") return pass(quasi, maybeop);
if (type == "new") return cont(maybeTarget(noComma));
return cont();
}
function maybeexpression(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
function maybeexpressionNoComma(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expressionNoComma);
}
function maybeoperatorComma(type, value) {
if (type == ",") return cont(expression);
return maybeoperatorNoComma(type, value, false);
}
function maybeoperatorNoComma(type, value, noComma) {
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
var expr = noComma == false ? expression : expressionNoComma;
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") {
if (/\+\+|--/.test(value)) return cont(me);
if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr);
}
if (type == "quasi") { return pass(quasi, me); }
if (type == ";") return;
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
}
function quasi(type, value) {
if (type != "quasi") return pass();
if (value.slice(value.length - 2) != "${") return cont(quasi);
return cont(expression, continueQuasi);
}
function continueQuasi(type) {
if (type == "}") {
cx.marked = "string-2";
cx.state.tokenize = tokenQuasi;
return cont(quasi);
}
}
function arrowBody(type) {
findFatArrow(cx.stream, cx.state);
return pass(type == "{" ? statement : expression);
}
function arrowBodyNoComma(type) {
findFatArrow(cx.stream, cx.state);
return pass(type == "{" ? statement : expressionNoComma);
}
function maybeTarget(noComma) {
return function(type) {
if (type == ".") return cont(noComma ? targetNoComma : target);
else return pass(noComma ? expressionNoComma : expression);
};
}
function target(_, value) {
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
}
function targetNoComma(_, value) {
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
}
function maybelabel(type) {
if (type == ":") return cont(poplex, statement);
return pass(maybeoperatorComma, expect(";"), poplex);
}
function property(type) {
if (type == "variable") {cx.marked = "property"; return cont();}
}
function objprop(type, value) {
if (type == "async") {
cx.marked = "property";
return cont(objprop);
} else if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
return cont(afterprop);
} else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property");
return cont(afterprop);
} else if (type == "jsonld-keyword") {
return cont(afterprop);
} else if (type == "modifier") {
return cont(objprop)
} else if (type == "[") {
return cont(expression, expect("]"), afterprop);
} else if (type == "spread") {
return cont(expression, afterprop);
} else if (type == ":") {
return pass(afterprop)
}
}
function getterSetter(type) {
if (type != "variable") return pass(afterprop);
cx.marked = "property";
return cont(functiondef);
}
function afterprop(type) {
if (type == ":") return cont(expressionNoComma);
if (type == "(") return pass(functiondef);
}
function commasep(what, end, sep) {
function proceed(type, value) {
if (sep ? sep.indexOf(type) > -1 : type == ",") {
var lex = cx.state.lexical;
if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
return cont(function(type, value) {
if (type == end || value == end) return pass()
return pass(what)
}, proceed);
}
if (type == end || value == end) return cont();
return cont(expect(end));
}
return function(type, value) {
if (type == end || value == end) return cont();
return pass(what, proceed);
};
}
function contCommasep(what, end, info) {
for (var i = 3; i < arguments.length; i++)
cx.cc.push(arguments[i]);
return cont(pushlex(end, info), commasep(what, end), poplex);
}
function block(type) {
if (type == "}") return cont();
return pass(statement, block);
}
function maybetype(type, value) {
if (isTS) {
if (type == ":") return cont(typeexpr);
if (value == "?") return cont(maybetype);
}
}
function typeexpr(type) {
if (type == "variable") {cx.marked = "type"; return cont(afterType);}
if (type == "string" || type == "number" || type == "atom") return cont(afterType);
if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType)
}
function maybeReturnType(type) {
if (type == "=>") return cont(typeexpr)
}
function typeprop(type, value) {
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property"
return cont(typeprop)
} else if (value == "?") {
return cont(typeprop)
} else if (type == ":") {
return cont(typeexpr)
} else if (type == "[") {
return cont(expression, maybetype, expect("]"), typeprop)
}
}
function typearg(type) {
if (type == "variable") return cont(typearg)
else if (type == ":") return cont(typeexpr)
}
function afterType(type, value) {
if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
if (value == "|" || type == ".") return cont(typeexpr)
if (type == "[") return cont(expect("]"), afterType)
if (value == "extends") return cont(typeexpr)
}
function vardef() {
return pass(pattern, maybetype, maybeAssign, vardefCont);
}
function pattern(type, value) {
if (type == "modifier") return cont(pattern)
if (type == "variable") { register(value); return cont(); }
if (type == "spread") return cont(pattern);
if (type == "[") return contCommasep(pattern, "]");
if (type == "{") return contCommasep(proppattern, "}");
}
function proppattern(type, value) {
if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
register(value);
return cont(maybeAssign);
}
if (type == "variable") cx.marked = "property";
if (type == "spread") return cont(pattern);
if (type == "}") return pass();
return cont(expect(":"), pattern, maybeAssign);
}
function maybeAssign(_type, value) {
if (value == "=") return cont(expressionNoComma);
}
function vardefCont(type) {
if (type == ",") return cont(vardef);
}
function maybeelse(type, value) {
if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
}
function forspec(type) {
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
}
function forspec1(type) {
if (type == "var") return cont(vardef, expect(";"), forspec2);
if (type == ";") return cont(forspec2);
if (type == "variable") return cont(formaybeinof);
return pass(expression, expect(";"), forspec2);
}
function formaybeinof(_type, value) {
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return cont(maybeoperatorComma, forspec2);
}
function forspec2(type, value) {
if (type == ";") return cont(forspec3);
if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
return pass(expression, expect(";"), forspec3);
}
function forspec3(type) {
if (type != ")") cont(expression);
}
function functiondef(type, value) {
if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
if (type == "variable") {register(value); return cont(functiondef);}
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
}
function funarg(type) {
if (type == "spread") return cont(funarg);
return pass(pattern, maybetype, maybeAssign);
}
function classExpression(type, value) {
// Class expressions may have an optional name.
if (type == "variable") return className(type, value);
return classNameAfter(type, value);
}
function className(type, value) {
if (type == "variable") {register(value); return cont(classNameAfter);}
}
function classNameAfter(type, value) {
if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter)
if (value == "extends" || value == "implements" || (isTS && type == ","))
return cont(isTS ? typeexpr : expression, classNameAfter);
if (type == "{") return cont(pushlex("}"), classBody, poplex);
}
function classBody(type, value) {
if (type == "variable" || cx.style == "keyword") {
if ((value == "async" || value == "static" || value == "get" || value == "set" ||
(isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) &&
cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) {
cx.marked = "keyword";
return cont(classBody);
}
cx.marked = "property";
return cont(isTS ? classfield : functiondef, classBody);
}
if (type == "[")
return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody)
if (value == "*") {
cx.marked = "keyword";
return cont(classBody);
}
if (type == ";") return cont(classBody);
if (type == "}") return cont();
if (value == "@") return cont(expression, classBody)
}
function classfield(type, value) {
if (value == "?") return cont(classfield)
if (type == ":") return cont(typeexpr, maybeAssign)
if (value == "=") return cont(expressionNoComma)
return pass(functiondef)
}
function afterExport(type, value) {
if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
return pass(statement);
}
function exportField(type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
if (type == "variable") return pass(expressionNoComma, exportField);
}
function afterImport(type) {
if (type == "string") return cont();
return pass(importSpec, maybeMoreImports, maybeFrom);
}
function importSpec(type, value) {
if (type == "{") return contCommasep(importSpec, "}");
if (type == "variable") register(value);
if (value == "*") cx.marked = "keyword";
return cont(maybeAs);
}
function maybeMoreImports(type) {
if (type == ",") return cont(importSpec, maybeMoreImports)
}
function maybeAs(_type, value) {
if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
}
function maybeFrom(_type, value) {
if (value == "from") { cx.marked = "keyword"; return cont(expression); }
}
function arrayLiteral(type) {
if (type == "]") return cont();
return pass(commasep(expressionNoComma, "]"));
}
function isContinuedStatement(state, textAfter) {
return state.lastType == "operator" || state.lastType == "," ||
isOperatorChar.test(textAfter.charAt(0)) ||
/[,.]/.test(textAfter.charAt(0));
}
// Interface
return {
startState: function(basecolumn) {
var state = {
tokenize: tokenBase,
lastType: "sof",
cc: [],
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
localVars: parserConfig.localVars,
context: parserConfig.localVars && {vars: parserConfig.localVars},
indented: basecolumn || 0
};
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
state.globalVars = parserConfig.globalVars;
return state;
},
token: function(stream, state) {
if (stream.sol()) {
if (!state.lexical.hasOwnProperty("align"))
state.lexical.align = false;
state.indented = stream.indentation();
findFatArrow(stream, state);
}
if (state.tokenize != tokenComment && stream.eatSpace()) return null;
var style = state.tokenize(stream, state);
if (type == "comment") return style;
state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
return parseJS(state, style, type, content, stream);
},
indent: function(state, textAfter) {
if (state.tokenize == tokenComment) return CodeMirror.Pass;
if (state.tokenize != tokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
// Kludge to prevent 'maybelse' from blocking lexical scope pops
if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
var c = state.cc[i];
if (c == poplex) lexical = lexical.prev;
else if (c != maybeelse) break;
}
while ((lexical.type == "stat" || lexical.type == "form") &&
(firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
(top == maybeoperatorComma || top == maybeoperatorNoComma) &&
!/^[,\.=+\-*:?[\(]/.test(textAfter))))
lexical = lexical.prev;
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
},
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
blockCommentStart: jsonMode ? null : "/*",
blockCommentEnd: jsonMode ? null : "*/",
lineComment: jsonMode ? null : "//",
fold: "brace",
closeBrackets: "()[]{}''\"\"``",
helperType: jsonMode ? "json" : "javascript",
jsonldMode: jsonldMode,
jsonMode: jsonMode,
expressionAllowed: expressionAllowed,
skipExpression: function(state) {
var top = state.cc[state.cc.length - 1]
if (top == expression || top == expressionNoComma) state.cc.pop()
}
};
});
CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
CodeMirror.defineMIME("text/javascript", "javascript");
CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/x-javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
});

432
vendor/jsonlint/jsonlint.js vendored Normal file
View File

@ -0,0 +1,432 @@
/* Jison generated parser */
var jsonlint = (function(){
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1},
terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"},
productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
var $0 = $$.length - 1;
switch (yystate) {
case 1: // replace escaped characters with actual character
this.$ = yytext.replace(/\\(\\|")/g, "$"+"1")
.replace(/\\n/g,'\n')
.replace(/\\r/g,'\r')
.replace(/\\t/g,'\t')
.replace(/\\v/g,'\v')
.replace(/\\f/g,'\f')
.replace(/\\b/g,'\b');
break;
case 2:this.$ = Number(yytext);
break;
case 3:this.$ = null;
break;
case 4:this.$ = true;
break;
case 5:this.$ = false;
break;
case 6:return this.$ = $$[$0-1];
break;
case 13:this.$ = {};
break;
case 14:this.$ = $$[$0-1];
break;
case 15:this.$ = [$$[$0-2], $$[$0]];
break;
case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
break;
case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1];
break;
case 18:this.$ = [];
break;
case 19:this.$ = $$[$0-1];
break;
case 20:this.$ = [$$[$0]];
break;
case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]);
break;
}
},
table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}],
defaultActions: {16:[2,6]},
parseError: function parseError(str, hash) {
throw new Error(str);
},
parse: function parse(input) {
var self = this,
stack = [0],
vstack = [null], // semantic value stack
lstack = [], // location stack
table = this.table,
yytext = '',
yylineno = 0,
yyleng = 0,
recovering = 0,
TERROR = 2,
EOF = 1;
//this.reductionCount = this.shiftCount = 0;
this.lexer.setInput(input);
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
if (typeof this.lexer.yylloc == 'undefined')
this.lexer.yylloc = {};
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
if (typeof this.yy.parseError === 'function')
this.parseError = this.yy.parseError;
function popStack (n) {
stack.length = stack.length - 2*n;
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}
function lex() {
var token;
token = self.lexer.lex() || 1; // $end = 1
// if token isn't its numeric value, convert
if (typeof token !== 'number') {
token = self.symbols_[token] || token;
}
return token;
}
var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
while (true) {
// retreive state number from top of stack
state = stack[stack.length-1];
// use default actions if available
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol == null)
symbol = lex();
// read action for current state and first input
action = table[state] && table[state][symbol];
}
// handle parse error
_handle_error:
if (typeof action === 'undefined' || !action.length || !action[0]) {
if (!recovering) {
// Report error
expected = [];
for (p in table[state]) if (this.terminals_[p] && p > 2) {
expected.push("'"+this.terminals_[p]+"'");
}
var errStr = '';
if (this.lexer.showPosition) {
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
} else {
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
(symbol == 1 /*EOF*/ ? "end of input" :
("'"+(this.terminals_[symbol] || symbol)+"'"));
}
this.parseError(errStr,
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
}
// just recovered from another error
if (recovering == 3) {
if (symbol == EOF) {
throw new Error(errStr || 'Parsing halted.');
}
// discard current lookahead and grab another
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
symbol = lex();
}
// try to recover from error
while (1) {
// check for error recovery rule in this state
if ((TERROR.toString()) in table[state]) {
break;
}
if (state == 0) {
throw new Error(errStr || 'Parsing halted.');
}
popStack(1);
state = stack[stack.length-1];
}
preErrorSymbol = symbol; // save the lookahead token
symbol = TERROR; // insert generic error symbol as new lookahead
state = stack[stack.length-1];
action = table[state] && table[state][TERROR];
recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
}
// this shouldn't happen, unless resolve defaults are off
if (action[0] instanceof Array && action.length > 1) {
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
}
switch (action[0]) {
case 1: // shift
//this.shiftCount++;
stack.push(symbol);
vstack.push(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]); // push state
symbol = null;
if (!preErrorSymbol) { // normal execution/no error
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0)
recovering--;
} else { // error just occurred, resume old lookahead f/ before error
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2: // reduce
//this.reductionCount++;
len = this.productions_[action[1]][1];
// perform semantic action
yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
// default location, uses first token for firsts, last for lasts
yyval._$ = {
first_line: lstack[lstack.length-(len||1)].first_line,
last_line: lstack[lstack.length-1].last_line,
first_column: lstack[lstack.length-(len||1)].first_column,
last_column: lstack[lstack.length-1].last_column
};
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== 'undefined') {
return r;
}
// pop off stack
if (len) {
stack = stack.slice(0,-1*len*2);
vstack = vstack.slice(0, -1*len);
lstack = lstack.slice(0, -1*len);
}
stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
vstack.push(yyval.$);
lstack.push(yyval._$);
// goto new state = table[STATE][NONTERMINAL]
newState = table[stack[stack.length-2]][stack[stack.length-1]];
stack.push(newState);
break;
case 3: // accept
return true;
}
}
return true;
}};
/* Jison generated lexer */
var lexer = (function(){
var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
if (this.yy.parseError) {
this.yy.parseError(str, hash);
} else {
throw new Error(str);
}
},
setInput:function (input) {
this._input = input;
this._more = this._less = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
return this;
},
input:function () {
var ch = this._input[0];
this.yytext+=ch;
this.yyleng++;
this.match+=ch;
this.matched+=ch;
var lines = ch.match(/\n/);
if (lines) this.yylineno++;
this._input = this._input.slice(1);
return ch;
},
unput:function (ch) {
this._input = ch + this._input;
return this;
},
more:function () {
this._more = true;
return this;
},
less:function (n) {
this._input = this.match.slice(n) + this._input;
},
pastInput:function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
},
upcomingInput:function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20-next.length);
}
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
},
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c+"^";
},
next:function () {
if (this.done) {
return this.EOF;
}
if (!this._input) this.done = true;
var token,
match,
tempMatch,
index,
col,
lines;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i=0;i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (!this.options.flex) break;
}
}
if (match) {
lines = match[0].match(/\n.*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {first_line: this.yylloc.last_line,
last_line: this.yylineno+1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
this.yytext += match[0];
this.match += match[0];
this.yyleng = this.yytext.length;
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
if (this.done && this._input) this.done = false;
if (token) return token;
else return;
}
if (this._input === "") {
return this.EOF;
} else {
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
}
},
lex:function lex() {
var r = this.next();
if (typeof r !== 'undefined') {
return r;
} else {
return this.lex();
}
},
begin:function begin(condition) {
this.conditionStack.push(condition);
},
popState:function popState() {
return this.conditionStack.pop();
},
_currentRules:function _currentRules() {
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
},
topState:function () {
return this.conditionStack[this.conditionStack.length-2];
},
pushState:function begin(condition) {
this.begin(condition);
}});
lexer.options = {};
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0:/* skip whitespace */
break;
case 1:return 6
break;
case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4
break;
case 3:return 17
break;
case 4:return 18
break;
case 5:return 23
break;
case 6:return 24
break;
case 7:return 22
break;
case 8:return 21
break;
case 9:return 10
break;
case 10:return 11
break;
case 11:return 8
break;
case 12:return 14
break;
case 13:return 'INVALID'
break;
}
};
lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/];
lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}};
;
return lexer;})()
parser.lexer = lexer;
return parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = jsonlint;
exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); }
exports.main = function commonjsMain(args) {
if (!args[1])
throw new Error('Usage: '+args[0]+' FILE');
if (typeof process !== 'undefined') {
var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
} else {
var cwd = require("file").path(require("file").cwd());
var source = cwd.join(args[1]).read({charset: "utf-8"});
}
return exports.parser.parse(source);
}
if (typeof module !== 'undefined' && require.main === module) {
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
}
}