diff --git a/.eslintignore b/.eslintignore index 325be71d..a710e413 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ -beautify/ -codemirror/ -csslint/ +vendor/ +vendor-overwrites/ diff --git a/.eslintrc b/.eslintrc index 24b34542..2e335468 100644 --- a/.eslintrc +++ b/.eslintrc @@ -16,7 +16,9 @@ globals: URLS: false BG: false notifyAllTabs: false + queryTabs: false getTab: false + getOwnTab: false getActiveTab: false getActiveTabRealURL: false getTabRealURL: false @@ -35,6 +37,8 @@ globals: saveStyleSafe: false sessionStorageHash: false download: false + doTimeout: false + invokeOrPostpone: false # localization.js template: false t: false @@ -78,7 +82,7 @@ rules: dot-location: [2, property] dot-notation: [0] eol-last: [2] - eqeqeq: [0] + eqeqeq: [1, always] func-call-spacing: [2, never] func-name-matching: [0] func-names: [0] @@ -89,7 +93,7 @@ rules: id-blacklist: [0] id-length: [0] id-match: [0] - indent: [2, 2, {VariableDeclarator: 0, SwitchCase: 1}] + indent-legacy: [2, 2, {VariableDeclarator: 0, SwitchCase: 1}] jsx-quotes: [0] key-spacing: [0] keyword-spacing: [2] @@ -128,7 +132,7 @@ rules: no-duplicate-imports: [2] no-else-return: [0] no-empty-character-class: [2] - no-empty-function: [0] + no-empty-function: [1] no-empty-pattern: [2] no-empty: [2, {allowEmptyCatch: true}] no-eq-null: [2] @@ -162,7 +166,7 @@ rules: no-mixed-operators: [0] no-mixed-requires: [2, true] no-mixed-spaces-and-tabs: [2] - no-multi-spaces: [0] + no-multi-spaces: [2, {ignoreEOLComments: true}] no-multi-str: [2] no-multiple-empty-lines: [2, {max: 2, maxEOF: 0, maxBOF: 0}] no-native-reassign: [2] @@ -211,7 +215,7 @@ rules: no-unreachable: [2] no-unsafe-finally: [2] no-unsafe-negation: [2] - no-unused-expressions: [2] + no-unused-expressions: [1] no-unused-labels: [0] no-unused-vars: [1, {args: after-used, vars: local, argsIgnorePattern: ^_}] no-use-before-define: [2, nofunc] @@ -245,7 +249,7 @@ rules: sort-imports: [0] sort-keys: [0] space-before-blocks: [2, always] - space-before-function-paren: [2, never] + space-before-function-paren: [2, {anonymous: always, asyncArrow: always, named: never}] space-in-parens: [2, never] space-infix-ops: [2] space-unary-ops: [2] diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..0fb81935 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,33 @@ +# Contributing to Stylus + +1. [Getting Involved](#getting-involved) +2. [How to Report Issues](#how-to-report-issues) +3. [Adding Tranlations](#adding-translations) +4. [Core Style Guide](#core-style-guide) +5. [Getting Started](#getting-started) + +## Getting Involved + +There are a number of ways to get involved with the development of Stylus. Even if you've never contributed to an Open Source project before, we're always looking for help by identifying issues and suggesting improvements. + +## How to Report issues + +When an issue is opened, a template is provided. Please answer these questions as thoroughly as possible. If we were psychic, we'd be hanging out in casinos playing poker until they kicked us out. We can't read your mind! Please provide step-by-step direction on how to reproduce the issue as well as the browser, operating system and versions of each. + +When adding a feature request, please include + +## Adding Translations + +You can help us translate the extension on [Transifex](https://www.transifex.com/github-7/Stylus). When `messages.json` file is ready to be merged, please open a new bug report in [stylus/issues](https://github.com/openstyles/stylus/issues). + +## Core Style Guide + +* Use the provided `.editorconfig` file with your code editor. Don't know what that is? Then check out http://editorconfig.org/. + +## Getting Started + +* First open an issue to discuss your changes. +* Then download, fork or clone this repository. + +* Make any changes within a branch of this repository (not the `master` branch). +* Submit a pull request and include a reference to the initial issue with the discussion. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..a44bd728 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ + + +* **Browser**: +* **Operating System**: +* **Screenshot**: + +* **HTML of the section where the issue occurs**: + + + +````html + +```` diff --git a/README.md b/README.md index d811f077..cc1c379b 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,15 @@ See the [help docs](http://userstyles.org/help/stylish_chrome) or [ask in userst ## Contributing -The source is hosted on [GitHub](https://github.com/schomery/stylish-chrome) and pull requests are welcome. +The source is hosted on [GitHub](https://github.com/openstyles/stylus) and pull requests are welcome. -You can help us translate the extension on [Transifex](https://www.transifex.com/github-7/Stylus). When `messages.json` file is ready to be merged, please open a new bug report in [stylish-chrome/issues](https://github.com/schomery/stylish-chrome/issues). +You can help us translate the extension on [Transifex](https://www.transifex.com/github-7/Stylus). When `messages.json` file is ready to be merged, please open a new bug report in [stylus/issues](https://github.com/openstyles/stylus/issues). + +See our [contributing](./.github/CONTRIBUTING.md) page for more details. ## License -For copyright status of the "codemirror" directory, see codemirror/LICENSE. Everything else is: +For copyright status of the "codemirror" directory, see [codemirror/LICENSE](https://github.com/openstyles/stylus/blob/master/src/vendor/codemirror/LICENSE). Everything else is: Copyright (C) 2005-2014 Jason Barnabe diff --git a/_locales/ar/messages.json b/_locales/ar/messages.json index 50c8f547..56ca799a 100644 --- a/_locales/ar/messages.json +++ b/_locales/ar/messages.json @@ -3,22 +3,6 @@ "message": "كل شيء", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "تمكين", "description": "Label for the button to enable a style" @@ -35,42 +19,18 @@ "message": "البحث عن تحديث", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "مساعدة", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "البحث عن المزيد من الأنماط لموقع الويب هذا.", + "message": "البحث عن المزيد من الأنماط لموقع الويب هذا", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "الأنماط المثبتة", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "ممكّن", "description": "Label for the enabled state of styles" @@ -87,14 +47,6 @@ "message": "حفظ", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "إضافة", "description": "Label for the button to add an 'applies' entry" @@ -112,30 +64,10 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "تعطيل", "description": "Label for the button to disable a style" }, - "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "رجوع للإدارة", "description": "Label for cancel button for style editing" @@ -144,40 +76,14 @@ "message": "لقد أجريت تغييرات على هذا النمط بدون حفظها.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { "message": "أخفق التحديث - الخادم يتعذر الوصول إليه.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "هل تريد بالتأكيد حذف هذا النمط؟", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "ينطبق على: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -187,15 +93,6 @@ } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { "message": "الأقسام", "description": "Title for the style sections section" @@ -217,18 +114,6 @@ "message": "عناوين URL البادئة بـ", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, "sectionHelp": { "message": "تتيح لك الأقسام تحديد أجزاء مختلفة من الرمز لتطبيقها على مجموعات مختلفة من عناوين URL بالنمط نفسه. فعلى سبيل المثال، يمكن لنمط مفرد تغيير الصفحة الرئيسية لموقع ويب بطريقة، مع تغيير بقية أجزاء موقع الويب بطريقة أخرى.", "description": "Help text for sections" @@ -245,22 +130,6 @@ "message": "إزالة", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "ينطبق على", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +139,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "أخفق التحديث - استجاب الخادم بالرمز $code$.", + "message": "أخفق التحديث - استجاب الخادم بالرمز $code$", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +155,10 @@ "message": "تثبيت التحديث", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "إزالة القسم", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "اكتمل التحديث.", "description": "Text that displays when an update completed" @@ -314,10 +171,6 @@ "message": "الرمز", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "استخدم عناصر تحكم 'ينطبق على' لتقييد عناوين URL التي ينطبق عليها الرمز في هذا القسم.", "description": "Help text for 'applies to' section" @@ -334,58 +187,10 @@ "message": "إضافة نمط", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "البحث عن تحديثات لكل الأنماط", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "حذف", "description": "Label for the button to delete a style" @@ -394,24 +199,12 @@ "message": "كتابة نمط جديد", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "تعديل", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "يمكنك تغيير نمط الويب باستخدام Stylus، وهي أداة لإدارة أنماط المستخدم. وتتيح Stylus لك بسهولة تثبيت المظاهر والأشكال الخارجية لكل من Google، وFacebook وYouTube وOrkut فضلاً عن الكثير جدًا من مواقع الويب الأخرى.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/cs/messages.json b/_locales/cs/messages.json index 2cf3eebe..fce24a4b 100644 --- a/_locales/cs/messages.json +++ b/_locales/cs/messages.json @@ -7,6 +7,22 @@ "message": "výchozí", "description": "Default CodeMirror CSS theme option on the edit style page" }, + "styleRegexpTestTitle": { + "message": "Seznam shod otevřených listů (klepnutím na URL zaměříte daný list)", + "description": "RegExp test report: title of the report" + }, + "bckpInstStyles": { + "message": "Exportovat styly", + "description": "" + }, + "optionsUpdateInterval": { + "message": "Automaticky vyhledávat a instalovat aktualizace stylů (interval je v hodinách)", + "description": "" + }, + "styleEnabledToggleHint": { + "message": "Stiskem Alt + Enter můžete zapnout/vypnout a uložit styl", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "Exportovat", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -15,6 +31,14 @@ "message": "Problémy", "description": "Label for the CSSLint issues block on the style edit page" }, + "optionsBadgeNormal": { + "message": "Barva pozadí", + "description": "" + }, + "updateCheckHistory": { + "message": "Historie aktualizací", + "description": "" + }, "cm_tabSize": { "message": "Velikost tabulátorů", "description": "Label for the text box controlling tab size option for the style editor." @@ -27,6 +51,14 @@ "message": "Zadejte název.", "description": "Error displayed when user saves without providing a name" }, + "genericHistoryLabel": { + "message": "Historie", + "description": "Used in various places to show a history log of something" + }, + "shortcutsNote": { + "message": "Nastavit klávesové zkratky", + "description": "" + }, "appliesDomainOption": { "message": "URL adresy na doméně", "description": "Option to make the style apply to the entered string as a domain" @@ -35,18 +67,42 @@ "message": "Zkontrolovat aktualizace", "description": "Label for the button to check a single style for an update" }, + "styleNotAppliedRegexpProblemTooltip": { + "message": "Styl nebyl použit kvůli nesprávnému použití „regexp()“", + "description": "Tooltip in the popup for styles that were not applied at all" + }, + "styleRegexpInvalidExplanation": { + "message": "Některá „regexp()“ pravidla nelze vůbec sestavit.", + "description": "" + }, + "optionsIconDark": { + "message": "Tmavé vzhledy", + "description": "" + }, "importAppendLabel": { "message": "Připojit ke stylu", "description": "Label for the button to import a style and append to the existing sections" }, + "optionsOpenManager": { + "message": "Spravovat styly", + "description": "" + }, "updateAllCheckSucceededNoUpdate": { - "message": "Všechny styly jsou aktuální.", + "message": "Aktualizace nenalezeny.", "description": "Text that displays when an update all check completed and no updates are available" }, + "importReportLegendAdded": { + "message": "přidáno", + "description": "Text after the number of styles added in the report shown after importing styles" + }, "styleFromMozillaFormatPrompt": { - "message": "Vložit kód v Mozilla-formátu", + "message": "Vložte kód v Mozilla formátu", "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" }, + "dragDropMessage": { + "message": "Pro import upusťte soubor zálohy kdekoliv na této stránce.", + "description": "Drag'n'drop message" + }, "helpAlt": { "message": "Nápověda", "description": "Alternate text for help buttons" @@ -55,46 +111,74 @@ "message": "Vyhledat", "description": "Label before the search input field in the editor shown on Ctrl-F" }, + "manageFaviconsGray": { + "message": "Zešednutí", + "description": "Label for the checkbox that toggles grayed out mode of applies-to favicons in the new UI on manage page" + }, "confirmYes": { "message": "Ano", "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "Najít styly pro tento web.", + "message": "Najít styly pro tento web", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { - "message": "Nainstalované Styly", + "message": "Nainstalované styly", "description": "Heading for the manage page" }, "styleBeautify": { "message": "Zkrášlit", "description": "Label for the CSS-beautifier button on the edit style page" }, + "styleRegexpProblemTooltip": { + "message": "Počet sekcí nepoužitých kvůli nesprávnému použití „regexp()“", + "description": "Tooltip in the popup for styles that were applied only partially" + }, "styleEnabledLabel": { "message": "Povolen", "description": "Label for the enabled state of styles" }, "styleToMozillaFormatHelp": { - "message": "Mozilla formát umožní použití stylu v prohlížeči Mozilla Firefox a jeho odeslání na userstyles.org.", + "message": "Kód v Mozilla formátu může být odeslán na userstyles.org a použit v aplikaci Stylish pro Firefox", "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" }, "sectionAdd": { "message": "Přidat další sekci", "description": "Label for the button to add a section" }, + "styleRegexpTestPartial": { + "message": "Shoda je nepřesná, proto přeskočeno", + "description": "RegExp test report: label for the partially matching expressions" + }, "styleSaveLabel": { "message": "Uložit", "description": "Label for save button for style editing" }, + "confirmDelete": { + "message": "Smazat", + "description": "" + }, + "confirmCancel": { + "message": "Storno", + "description": "" + }, + "retrieveBckp": { + "message": "Importovat styly", + "description": "" + }, "confirmStop": { "message": "Zastavit", "description": "'Stop' button in a confirm dialog" }, "writeStyleForURL": { - "message": "tato URL", + "message": "tuto URL", "description": "Text for link in toolbar pop-up to write a new style for the current URL" }, + "optionsSubheading": { + "message": "Další možnosti", + "description": "Subheading for options section on manage page." + }, "appliesAdd": { "message": "Přidat", "description": "Label for the button to add an 'applies' entry" @@ -103,8 +187,16 @@ "message": "URL adresy odpovídající reg. výrazu", "description": "Option to make the style apply to the entered string as a regular expression" }, + "optionsAdvancedExposeIframesNote": { + "message": "Povolení speciální iframe CSS syntaxe jako např. „html[stylus-iframe] h1 { display:none }“", + "description": "" + }, + "importReportLegendUpdatedCode": { + "message": "kód aktualizován", + "description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles" + }, "styleInstall": { - "message": "Nainstalovat '$stylename$' do Stylus?", + "message": "Nainstalovat „$stylename$“ do Stylus?", "description": "Confirmation when installing a style", "placeholders": { "stylename": { @@ -112,22 +204,50 @@ } } }, + "optionsBadgeDisabled": { + "message": "Barva pozadí při zakázání", + "description": "" + }, + "optionsCheck": { + "message": "Aktualizovat styly", + "description": "" + }, "manageText": { - "message": "Získat styly na userstyles.org | Získat pomoc", + "message": "Získat styly na userstyles.org | Nápověda", "description": "Help text on the manage page" }, + "manageOnlyLocal": { + "message": "Pouze lokální styly", + "description": "Checkbox to show only locally created styles i.e. non-updatable" + }, "searchStyles": { "message": "Prohledat obsah", "description": "Label for the search filter textbox on the Manage styles page" }, + "optionsUpdateImportNote": { + "message": "Importujete-li zálohy stylů ze starší verze nebo z rozšíření Stylish, proveďte jednorázovou ruční kontrolu aktualizací ve správci stylů, aby byly všechny styly aktuální.", + "description": "" + }, + "checkAllUpdatesForce": { + "message": "Vynutit aktualizaci (neprovedeny žádné změny stylů)", + "description": "Label for the button to apply all detected updates" + }, + "unreachableFileHint": { + "message": "Stylus může přistupovat k file:// URL pouze při povolení odpovídající možnosti pro rozšíření Stylus ve správci chrome://extensions.", + "description": "Note in the toolbar popup for file:// URLs" + }, "disableStyleLabel": { "message": "Zakázat", "description": "Label for the button to disable a style" }, "prefShowBadge": { - "message": "Zobrazit počet aktivních stylů pro současnou stránku u tlačítka v panelu nástrojů", + "message": "Počet aktivních stylů pro současnou stránku", "description": "Label for the checkbox controlling toolbar badge text." }, + "manageFavicons": { + "message": "Favicony v části „Platí pro“ ", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, "menuShowBadge": { "message": "Zobrazit počet aktivních stylů", "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." @@ -148,8 +268,12 @@ "message": "Importovat", "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" }, + "shortcuts": { + "message": "Zkratky", + "description": "Go to shortcut configuration" + }, "updateCheckFailServerUnreachable": { - "message": "Aktualizace selhala - server je nedosažitelný.", + "message": "Aktualizace se nezdařila: server je nedosažitelný.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -160,15 +284,21 @@ "message": "Použít všechny aktualizace", "description": "Label for the button to apply all detected updates" }, + "optionsReset": { + "message": "Obnovit výchozí nastavení", + "description": "" + }, + "optionsCustomizeUpdate": { + "message": "Aktualizace", + "description": "" + }, "deleteStyleConfirm": { "message": "Opravdu chcete tento styl smazat?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" + "optionsCustomizePopup": { + "message": "Vyskakovací okno", + "description": "" }, "styleBadRegexp": { "message": "Tento regulérní výraz je neplatný.", @@ -187,8 +317,16 @@ } } }, + "optionsIconLight": { + "message": "Světlé vzhledy", + "description": "" + }, + "openStylesManager": { + "message": "Otevřít správce stylů", + "description": "Label for the style maanger opener in the browser action context menu." + }, "styleUpdate": { - "message": "Určitě chcete aktualizovat '$stylename$'?", + "message": "Určitě chcete aktualizovat „$stylename$“?", "description": "Confirmation when updating a style", "placeholders": { "stylename": { @@ -200,8 +338,12 @@ "message": "Sekce", "description": "Title for the style sections section" }, + "importReportLegendInvalid": { + "message": "neplatné přeskočeny", + "description": "Text after the number of styles skipped due to being invalid (not a Stylus/Stylish backup file probably) in the report shown after importing styles" + }, "editStyleTitle": { - "message": "Upravit Styl $stylename$", + "message": "Upravit styl $stylename$", "description": "Title of the page for editing styles", "placeholders": { "stylename": { @@ -217,17 +359,33 @@ "message": "URL adresy začínající", "description": "Option to make the style apply to the entered string as a URL prefix" }, + "cm_matchHighlightToken": { + "message": "Stopa pod kurzorem", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected" + }, "searchRegexp": { - "message": "Použít /re/ syntaxi pro vyhledávání s regulérními výrazy", + "message": "Použijte /re/ syntaxi pro vyhledávání s regulérními výrazy", "description": "Label after the search input field in the editor shown on Ctrl-F" }, + "updateCheckManualUpdateHint": { + "message": "Vynucení aktualizace přepíše všechny lokální úpravy.", + "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" + }, + "toggleStyle": { + "message": "Přepnout styl", + "description": "Label for the checkbox to enable/disable a style" + }, "importReplaceTooltip": { "message": "Zahodit obsah současného stylu a přepsat ho importovaným stylem", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "Dvojklikem maximalizujete/obnovíte výšku", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { - "message": "Vypsat styly před příkazy v menu pod tlačítkem na panelu nástrojů", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "message": "Styly před příkazy", + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Pomocí sekcí můžete v jednom stylu nadefinovat více částí kódu pro různé skupiny URL adres. Jeden styl pak může např. změnit hlavní stránku webu jinak, než jeho zbytek.", @@ -245,32 +403,44 @@ "message": "Odstranit", "description": "Label for the button to remove an 'applies' entry" }, + "updatesCurrentlyInstalled": { + "message": "Instalované aktualizace:", + "description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates." + }, "styleToMozillaFormatTitle": { "message": "Styl v Mozilla formátu", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { - "message": "Napsat nový styl", + "message": "Napsat nový styl pro:", "description": "Label for toolbar pop-up that precedes the links to write a new style" }, "replace": { "message": "Nahradit", "description": "Label before the replace input field in the editor shown on Ctrl-H" }, + "styleRegexpTestNone": { + "message": "Žádné odpovídající listy", + "description": "RegExp test report: label for expressions that didn't match any tabs" + }, + "importReportLegendUpdatedMeta": { + "message": "meta informace aktualizována", + "description": "Text after the number of styles with updated meta info like name/url in the report shown after importing styles" + }, "appliesLabel": { "message": "Platí pro", "description": "Label for 'applies to' fields on the edit/add screen" }, + "openOptionsPopup": { + "message": "Možnosti", + "description": "Go to Options UI" + }, "openManage": { - "message": "Spravovat nainstalovné styly", + "message": "Spravovat", "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Aktualizace selhala - server opověděl kódem $code$.", + "message": "Aktualizace se nezdařila: server odpověděl kódem $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,56 +456,144 @@ "message": "Nainstalovat aktualizaci", "description": "Label for the button to install an update for a single style" }, + "optionsCheckUpdate": { + "message": "Vyhledávat a instalovat všechny aktualizace", + "description": "" + }, + "importReportTitle": { + "message": "Import stylů dokončen", + "description": "Title of the report shown after importing styles" + }, "styleMozillaFormatHeading": { - "message": "Mozilla Formát", + "message": "Mozilla formát", "description": "Heading for the section with buttons to import/export Mozilla format of the style" }, - "stylusUnavailableForURL": { - "message": "Stylus nefunguje na těchto stránkách.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + "cm_matchHighlight": { + "message": "Zvýraznění", + "description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor." + }, + "styleRegexpPartialExplanation": { + "message": "Tento styl obsahuje částečnou regexp shodu, která je však v rozporu s CSS4 @document specifikací, která vyžaduje přesnou shodu URL adresy. Dotčené CSS části nebyly na stránku použity. Tento styl byl pravděpodobně vytvořen v rozšíření Stylish pro Chrome, které nesprávně kontroluje regexp() pravidla již od své první verze (známá chyba).", + "description": "" + }, + "styleBeautifyIndentConditional": { + "message": "Odsadit @media, @supports", + "description": "CSS-beautifier option" + }, + "unreachableContentScript": { + "message": "Se stránkou nelze komunikovat. Zkuste znovu načíst list.", + "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" }, "sectionRemove": { "message": "Odstranit sekci", "description": "Label for the button to remove a section" }, "disableAllStyles": { - "message": "Vypnout všechny styly.", + "message": "Vypnout všechny styly", "description": "Label for the checkbox that turns all enabled styles off." }, + "updateCheckSkippedMaybeLocallyEdited": { + "message": "Tento styl mohl být upraven lokálně.", + "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications" + }, "undoGlobal": { - "message": "Zpět (globálně)", + "message": "Zpět ve všech sekcích", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "Ikona na panelu nástrojů", + "description": "" + }, "updateCompleted": { "message": "Aktalizace dokončena.", "description": "Text that displays when an update completed" }, "checkingForUpdate": { - "message": "Kontroluji...", + "message": "Kontrola…", "description": "Text to display when checking a style for an update" }, + "styleRegexpTestFull": { + "message": "Odpovídající listy", + "description": "RegExp test report: label for the fully matching expressions" + }, + "manageMaxTargets": { + "message": "Počet položek v části „Platí pro“", + "description": "Label for the numeric input box to limit max number of applies-to targets in the new UI on manage page" + }, + "manageFaviconsHelp": { + "message": "Stylus používá externí službu https://www.google.com/s2/favicons", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, + "updateCheckSkippedLocallyEdited": { + "message": "Tento styl byl upraven lokálně.", + "description": "Text that displays when an update check skipped updating the style to avoid losing local modifications" + }, + "optionsResetButton": { + "message": "Obnovit nastavení", + "description": "" + }, "sectionCode": { "message": "Kód", "description": "Label for the code for a section" }, + "optionsAdvancedContextDelete": { + "message": "Přidat tlačítko Smazat do kontextové nabídky v editoru", + "description": "" + }, + "importReportLegendUpdatedBoth": { + "message": "meta informace a kód aktualizován", + "description": "Text after the number of styles updated entirely in the report shown after importing styles" + }, "cm_smartIndent": { "message": "Použít chytré odsazení", "description": "Label for the checkbox controlling smart indentation option for the style editor." }, + "styleRegexpTestButton": { + "message": "Otestovat RegExp", + "description": "RegExp test button label in the editor shown when applies-to list has a regexp value" + }, "appliesHelp": { - "message": "Nastavte 'Platí pro', pokud chcete limitovat, na které URL adresy se použije kód v této sekci.", + "message": "Nastavte „Platí pro“, pokud chcete omezit působnost kódu v této sekci na určité URL adresy.", "description": "Help text for 'applies to' section" }, "editStyleHeading": { - "message": "Upravit Styl", + "message": "Upravit styl", "description": "Title of the page for editing styles" }, + "editDeleteText": { + "message": "Smazat", + "description": "Label for the context menu item in the editor to delete selected text" + }, + "cm_autocompleteOnTyping": { + "message": "Automatické dokončování během psaní", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "Jako bezpečností opatření, prohlížeč brání rozšířením v ovlivnění vestavěných stránek (např. chrome://version nebo about:addons) a stránek dalších rozšíření. Každý prohlížeč také omezuje přístup do vlastního obchodu s rozšířeními (např. Chrome Web Store nebo AMO).", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "cm_matchHighlightSelection": { + "message": "Pouze výběr", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" + }, "appliesUrlOption": { - "message": "URL", + "message": "URL adresa", "description": "Option to make the style apply to the entered string as a URL" }, + "updateAllCheckSucceededSomeEdited": { + "message": "Některé aktualizovatelné styly nebyly aktualizovány kvůli zabránění ztrátám možných lokálních změn. Aktualizace může být vynucena individuálně nebo spuštěním další aktualizace všech stylů (lokální změny mohou být přepsány).", + "description": "Text that displays when an update all check completed and no updates are available" + }, + "stylusUnavailableForURL": { + "message": "Stylus nepracuje se stránkami jako je tato.", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "manageOnlyUpdates": { + "message": "Pouze s aktualizacemi nebo chybami", + "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed" + }, "addStyleTitle": { - "message": "Přidat Styl", + "message": "Přidat styl", "description": "Title of the page for adding styles" }, "importReplaceLabel": { @@ -346,30 +604,74 @@ "message": "Nastala chyba při používání Stylus databáze. Chcete navštívit webovou stránku s možnými řešeními?", "description": "Prompt when a DB error is encountered" }, + "optionsAdvanced": { + "message": "Pokročilé", + "description": "" + }, "importAppendTooltip": { "message": "Připojit importovaný styl k současnému stylu", "description": "Tooltip for the button to import a style and append to the existing sections" }, + "editorStylesButton": { + "message": "Najít styly pro editor", + "description": "Find styles for the editor" + }, + "optionsOpen": { + "message": "Otevřít", + "description": "" + }, "helpKeyMapHotkey": { "message": "Stiskněte kláves. zkratku", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, + "styleRegexpTestInvalid": { + "message": "Neplatné regexp přeskočeny", + "description": "RegExp test report: label for the invalid expressions" + }, "replaceAll": { "message": "Nahradit vše", "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" }, + "importReportUnchanged": { + "message": "Žádné změny.", + "description": "Message in the report shown after importing styles" + }, + "optionsActions": { + "message": "Akce", + "description": "" + }, "editGotoLine": { "message": "Přejít na řádek (nebo na line:col)", "description": "Go to line or line:column on Ctrl-G in style code editor" }, + "manageOnlyLocalTooltip": { + "message": "(styly nenainstalované prostřednictvím webu userstyles.org)", + "description": "Tooltip for the checkbox to show only locally created styles i.e. non-updatable" + }, "checkAllUpdates": { "message": "Aktualizovat všechny styly", "description": "Label for the button to check all styles for updates" }, + "openOptionsManage": { + "message": "Možnosti rozhraní", + "description": "Go to Options UI" + }, "issuesHelp": { "message": "Problémy nalezené aplikací CSSLint s těmito povolenými pravidly:", "description": "Help popup message for the CSSLint issues block on the style edit page" }, + "optionsCustomizeBadge": { + "message": "Ikona tlačítka na panelu", + "description": "" + }, + "importReportLegendIdentical": { + "message": "identické přeskočeny", + "description": "Text after the number of styles skipped due to being identical to the already installed ones in the report shown after importing styles" + }, + "optionsPopupWidth": { + "message": "Šířka vyskakovacího okna [px]", + "description": "" + }, "confirmNo": { "message": "Ne", "description": "'No' button in a confirm dialog" @@ -382,6 +684,18 @@ "message": "Přiřazení kláves", "description": "Label for the drop-down list controlling the keymap for the style editor." }, + "manageNewUI": { + "message": "Nové rozložení správy rozhraní", + "description": "Label for the checkbox that toggles the new UI on manage page" + }, + "importReportUndoneTitle": { + "message": "Import byl přerušen", + "description": "Title of the message box shown after undoing the import of styles" + }, + "genericDisabledLabel": { + "message": "Zakázáno", + "description": "Used in various lists/options to indicate that something is disabled" + }, "cm_indentWithTabs": { "message": "Použít tabulátory s chytrým odsazením", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." @@ -394,10 +708,26 @@ "message": "Smazat", "description": "Label for the button to delete a style" }, + "updateCheckManualUpdateForce": { + "message": "Instalovat aktualizaci (lokální změny budou přepsány)", + "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" + }, + "optionsAdvancedExposeIframes": { + "message": "Obnažit iframe prvky pomocí HTML[stylus-iframe]", + "description": "" + }, "addStyleLabel": { "message": "Napsat nový styl", "description": "Label for the button to go to the add style page" }, + "optionsUpdateIntervalNote": { + "message": "Pro vypnutí automatického vyhledávání aktualizací stylů nastavte interval na nulu.", + "description": "" + }, + "backupButtons": { + "message": "Zálohovat", + "description": "Heading for backup" + }, "manageOnlyEnabled": { "message": "Pouze povolené styly", "description": "Checkbox to show only enabled styles" @@ -410,12 +740,20 @@ "message": "Schéma", "description": "Label for the style editor's CSS theme." }, + "backupMessage": { + "message": "Vyberte soubor nebo ho přetáhněte na tuto stránku.", + "description": "Message for backup" + }, + "importReportUndone": { + "message": "styly navráceny zpět", + "description": "Text after the number of styles reverted in the message box shown after undoing the import of styles" + }, "helpKeyMapCommand": { "message": "Napište název příkazu", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "description": { - "message": "Přestylujte web pomocí Stylus, manažeru uživatelských stylů. Stylus vám umožní nainstalovat témata a skiny pro mnoho populárních webů.", + "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" } -} +} \ No newline at end of file diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 9d99a162..1052a73e 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -8,7 +8,7 @@ "description": "Default CodeMirror CSS theme option on the edit style page" }, "styleRegexpTestTitle": { - "message": "Liste der zutreffenden, geöffneten Tabs (URL anklicken um den Tab zu fokussieren)", + "message": "Liste der zutreffenden, geöffneten Tabs (URL anklicken, um den Tab zu fokussieren)", "description": "RegExp test report: title of the report" }, "bckpInstStyles": { @@ -19,6 +19,10 @@ "message": "Automatischer Update- und Installations-Intervall (in Stunden)", "description": "" }, + "styleEnabledToggleHint": { + "message": "Drücke Alt-Enter, um den Style zu aktivieren / deaktivieren und gleichzeitig zu speichern.", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "Exportieren", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -64,11 +68,15 @@ "description": "Label for the button to check a single style for an update" }, "styleNotAppliedRegexpProblemTooltip": { - "message": "Style wurde aufgrund der nicht gültigen Verwendung der Regulären Ausdrücke 'regexp()' nicht angewandt.", + "message": "Der Style wurde aufgrund ungültiger Regulärer Ausdrücke nicht angewandt.", "description": "Tooltip in the popup for styles that were not applied at all" }, "styleRegexpInvalidExplanation": { - "message": "Einige Regeln der Regulären Ausdrücke 'regexp()' konnten nicht überprüft werden.", + "message": "Einige Regeln der Regulären Ausdrücke konnten nicht überprüft werden.", + "description": "" + }, + "optionsIconDark": { + "message": "Dunkle Browser-Themes", "description": "" }, "importAppendLabel": { @@ -88,7 +96,7 @@ "description": "Text after the number of styles added in the report shown after importing styles" }, "styleFromMozillaFormatPrompt": { - "message": "Mozilla Format Code einfügen", + "message": "Mozilla-Format Code einfügen", "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" }, "dragDropMessage": { @@ -99,10 +107,6 @@ "message": "Hilfe", "description": "Alternate text for help buttons" }, - "confirmOK": { - "message": "OK", - "description": "" - }, "search": { "message": "Suche", "description": "Label before the search input field in the editor shown on Ctrl-F" @@ -116,19 +120,15 @@ "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "Weitere Styles für diese Webseite finden.", + "message": "Weitere Styles für diese Seite finden", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Installierte Styles", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleRegexpProblemTooltip": { - "message": "Anzahl der Bereiche wurde nicht angewendet aufgrund von nicht korrekten Verwendeten Regulären Ausdrücken 'regexp()'", + "message": "Anzahl der Bereiche, welche aufgrund nicht korrekt verwendeter Regulärer Ausdrücke nicht angewendet wurden.", "description": "Tooltip in the popup for styles that were applied only partially" }, "styleEnabledLabel": { @@ -144,7 +144,7 @@ "description": "Label for the button to add a section" }, "styleRegexpTestPartial": { - "message": "Kein genau Übereinstimmung, wurden daher übersprungen.", + "message": "Keine genaue Übereinstimmung, wurde daher übersprungen.", "description": "RegExp test report: label for the partially matching expressions" }, "styleSaveLabel": { @@ -180,11 +180,11 @@ "description": "Label for the button to add an 'applies' entry" }, "appliesRegexpOption": { - "message": "URLs, die mit dem Regulären Ausdrücken übereinstimmen", + "message": "URLs, die mit dem Regulären Ausdruck übereinstimmen", "description": "Option to make the style apply to the entered string as a regular expression" }, "optionsAdvancedExposeIframesNote": { - "message": "Aktiviert die Iframe-spezifische CSS Auszeichnung wie 'html[stylus-iframe] h1 { display:none; }'", + "message": "Aktiviert die iFrame-spezifische CSS Auszeichnung wie 'html[stylus-iframe] h1 { display:none; }'", "description": "" }, "importReportLegendUpdatedCode": { @@ -213,7 +213,7 @@ "description": "Help text on the manage page" }, "manageOnlyLocal": { - "message": "Nur Locale Styles anzeigen", + "message": "Nur lokale Styles anzeigen", "description": "Checkbox to show only locally created styles i.e. non-updatable" }, "searchStyles": { @@ -221,7 +221,7 @@ "description": "Label for the search filter textbox on the Manage styles page" }, "optionsUpdateImportNote": { - "message": "Nach dem Importieren alter Backups oder aus Stylish ist eine einmalige manuelle Überprüfung der Aktualisierungen (Updates) in der Verwaltung nötig. Dies stellt sicher das alle Styles auf dem aktuellsten Stand sind.", + "message": "Nach dem Importieren von Styles aus einer alten Version oder von Stylish ist eine einmalige manuelle Überprüfung der Aktualisierungen (Updates) in der Verwaltung nötig. Dies stellt sicher, dass alle Styles auf dem aktuellsten Stand sind.", "description": "" }, "checkAllUpdatesForce": { @@ -229,7 +229,7 @@ "description": "Label for the button to apply all detected updates" }, "unreachableFileHint": { - "message": "Stylus kann nur auf das file:// Protokoll in der URL zugreifen wenn dies in den Einstellungen der Erweiterung festgelegt ist.", + "message": "Stylus kann nur auf das file:// Protokoll in der URL zugreifen, wenn dies in den Einstellungen der Erweiterung unter chrome://extensions festgelegt wurde.", "description": "Note in the toolbar popup for file:// URLs" }, "disableStyleLabel": { @@ -257,7 +257,7 @@ "description": "Label for cancel button for style editing" }, "styleChangesNotSaved": { - "message": "Sie haben Änderungen an diesem Style vorgenommen, ohne diese zu speichern.", + "message": "Du hast Änderungen an diesem Style vorgenommen, ohne diese zu speichern.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, "importLabel": { @@ -269,7 +269,7 @@ "description": "Go to shortcut configuration" }, "updateCheckFailServerUnreachable": { - "message": "Aktualisierung fehlgeschlagen - Server nicht erreichbar.", + "message": "Update fehlgeschlagen: Server nicht erreichbar.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -284,18 +284,10 @@ "message": "Setzt diese Einstellungen auf die Standard-Werte zurück.", "description": "" }, - "optionsCustomizeUpdate": { - "message": "Updates", - "description": "" - }, "deleteStyleConfirm": { - "message": "Möchten Sie diesen Style wirklich löschen?", + "message": "Möchtest Du diesen Style wirklich löschen?", "description": "Confirmation before deleting a style" }, - "optionsCustomizePopup": { - "message": "Popup", - "description": "" - }, "styleBadRegexp": { "message": "Regulärer Ausdruck ist ungültig.", "description": "Validation message for a bad regexp in a style" @@ -313,12 +305,16 @@ } } }, + "optionsIconLight": { + "message": "Helle Browser-Themes", + "description": "" + }, "openStylesManager": { "message": "Style Verwaltung öffnen", "description": "Label for the style maanger opener in the browser action context menu." }, "styleUpdate": { - "message": "Möchten Sie '$stylename$' wirklich aktualisieren?", + "message": "Möchtest Du '$stylename$' wirklich aktualisieren?", "description": "Confirmation when updating a style", "placeholders": { "stylename": { @@ -352,11 +348,11 @@ "description": "Option to make the style apply to the entered string as a URL prefix" }, "cm_matchHighlightToken": { - "message": "Auszeichnung unter dem Textzeiger.", + "message": "Unter dem Textzeiger", "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected" }, "searchRegexp": { - "message": "Verwende die /re/ Syntax für Suche als Regulärer Ausdruck", + "message": "Verwende die /re/ Syntax zur Suche als Regulärer Ausdruck", "description": "Label after the search input field in the editor shown on Ctrl-F" }, "updateCheckManualUpdateHint": { @@ -371,12 +367,16 @@ "message": "Verwerfe den aktuellen Style-Inhalt und überschreibe ihn mit dem importierten", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "Doppelklick, um Höhe zu maximieren / wiederherzustellen", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { "message": "Styles vor den Bedienelementen", "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { - "message": "Mit Bereichen können Sie unterschiedliche Code-Teile auf unterschiedliche URLs in dem gleichen Style anwenden. Beispielsweise kann über einen einzigen Style die Startseite der Website geändert werden, während für den Rest der Website andere Änderungen gelten.", + "message": "Mit Bereichen kannst Du unterschiedliche Code-Teile auf unterschiedliche URLs in dem gleichen Style anwenden. Beispielsweise kann über einen einzigen Style die Startseite der Website geändert werden, während für den Rest der Website andere Änderungen gelten.", "description": "Help text for sections" }, "noStylesForSite": { @@ -399,10 +399,6 @@ "message": "Style im Mozilla Format", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "Style erstellen für:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -432,7 +428,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Aktualisierung fehlgeschlagen - Server hat den Fehler $code$ ausgegeben.", + "message": "Update fehlgeschlagen: Server antwortete mit Code $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -456,20 +452,20 @@ "message": "Importieren der Styles abgeschlossen.", "description": "Title of the report shown after importing styles" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "cm_matchHighlight": { "message": "Hervorheben", "description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor." }, "styleRegexpPartialExplanation": { - "message": "Dieser Style verwendet teilweise Übereinstimmende RegExps unter Missachtung der CSS4 @document Spezifikationen, welche eine vollständig Übereinstimmende URL benötigen. Der betroffene CSS Bereich wurde nicht auf die Website angewandt. Dieser Style wurde wahrscheinlich unter Stylish für Chrome erstellt, in der seit der ersten Version eine inkorrekte Überprüfung der Reguläre Ausdrücke 'regexp()' vorhanden ist (bekannter Fehler).", + "message": "Dieser Style verwendet teilweise Übereinstimmende Reguläre Ausdrücke unter Missachtung der CSS4 @document Spezifikationen, welche eine vollständig Übereinstimmende URL benötigen. Der betroffene CSS Bereich wurde nicht auf die Website angewandt. Dieser Style wurde wahrscheinlich unter Stylish für Chrome erstellt, in der seit der ersten Version eine inkorrekte Überprüfung der Regulären Ausdrücke vorhanden ist (bekannter Fehler).", "description": "" }, + "styleBeautifyIndentConditional": { + "message": "Rücke @media / @supports ein", + "description": "CSS-beautifier option" + }, "unreachableContentScript": { - "message": "Konnte nicht mit der Seite Kommunizieren. Bitte versuchen sie die Seite erneut zuladen.", + "message": "Konnte nicht mit der Seite kommunizieren. Bitte versuchen Sie, die Seite erneut zu laden.", "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" }, "sectionRemove": { @@ -477,17 +473,21 @@ "description": "Label for the button to remove a section" }, "disableAllStyles": { - "message": "Schalten Sie alle Stile aus", + "message": "Schalte alle Stile aus", "description": "Label for the checkbox that turns all enabled styles off." }, "updateCheckSkippedMaybeLocallyEdited": { - "message": "Dieser Style scheint Lokal bearbeitet worden zu sein.", + "message": "Dieser Style scheint lokal bearbeitet worden zu sein.", "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications" }, "undoGlobal": { - "message": "Rückgängig (global)", + "message": "Rückgängig machen in allen Bereichen", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "Symbolleisten-Icon", + "description": "" + }, "updateCompleted": { "message": "Aktualisierung abgeschlossen.", "description": "Text that displays when an update completed" @@ -509,19 +509,15 @@ "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" }, "updateCheckSkippedLocallyEdited": { - "message": "Dieser Style wurde Local bearbeitet.", + "message": "Dieser Style wurde lokal bearbeitet.", "description": "Text that displays when an update check skipped updating the style to avoid losing local modifications" }, "optionsResetButton": { "message": "Optionen zurücksetzten", "description": "" }, - "sectionCode": { - "message": "Code", - "description": "Label for the code for a section" - }, "optionsAdvancedContextDelete": { - "message": "'Löschen' im Editor Context-Menü hinzufügen", + "message": "'Löschen' im Editor-Kontextmenü hinzufügen", "description": "" }, "importReportLegendUpdatedBoth": { @@ -533,11 +529,11 @@ "description": "Label for the checkbox controlling smart indentation option for the style editor." }, "styleRegexpTestButton": { - "message": "Reguläre Ausdrücke Testen", + "message": "Reguläre Ausdrücke testen", "description": "RegExp test button label in the editor shown when applies-to list has a regexp value" }, "appliesHelp": { - "message": "Legen Sie mit den Einstellungen von 'Gilt für' fest, für welche URLs der Code in diesem Bereich gelten soll.", + "message": "Lege mit den Einstellungen von 'Gilt für' fest, für welche URLs der Code in diesem Bereich gelten soll.", "description": "Help text for 'applies to' section" }, "editStyleHeading": { @@ -548,16 +544,20 @@ "message": "Löschen", "description": "Label for the context menu item in the editor to delete selected text" }, + "cm_autocompleteOnTyping": { + "message": "Autovervollständigen bei Eingabe", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "Der Browser verbietet als Sicherheitsvorkehrung den Zugriff der Erweiterung auf interne Seiten (wie z.B. chrome://version oder about:addons) sowie andere Erweiterungsseiten. Jeder Browser begrenzt den Zugriff auf eigene Erweiterungs-Galerien (wie z.B. den Chrome Web Store oder addons.mozilla.org).", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, "cm_matchHighlightSelection": { "message": "Nur die Auswahl", "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "updateAllCheckSucceededSomeEdited": { - "message": "Einige Styles wurden nicht Aktualisiert um einen Datenverlust vorzubeugen. Die Aktualisierungen können durch eine manuelle Überprüfung erzwungen werden oder für alle verbliebenen Styles durch das ausführen der erzwungenen Aktualisierung (Lokal Bearbeitungen werden überschrieben).", + "message": "Einige Styles wurden nicht aktualisiert, um dem Verlust von lokalen Bearbeitungen vorzubeugen. Die Aktualisierungen können entweder durch einzelne manuelle Überprüfung oder durch eine erneute Ausführung der Aktualisierung für alle Styles erzwungen werden (Lokale Bearbeitungen werden dann überschrieben).", "description": "Text that displays when an update all check completed and no updates are available" }, "stylusUnavailableForURL": { @@ -577,7 +577,7 @@ "description": "Label for the button to import and overwrite current style" }, "dbError": { - "message": "Ein Fehler ist beim Verwenden der Stylus-Datenbank aufgetreten. Wollen Sie eine Webseite mit möglichen Lösungen besuchen?", + "message": "Ein Fehler ist beim Verwenden der Stylus-Datenbank aufgetreten. Willst Du eine Webseite mit möglichen Lösungen besuchen?", "description": "Prompt when a DB error is encountered" }, "optionsAdvanced": { @@ -645,7 +645,7 @@ "description": "Text after the number of styles skipped due to being identical to the already installed ones in the report shown after importing styles" }, "optionsPopupWidth": { - "message": "Popup breite (in pixeln)", + "message": "Popup-Breite (in Pixeln)", "description": "" }, "confirmNo": { @@ -685,7 +685,7 @@ "description": "Label for the button to delete a style" }, "updateCheckManualUpdateForce": { - "message": "Aktualisierungen Installieren (Lokale Bearbeitungen werden überschrieben)", + "message": "Aktualisierungen installieren (Lokale Bearbeitungen werden überschrieben)", "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" }, "optionsAdvancedExposeIframes": { @@ -697,7 +697,7 @@ "description": "Label for the button to go to the add style page" }, "optionsUpdateIntervalNote": { - "message": "Zum deaktivieren der automatischen Aktualisierungs-Überprüfung den Wert auf 0 setzten.", + "message": "Zum Deaktivieren der automatischen Aktualisierungs-Überprüfung den Wert auf 0 setzen.", "description": "" }, "backupButtons": { @@ -712,10 +712,6 @@ "message": "Bearbeiten", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, "backupMessage": { "message": "Wähle eine Datei aus oder ziehe die Datei auf diese Seite. (Drag and Drop)", "description": "Message for backup" @@ -729,7 +725,7 @@ "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "description": { - "message": "Gestalten Sie das Web mit Stylus, einem Manager für Benutzer-Styles, um. Stylus lässt Sie ganz einfach Themes und Skins für viele beliebte Webseiten installieren.", + "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" } -} +} \ No newline at end of file diff --git a/_locales/el/messages.json b/_locales/el/messages.json index ac685072..a6e0de1a 100644 --- a/_locales/el/messages.json +++ b/_locales/el/messages.json @@ -3,18 +3,6 @@ "message": "Τα πάντα", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, "cm_tabSize": { "message": "Μέγεθος καρτέλας", "description": "Label for the text box controlling tab size option for the style editor." @@ -35,42 +23,22 @@ "message": "Έλεγχος για ενημερώσεις", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, "updateAllCheckSucceededNoUpdate": { "message": "Όλα τα στυλ είναι ενημερωμένα.", "description": "Text that displays when an update all check completed and no updates are available" }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Βοήθεια", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Αναζήτηση περισσότερων στυλ για αυτή την ιστοσελίδα.", + "message": "Αναζήτηση περισσότερων στυλ για αυτή την ιστοσελίδα", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Εγκατεστημένα Στυλ", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Ενεργοποιημένη", "description": "Label for the enabled state of styles" @@ -87,10 +55,6 @@ "message": "Αποθήκευση", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, "writeStyleForURL": { "message": "αυτή την διεύθυνση URL", "description": "Text for link in toolbar pop-up to write a new style for the current URL" @@ -112,14 +76,6 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Απενεργοποίηση", "description": "Label for the button to disable a style" @@ -144,12 +100,8 @@ "message": "Έχετε κάνει αλλαγές σε αυτό το ύφος χωρίς αποθήκευση.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Αποτυχία ενημέρωσης - απρόσιτος διακομιστής.", + "message": "Αποτυχία ενημέρωσης: απρόσιτος διακομιστής.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -164,12 +116,6 @@ "message": "Είστε σίγουροι ότι θέλετε να διαγράψετε αυτό το στυλ;", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, "styleBadRegexp": { "message": "Το Regexp δεν είναι έγκυρο.", "description": "Validation message for a bad regexp in a style" @@ -217,17 +163,9 @@ "message": "Διευθύνσεις URL που αρχίζουν με", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, "popupStylesFirst": { "message": "Στυλ λίστας πριν των εντολών στο μενού του κουμπιού γραμμής εργαλείων", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Ενότητες σας επιτρέπουν να ορίσετε διαφορετικά κομμάτια του κώδικα για να εφαρμόζονται σε διαφορετικά σύνολα των διευθύνσεων URL στο ίδιο στυλ. Για παράδειγμα, ένα ενιαίο ύφος θα μπορούσε να αλλάξει την αρχική σελίδα ενός ιστότοπου με έναν τρόπο, ενώ αλλάζει το υπόλοιπο μιας τοποθεσίας ένας άλλος τρόπος.", @@ -245,10 +183,6 @@ "message": "Αφαίρεση", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, "manageTitle": { "message": "Κομψή", "description": "Title for the manage page" @@ -257,10 +191,6 @@ "message": "Γράψτε νέο στυλ για:", "description": "Label for toolbar pop-up that precedes the links to write a new style" }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Ισχύει για", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +200,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Αποτυχία ενημέρωσης - ο διακομιστής ανταποκρίθηκε με κωδικό $code$.", + "message": "Αποτυχία ενημέρωσης: ο διακομιστής ανταποκρίθηκε με κωδικό $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,14 +216,6 @@ "message": "Εγκατάσταση ενημέρωσης", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, - "stylusUnavailableForURL": { - "message": "To Stylus δεν λειτουργεί σε σελίδες όπως αυτή.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" - }, "sectionRemove": { "message": "Αφαίρεση ενότητας", "description": "Label for the button to remove a section" @@ -302,10 +224,6 @@ "message": "Απενεργοποιηση ολων των στυλ", "description": "Label for the checkbox that turns all enabled styles off." }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Η ενημέρωση ολοκληρώθηκε.", "description": "Text that displays when an update completed" @@ -330,34 +248,18 @@ "message": "Επεξεργασία Στυλ", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "stylusUnavailableForURL": { + "message": "To Stylus δεν λειτουργεί σε σελίδες όπως αυτή.", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" }, "addStyleTitle": { "message": "Προσθήκη στυλ", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, "dbError": { "message": "Παρουσιάστηκε σφάλμα χρησιμοποιώντας την κομψή βάση δεδομένων. Θα θέλατε να επισκεφθείτε μια ιστοσελίδα με πιθανές λύσεις;", "description": "Prompt when a DB error is encountered" }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, "editGotoLine": { "message": "Μετάβαση στη γραμμή (ή line:col)", "description": "Go to line or line:column on Ctrl-G in style code editor" @@ -366,30 +268,10 @@ "message": "Έλεγχος όλων των στυλ για ενημερώσεις", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, "cm_indentWithTabs": { "message": "Χρήση καρτελών με έξυπνη εσοχή", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Διαγραφή", "description": "Label for the button to delete a style" @@ -406,16 +288,8 @@ "message": "Επεξεργασία", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Επαναπροσδιορίση του διαδίκτυου με το Stylus, έναν διαχειριστή στυλ. Το Stylus σας επιτρέπει να εγκαταστήσετε εύκολα themes και skins για πολλές δημοφιλείς ιστοσελίδες.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b4ed254c..633fb311 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -91,6 +91,10 @@ "message": "Checking...", "description": "Text to display when checking a style for an update" }, + "cm_autocompleteOnTyping": { + "message": "Autocomplete on typing", + "description": "Label for the checkbox in the style editor." + }, "cm_indentWithTabs": { "message": "Use tabs with smart indentation", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." @@ -127,6 +131,10 @@ "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" + }, "genericDisabledLabel": { "message": "Disabled", "description": "Used in various lists/options to indicate that something is disabled" @@ -230,7 +238,7 @@ "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" }, "findStylesForSite": { - "message": "Find more styles for this site.", + "message": "Find more styles for this site", "description": "Text for a link that gets a list of styles for the current site" }, "helpAlt": { @@ -498,6 +506,10 @@ "message": "Beautify", "description": "Label for the CSS-beautifier button on the edit style page" }, + "styleBeautifyIndentConditional": { + "message": "Indent @media, @supports", + "description": "CSS-beautifier option" + }, "styleCancelEditLabel": { "message": "Back to manage", "description": "Label for cancel button for style editing" @@ -510,6 +522,10 @@ "message": "Enabled", "description": "Label for the enabled state of styles" }, + "styleEnabledToggleHint": { + "message": "Press Alt-Enter to toggle enabled/disabled state and save the style", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "styleInstall": { "message": "Install '$stylename$' into Stylus?", "description": "Confirmation when installing a style", @@ -573,7 +589,7 @@ "description": "Button label" }, "undoGlobal": { - "message": "Undo (global)", + "message": "Undo in all sections", "description": "CSS-beautify global Undo button label" }, "unreachableContentScript": { @@ -585,7 +601,7 @@ "description": "Note in the toolbar popup for file:// URLs" }, "updateCheckFailBadResponseCode": { - "message": "Update failed - server responded with code $code$.", + "message": "Update failed: server responded with code $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -594,7 +610,7 @@ } }, "updateCheckFailServerUnreachable": { - "message": "Update failed - server unreachable.", + "message": "Update failed: server unreachable.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "updateCheckSkippedLocallyEdited": { @@ -665,6 +681,15 @@ "optionsUpdateImportNote": { "message": "When importing style backups from old version or from Stylish, do a one-time check for updates manually in the styles manager to ensure all styles are updated." }, + "optionsCustomizeIcon": { + "message": "Toolbar icon" + }, + "optionsIconLight": { + "message": "Light browser themes" + }, + "optionsIconDark": { + "message": "Dark browser themes" + }, "optionsCustomizeBadge": { "message": "Badge on the toolbar icon" }, diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 0bf45dd6..e925e441 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -19,6 +19,10 @@ "message": "Buscar e instalar automáticamente todas las actualizaciones disponibles de estilos de usuario (en horas)", "description": "" }, + "styleEnabledToggleHint": { + "message": "Pulse Alt-Intro para accionar el estado habilitado/deshabilitado y guardar el estilo", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "Exportar", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -40,11 +44,11 @@ "description": "Label for the text box controlling tab size option for the style editor." }, "enableStyleLabel": { - "message": "Habilitar", + "message": "Activar", "description": "Label for the button to enable a style" }, "styleMissingName": { - "message": "Introduzca un nombre.", + "message": "Introduzca un nombre", "description": "Error displayed when user saves without providing a name" }, "genericHistoryLabel": { @@ -52,7 +56,7 @@ "description": "Used in various places to show a history log of something" }, "shortcutsNote": { - "message": "Define atajos de teclado", + "message": "Defina atajos de teclado", "description": "" }, "appliesDomainOption": { @@ -68,7 +72,11 @@ "description": "Tooltip in the popup for styles that were not applied at all" }, "styleRegexpInvalidExplanation": { - "message": "Algunas reglas 'regexp()' que no se pudieron compilar en absoluto.", + "message": "Algunas reglas «regexp()» que no se pudieron compilar en absoluto.", + "description": "" + }, + "optionsIconDark": { + "message": "Temas de navegador oscuros", "description": "" }, "importAppendLabel": { @@ -92,7 +100,7 @@ "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" }, "dragDropMessage": { - "message": "Suelte su fichero de copia de seguridad en cualquier lugar de esta página para importar.", + "message": "Suelte su fichero de copia de seguridad en cualquier lugar de esta página para importarlo.", "description": "Drag'n'drop message" }, "helpAlt": { @@ -116,7 +124,7 @@ "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "Buscar más estilos para este sitio", + "message": "Encontrar más estilos para este sitio", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { @@ -132,7 +140,7 @@ "description": "Tooltip in the popup for styles that were applied only partially" }, "styleEnabledLabel": { - "message": "Habilitado", + "message": "Activado", "description": "Label for the enabled state of styles" }, "styleToMozillaFormatHelp": { @@ -168,7 +176,7 @@ "description": "'Stop' button in a confirm dialog" }, "writeStyleForURL": { - "message": "Esta URL", + "message": "este URL", "description": "Text for link in toolbar pop-up to write a new style for the current URL" }, "optionsSubheading": { @@ -184,7 +192,7 @@ "description": "Option to make the style apply to the entered string as a regular expression" }, "optionsAdvancedExposeIframesNote": { - "message": "Habilita la escritura de CSS específico-para-iframe como 'html[stylus-iframe] h1 { display:none }'", + "message": "Activa la escritura de CSS específico-para-iframe como «html[stylus-iframe] h1 { display:none }»", "description": "" }, "importReportLegendUpdatedCode": { @@ -192,7 +200,7 @@ "description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles" }, "styleInstall": { - "message": "¿Instalar \"$stylename$\" en Stylus?", + "message": "¿Quiere instalar «$stylename$» en Stylus?", "description": "Confirmation when installing a style", "placeholders": { "stylename": { @@ -201,7 +209,7 @@ } }, "optionsBadgeDisabled": { - "message": "Color de fondo cuando está deshabilitado", + "message": "Color de fondo cuando está desactivado", "description": "" }, "optionsCheck": { @@ -229,15 +237,15 @@ "description": "Label for the button to apply all detected updates" }, "unreachableFileHint": { - "message": "Stylus puede acceder a URLs file:// sólo si habilita la casilla correspondiente para la extensión Stylus en la página chrome://extensions.", + "message": "Stylus puede acceder a URL file:// solo si activa la casilla correspondiente para la extensión Stylus en la página chrome://extensions.", "description": "Note in the toolbar popup for file:// URLs" }, "disableStyleLabel": { - "message": "Deshabilitar", + "message": "Desactivar", "description": "Label for the button to disable a style" }, "prefShowBadge": { - "message": "Número de estilos activos para el sitio actual", + "message": "Número de estilos activos en el sitio actual", "description": "Label for the checkbox controlling toolbar badge text." }, "manageFavicons": { @@ -269,7 +277,7 @@ "description": "Go to shortcut configuration" }, "updateCheckFailServerUnreachable": { - "message": "Error de actualización - No se pudo alcanzar el servidor.", + "message": "Error de actualización: no se pudo contactar con el servidor.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -281,7 +289,7 @@ "description": "Label for the button to apply all detected updates" }, "optionsReset": { - "message": "Reinicializar las opciones a sus valores predeterminados.", + "message": "Restablecer las opciones a sus valores predeterminados.", "description": "" }, "optionsCustomizeUpdate": { @@ -289,7 +297,7 @@ "description": "" }, "deleteStyleConfirm": { - "message": "¿Está seguro de que quiere eliminar este estilo?", + "message": "¿Confirma que quiere eliminar este estilo?", "description": "Confirmation before deleting a style" }, "optionsCustomizePopup": { @@ -297,7 +305,7 @@ "description": "" }, "styleBadRegexp": { - "message": "La expresión regular (regexp) introducida no es válida", + "message": "La expresión regular proporcionada no es válida.", "description": "Validation message for a bad regexp in a style" }, "optionsHeading": { @@ -313,12 +321,16 @@ } } }, + "optionsIconLight": { + "message": "Temas de navegador claros", + "description": "" + }, "openStylesManager": { "message": "Abrir administrador de estilos", "description": "Label for the style maanger opener in the browser action context menu." }, "styleUpdate": { - "message": "¿Está seguro de que quiere actualizar '$stylename$'?", + "message": "¿Confirma que quiere actualizar «$stylename$»?", "description": "Confirmation when updating a style", "placeholders": { "stylename": { @@ -348,7 +360,7 @@ "description": "Text that displays when an update check completed and no update is available" }, "appliesUrlPrefixOption": { - "message": "URLs que empiezan por", + "message": "URLs que empiezan con", "description": "Option to make the style apply to the entered string as a URL prefix" }, "cm_matchHighlightToken": { @@ -356,7 +368,7 @@ "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected" }, "searchRegexp": { - "message": "Use la sintaxis /re/ para búsquedas con regexp", + "message": "Use la sintaxis /re/ para búsquedas con expresiones regulares", "description": "Label after the search input field in the editor shown on Ctrl-F" }, "updateCheckManualUpdateHint": { @@ -371,8 +383,12 @@ "message": "Descarta contenidos del estilo actual y los sobrescribe con el estilo importado", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "Doble-clic para maximizar/restaurar la altura", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { - "message": "Estilos antes de los comandos", + "message": "Estilos antes que las órdenes", "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { @@ -399,10 +415,6 @@ "message": "Estilo en formato Mozilla", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "Escribir estilo para:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -432,7 +444,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Error de actualización - El servidor ha respondido con el código $code$.", + "message": "Error de actualización: el servidor ha respondido con el código $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -468,6 +480,10 @@ "message": "Este estilo utiliza expresiones regulares (regexps) parcialmente coincidentes violando así la especificación @document de CSS4 que requiere una coincidencia completa de URL. Las secciones CSS afectadas no se aplicaron a la página. Probablemente este estilo fue creado con Stylish-para-Chrome, que evalúa de modo incorrecto las reglas 'regexp()' desde la primera versión (un fallo conocido).", "description": "" }, + "styleBeautifyIndentConditional": { + "message": "Sangrar @media, @supports", + "description": "CSS-beautifier option" + }, "unreachableContentScript": { "message": "No se pudo comunicar con la página. Pruebe a recargar la pestaña.", "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" @@ -477,17 +493,21 @@ "description": "Label for the button to remove a section" }, "disableAllStyles": { - "message": "Deshabilitar todos los estlos", + "message": "Desactivar todos los estilos", "description": "Label for the checkbox that turns all enabled styles off." }, "updateCheckSkippedMaybeLocallyEdited": { - "message": "Este estilo podría haber sido editado localmente.", + "message": "Este estilo podría haberse editado localmente.", "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications" }, "undoGlobal": { - "message": "Deshacer (global)", + "message": "Deshacer en todas las secciones", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "Icono de barra de herramientas", + "description": "" + }, "updateCompleted": { "message": "Actualización completada.", "description": "Text that displays when an update completed" @@ -513,7 +533,7 @@ "description": "Text that displays when an update check skipped updating the style to avoid losing local modifications" }, "optionsResetButton": { - "message": "Opciones de reinicialización", + "message": "Restablecer opciones", "description": "" }, "sectionCode": { @@ -521,7 +541,7 @@ "description": "Label for the code for a section" }, "optionsAdvancedContextDelete": { - "message": "Añadir 'Borrar' al menú contextual del editor", + "message": "Añadir 'Eliminar' al menú contextual del editor", "description": "" }, "importReportLegendUpdatedBoth": { @@ -533,11 +553,11 @@ "description": "Label for the checkbox controlling smart indentation option for the style editor." }, "styleRegexpTestButton": { - "message": "Test de regexp", + "message": "Prueba de regexp", "description": "RegExp test button label in the editor shown when applies-to list has a regexp value" }, "appliesHelp": { - "message": "Utilice los controles 'Se aplica a' para limitar las URLs a las que se aplica el código de esta sección.", + "message": "Utilice los controles 'Se aplica a' para limitar a qué URLs se aplica el código de esta sección.", "description": "Help text for 'applies to' section" }, "editStyleHeading": { @@ -545,17 +565,21 @@ "description": "Title of the page for editing styles" }, "editDeleteText": { - "message": "Borrar", + "message": "Eliminar", "description": "Label for the context menu item in the editor to delete selected text" }, + "cm_autocompleteOnTyping": { + "message": "Autocompletar al escribir", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "Como medida de precaución, el navegador prohíbe que las extensiones afecten a sus páginas internas (como chrome://version o about:addons) además de las páginas internas de otras extensiones. Cada navegador además restringe el acceso a su propia galería de extensiones (como Chrome Web Store o AMO).", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, "cm_matchHighlightSelection": { "message": "Sólo selección", "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "updateAllCheckSucceededSomeEdited": { "message": "No se buscaron actualizaciones de algunos estilos actualizables para evitar la pérdida de posibles ediciones locales. Se puede forzar la búsqueda de actualizaciones mediante comprobación individual, o ejecutando otra búsqueda para todos los estilos (las ediciones locales se sobrescribirán).", "description": "Text that displays when an update all check completed and no updates are available" @@ -601,7 +625,7 @@ "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "styleRegexpTestInvalid": { - "message": "Regexp no válidas omitidas", + "message": "Expresiones regulares no válidas omitidas", "description": "RegExp test report: label for the invalid expressions" }, "replaceAll": { @@ -648,10 +672,6 @@ "message": "Anchura del diálogo emergente (en píxeles)", "description": "" }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, "undo": { "message": "Deshacer", "description": "Button label" @@ -661,7 +681,7 @@ "description": "Label for the drop-down list controlling the keymap for the style editor." }, "manageNewUI": { - "message": "Nuevo diseño de interfaz de administración", + "message": "Nuevo diseño de interfaz de gestión", "description": "Label for the checkbox that toggles the new UI on manage page" }, "importReportUndoneTitle": { @@ -669,7 +689,7 @@ "description": "Title of the message box shown after undoing the import of styles" }, "genericDisabledLabel": { - "message": "Deshabilitado", + "message": "Desactivado", "description": "Used in various lists/options to indicate that something is disabled" }, "cm_indentWithTabs": { @@ -693,11 +713,11 @@ "description": "" }, "addStyleLabel": { - "message": "Escribir nuevo estilo", + "message": "Escribir un nuevo estilo", "description": "Label for the button to go to the add style page" }, "optionsUpdateIntervalNote": { - "message": "Para deshabilitar las búsquedas automáticas de actualizaciones, establezca el intervalo a 0", + "message": "Para desactivar las búsquedas automáticas de actualizaciones, establezca el intervalo a 0", "description": "" }, "backupButtons": { @@ -705,7 +725,7 @@ "description": "Heading for backup" }, "manageOnlyEnabled": { - "message": "Sólo estilos habilitados", + "message": "Sólo estilos activos", "description": "Checkbox to show only enabled styles" }, "editStyleLabel": { @@ -713,11 +733,11 @@ "description": "Label for the button to go to the edit style page" }, "cm_theme": { - "message": "Tema", + "message": "Temas", "description": "Label for the style editor's CSS theme." }, "backupMessage": { - "message": "Seleccione un fichero o arrastre y suéltelo en esta página.", + "message": "Seleccione un fichero o arrástrelo y suéltelo en esta página.", "description": "Message for backup" }, "importReportUndone": { @@ -725,11 +745,11 @@ "description": "Text after the number of styles reverted in the message box shown after undoing the import of styles" }, "helpKeyMapCommand": { - "message": "Escriba un nombre de comando", + "message": "Nombre del comando", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "description": { - "message": "Modifique el estilo de la Web con Stylus, un administrador de estilos de usuario. Stylus le permite instalar fácilmente temas y coberturas para muchos sitios populares.", + "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" } -} +} \ No newline at end of file diff --git a/_locales/fi/messages.json b/_locales/fi/messages.json index 9d7e0ea8..3a751e01 100644 --- a/_locales/fi/messages.json +++ b/_locales/fi/messages.json @@ -3,22 +3,6 @@ "message": "Kaikki", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "Aktivoi", "description": "Label for the button to enable a style" @@ -35,42 +19,22 @@ "message": "Hae päivityksiä", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, "updateAllCheckSucceededNoUpdate": { "message": "All styles are up to date.", "description": "Text that displays when an update all check completed and no updates are available" }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Apu", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Hae lisää tyylejä tälle sivustolle.", + "message": "Hae lisää tyylejä tälle sivustolle", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Asennetut Tyylit", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Aktivoitu", "description": "Label for the enabled state of styles" @@ -87,14 +51,6 @@ "message": "Tallenna", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "Lisää", "description": "Label for the button to add an 'applies' entry" @@ -112,14 +68,6 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Poista Käytöstä", "description": "Label for the button to disable a style" @@ -128,14 +76,6 @@ "message": "Show number of styles active for the current site on the toolbar button", "description": "Label for the checkbox controlling toolbar badge text." }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "Takaisin hallintapaneeliin", "description": "Label for cancel button for style editing" @@ -144,40 +84,18 @@ "message": "Olet tehnyt muutoksia tähän tyyliin tallentamatta.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Päivitys epäonnistui - ei voitu yhdistää palvelimeen.", + "message": "Päivitys epäonnistui: ei voitu yhdistää palvelimeen.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "Oletko varma että haluat poistaa tämän tyylin?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, "styleBadRegexp": { "message": "Regexp ei kelpaa.", "description": "Validation message for a bad regexp in a style" }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "Kooskee: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -187,15 +105,6 @@ } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { "message": "Osiot", "description": "Title for the style sections section" @@ -217,17 +126,9 @@ "message": "URL osoitteet jotka alkavat", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, "popupStylesFirst": { "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Osiot antavat sinun tarkentaa koodin eri osia niin että ne koskevat eri URL osoitteita samassa tyylissä. Esimerkiksi, yksi tyyli voi muokata kotisivua yhdellä tavalla kun se muokkaa koko muuta sivustoa toisella tavalla.", @@ -245,22 +146,10 @@ "message": "Poista", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, "manageTitle": { "message": "Tyylikäs", "description": "Title for the manage page" }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Koskee", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +159,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Päivitys epäonnistui - palvelin vastasi koodilla $code$.", + "message": "Päivitys epäonnistui: palvelin vastasi koodilla $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +175,10 @@ "message": "Asenna päivitys", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "Poista osio", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Päivitys suoritettu.", "description": "Text that displays when an update completed" @@ -314,10 +191,6 @@ "message": "Koodi", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "Käytä 'Koskee' kontrolleja rajoittaaksesi mitä URL osoitteisiin tämä osio koodista koskee.", "description": "Help text for 'applies to' section" @@ -326,66 +199,14 @@ "message": "Muokkaa Tyyliä", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "addStyleTitle": { "message": "Lisää Tyyli", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "Tarkista kaikki tyylit päivityksien varalta", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Poista", "description": "Label for the button to delete a style" @@ -394,24 +215,12 @@ "message": "Uusi Tyyli", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "Muokkaa", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Uudelleen stailaa netti Stylusillä, käyttäjän tyyli hallintapaneelilla. Stylus antaa sinun helposti asentaa teemoja ja skinejä palvelluille kuten Google, Facebook, YouTube, Orkut, ja monelle, monelle muulle sivustolle.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index b399a0d5..892244cf 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -11,10 +11,6 @@ "message": "Exportez", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, "cm_tabSize": { "message": "Taille des tabulations", "description": "Label for the text box controlling tab size option for the style editor." @@ -35,26 +31,14 @@ "message": "Rechercher des mises à jour", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, "updateAllCheckSucceededNoUpdate": { "message": "All styles are up to date.", "description": "Text that displays when an update all check completed and no updates are available" }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Aide", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, "confirmYes": { "message": "Oui", "description": "'Yes' button in a confirm dialog" @@ -67,10 +51,6 @@ "message": "Styles installés", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Activé", "description": "Label for the enabled state of styles" @@ -87,10 +67,6 @@ "message": "Enregistrer", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, "writeStyleForURL": { "message": "cette URL", "description": "Text for link in toolbar pop-up to write a new style for the current URL" @@ -112,14 +88,6 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Désactiver", "description": "Label for the button to disable a style" @@ -128,10 +96,6 @@ "message": "Afficher le nombre de styles actifs pour le site actuel sur le boutton Stylus", "description": "Label for the checkbox controlling toolbar badge text." }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, "cm_lineWrapping": { "message": "Retour à la ligne automatique", "description": "Label for the checkbox controlling word wrap option for the style editor." @@ -144,40 +108,22 @@ "message": "Vous avez apporté des modifications à ce style sans les enregistrer.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Échec de la mise à jour. Le serveur est inaccessible.", + "message": "Échec de la mise à jour: le serveur est inaccessible.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { "message": "Filtres", "description": "Label for filters container" }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "Voulez-vous vraiment supprimer ce style ?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, "styleBadRegexp": { "message": "Le Regexp est éronné", "description": "Validation message for a bad regexp in a style" }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "S'applique à : $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -196,10 +142,6 @@ } } }, - "styleSectionsTitle": { - "message": "Sections", - "description": "Title for the style sections section" - }, "editStyleTitle": { "message": "Modifier le style $stylename$", "description": "Title of the page for editing styles", @@ -217,17 +159,9 @@ "message": "URL commençant par", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, "popupStylesFirst": { "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Les sections vous permettent de définir différentes portions de code correspondant à un même style que vous pouvez appliquer à des ensembles d'URL distincts. Par exemple, un même style appliqué à la page d'accueil peut modifier celle-ci d'une certaine manière et modifier le reste du site Web d'une autre manière.", @@ -245,22 +179,6 @@ "message": "Supprimer", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "S'applique à", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +188,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Échec de la mise à jour. Le serveur a renvoyé le $code$ $code$.", + "message": "Échec de la mise à jour: le serveur a renvoyé le code $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,14 +204,6 @@ "message": "Installer la mise à jour", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, - "stylusUnavailableForURL": { - "message": "Stylus ne fonctionne pas sur les pages de ce genre", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" - }, "sectionRemove": { "message": "Supprimer la section", "description": "Label for the button to remove a section" @@ -302,10 +212,6 @@ "message": "Désactiver tous les styles", "description": "Label for the checkbox that turns all enabled styles off." }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Mise à jour terminée.", "description": "Text that displays when an update completed" @@ -314,14 +220,6 @@ "message": "Vérification en cours…", "description": "Text to display when checking a style for an update" }, - "sectionCode": { - "message": "Code", - "description": "Label for the code for a section" - }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "Utilisez l'option \"S'applique à\" pour préciser les URL auxquelles le code de cette section s'applique.", "description": "Help text for 'applies to' section" @@ -330,66 +228,26 @@ "message": "Modifier le style", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "stylusUnavailableForURL": { + "message": "Stylus ne fonctionne pas sur les pages de ce genre", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" }, "addStyleTitle": { "message": "Ajouter un style", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, "dbError": { "message": "Une erreur s'est produite dans la base de donnée de Stylus. Voulez-vous visiter une page web avec les solutions possibles ?", "description": "Prompt when a DB error is encountered" }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "Rechercher des mises à jour pour tous les styles", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, "confirmNo": { "message": "Non", "description": "'No' button in a confirm dialog" }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Supprimer", "description": "Label for the button to delete a style" @@ -398,18 +256,10 @@ "message": "Créer un nouveau style", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "Modifier", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, "helpKeyMapCommand": { "message": "Tapez un nom de commande", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" @@ -418,4 +268,4 @@ "message": "Changez l'apparence du Web grâce à Stylus, un outil de gestion des styles utilisateur. Stylus vous permet d'installer facilement des thèmes et des habillages pour Google, Facebook, YouTube, orkut et bien d'autres sites encore.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/it/messages.json b/_locales/it/messages.json index 3a485c49..e1a6f6a4 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -3,22 +3,6 @@ "message": "Tutto", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "Attiva", "description": "Label for the button to enable a style" @@ -35,42 +19,18 @@ "message": "Verifica la presenza di aggiornamenti", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Guida", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Trova più stili per questo sito.", + "message": "Trova più stili per questo sito", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Stili installati", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Attivato", "description": "Label for the enabled state of styles" @@ -87,14 +47,6 @@ "message": "Salva", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "Aggiungi", "description": "Label for the button to add an 'applies' entry" @@ -112,30 +64,10 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Disattiva", "description": "Label for the button to disable a style" }, - "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "Torna a gestione", "description": "Label for cancel button for style editing" @@ -144,40 +76,14 @@ "message": "Hai apportato modifiche a questo stile senza salvare.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Aggiornamento non riuscito - server non raggiungibile.", + "message": "Aggiornamento non riuscito: server non raggiungibile.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "Vuoi eliminare questo stile?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "Applica a: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -187,15 +93,6 @@ } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { "message": "Sezioni", "description": "Title for the style sections section" @@ -217,18 +114,6 @@ "message": "URL che iniziano con", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, "sectionHelp": { "message": "Le sezioni consentono di definire diverse parti di codice da applicare a diversi insiemi di URL dello stesso stile. Ad esempio, un unico stile potrebbe modificare la home page di un sito diversamente da come modificherebbe il resto del sito.", "description": "Help text for sections" @@ -245,22 +130,6 @@ "message": "Rimuovi", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Vale per", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +139,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Aggiornamento non riuscito - il server ha risposto con il codice $code$.", + "message": "Aggiornamento non riuscito: il server ha risposto con il codice $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +155,10 @@ "message": "Installa aggiornamento", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "Rimuovi sezione", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Aggiornamento completato.", "description": "Text that displays when an update completed" @@ -314,10 +171,6 @@ "message": "Codice", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "Utilizza i controlli \"Applica a\" per limitare gli URL a cui viene applicato il codice in questa sezione.", "description": "Help text for 'applies to' section" @@ -326,66 +179,14 @@ "message": "Modifica di stili", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "addStyleTitle": { "message": "Aggiunta di stili", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "Verifica la disponibilità di aggiornamenti per tutti gli stili", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Elimina", "description": "Label for the button to delete a style" @@ -394,24 +195,12 @@ "message": "Scrivi nuovo stile", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "Modifica", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Modifica lo stile del Web con Stylus, un gestore di stili utente. Stylus consente di installare facilmente temi e skin per Google, Facebook, YouTube, Orkut e moltissimi altri siti.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index db2a6210..e01bad4a 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -4,19 +4,39 @@ "description": "Text displayed for styles that apply to all sites" }, "defaultTheme": { - "message": "default", + "message": "デフォルト", "description": "Default CodeMirror CSS theme option on the edit style page" }, + "styleRegexpTestTitle": { + "message": "一致する開いているタブのリスト(URLをクリックしてタブにフォーカスします)", + "description": "RegExp test report: title of the report" + }, + "bckpInstStyles": { + "message": "スタイルをエクスポート", + "description": "" + }, + "optionsUpdateInterval": { + "message": "利用可能なすべてのユーザースタイルの更新を自動的にチェックしてインストールします(時間単位)", + "description": "" + }, + "styleEnabledToggleHint": { + "message": "Alt-Enterを押して、有効/無効の状態を切り替え、スタイルを保存します", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { - "message": "Export", + "message": "エクスポート", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" + "optionsBadgeNormal": { + "message": "背景色", + "description": "" + }, + "updateCheckHistory": { + "message": "更新チェックの履歴", + "description": "" }, "cm_tabSize": { - "message": "Tab size", + "message": "タブサイズ", "description": "Label for the text box controlling tab size option for the style editor." }, "enableStyleLabel": { @@ -27,6 +47,14 @@ "message": "名前を入力してください。", "description": "Error displayed when user saves without providing a name" }, + "genericHistoryLabel": { + "message": "履歴", + "description": "Used in various places to show a history log of something" + }, + "shortcutsNote": { + "message": "キーボードショートカットを定義する", + "description": "" + }, "appliesDomainOption": { "message": "ドメイン上の URL", "description": "Option to make the style apply to the entered string as a domain" @@ -36,27 +64,39 @@ "description": "Label for the button to check a single style for an update" }, "importAppendLabel": { - "message": "Append to style", + "message": "スタイルに追加", "description": "Label for the button to import a style and append to the existing sections" }, + "optionsOpenManager": { + "message": "スタイルを管理", + "description": "" + }, "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", + "message": "更新は見つかりませんでした。", "description": "Text that displays when an update all check completed and no updates are available" }, "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", + "message": "Mozilla形式のコードを貼り付ける", "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" }, + "dragDropMessage": { + "message": "このページの任意の場所にバックアップファイルをドロップしてインポートします。", + "description": "Drag'n'drop message" + }, "helpAlt": { "message": "ヘルプ", "description": "Alternate text for help buttons" }, "search": { - "message": "Search", + "message": "検索", "description": "Label before the search input field in the editor shown on Ctrl-F" }, + "manageFaviconsGray": { + "message": "グレーアウト", + "description": "Label for the checkbox that toggles grayed out mode of applies-to favicons in the new UI on manage page" + }, "confirmYes": { - "message": "Yes", + "message": "はい", "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { @@ -67,10 +107,6 @@ "message": "インストール済みのスタイル", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "有効", "description": "Label for the enabled state of styles" @@ -87,13 +123,25 @@ "message": "保存", "description": "Label for save button for style editing" }, + "confirmDelete": { + "message": "削除", + "description": "" + }, + "confirmCancel": { + "message": "キャンセル", + "description": "" + }, + "retrieveBckp": { + "message": "スタイルをインポート", + "description": "" + }, "confirmStop": { - "message": "Stop", + "message": "中止", "description": "'Stop' button in a confirm dialog" }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" + "optionsSubheading": { + "message": "その他のオプション", + "description": "Subheading for options section on manage page." }, "appliesAdd": { "message": "追加", @@ -103,6 +151,10 @@ "message": "正規表現に一致する URL", "description": "Option to make the style apply to the entered string as a regular expression" }, + "importReportLegendUpdatedCode": { + "message": "コードを更新", + "description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles" + }, "styleInstall": { "message": "「$stylename$」を Stylus にインストールしますか?", "description": "Confirmation when installing a style", @@ -112,28 +164,52 @@ } } }, + "optionsBadgeDisabled": { + "message": "無効にしたときの背景色", + "description": "" + }, + "optionsCheck": { + "message": "スタイルを更新", + "description": "" + }, "manageText": { - "message": "Get styles on userstyles.org | Get help", + "message": "userstyles.orgでスタイルを取得 | ヘルプ", "description": "Help text on the manage page" }, + "manageOnlyLocal": { + "message": "ローカルに作成されたスタイルのみ", + "description": "Checkbox to show only locally created styles i.e. non-updatable" + }, "searchStyles": { - "message": "Search contents", + "message": "コンテンツの検索", "description": "Label for the search filter textbox on the Manage styles page" }, + "optionsUpdateImportNote": { + "message": "旧バージョンまたはStylishからスタイルバックアップをインポートする場合は、スタイルマネージャで手動で更新を一度チェックして、すべてのスタイルが確実に更新されるようにします。", + "description": "" + }, + "unreachableFileHint": { + "message": "Stylusはchrome://extensionsページのStylus拡張機能に対応するチェックボックスを有効にした場合にのみ、file:// URLにアクセスできます。", + "description": "Note in the toolbar popup for file:// URLs" + }, "disableStyleLabel": { "message": "無効化", "description": "Label for the button to disable a style" }, "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", + "message": "現在のサイトでアクティブなスタイルの数を表示", "description": "Label for the checkbox controlling toolbar badge text." }, + "manageFavicons": { + "message": "適用先欄のファビコン", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, "menuShowBadge": { - "message": "Show active style count", + "message": "アクティブなスタイルの数を表示する", "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." }, "cm_lineWrapping": { - "message": "Word wrap", + "message": "ワードラップ", "description": "Label for the checkbox controlling word wrap option for the style editor." }, "styleCancelEditLabel": { @@ -145,37 +221,47 @@ "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, "importLabel": { - "message": "Import", + "message": "インポート", "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" }, + "shortcuts": { + "message": "ショートカット", + "description": "Go to shortcut configuration" + }, "updateCheckFailServerUnreachable": { - "message": "更新に失敗しました - サーバーに到達できません。", + "message": "更新に失敗しました: サーバーに到達できません。", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { - "message": "Filters", + "message": "フィルター", "description": "Label for filters container" }, "applyAllUpdates": { - "message": "Apply all updates", + "message": "すべての更新を適用", "description": "Label for the button to apply all detected updates" }, + "optionsReset": { + "message": "オプションをデフォルト値にリセットする", + "description": "" + }, + "optionsCustomizeUpdate": { + "message": "アップデート", + "description": "" + }, "deleteStyleConfirm": { "message": "このスタイルを削除してもよろしいですか?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" + "optionsCustomizePopup": { + "message": "ポップアップ", + "description": "" }, "styleBadRegexp": { "message": "正規表現は無効です。", "description": "Validation message for a bad regexp in a style" }, "optionsHeading": { - "message": "Options", + "message": "オプション", "description": "Heading for options section on manage page." }, "appliesDisplay": { @@ -187,8 +273,12 @@ } } }, + "openStylesManager": { + "message": "スタイルマネージャーを開く", + "description": "Label for the style maanger opener in the browser action context menu." + }, "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", + "message": "「$stylename$」を更新してもよろしいですか?", "description": "Confirmation when updating a style", "placeholders": { "stylename": { @@ -217,17 +307,29 @@ "message": "次で始まる URL", "description": "Option to make the style apply to the entered string as a URL prefix" }, + "cm_matchHighlightToken": { + "message": "カーソル下の単語", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected" + }, "searchRegexp": { - "message": "Use /re/ syntax for regexp search", + "message": "正規表現検索に /re/ シンタックスを使用する", "description": "Label after the search input field in the editor shown on Ctrl-F" }, + "updateCheckManualUpdateHint": { + "message": "更新を強制すると、すべてのローカル編集が上書きされます。", + "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" + }, + "toggleStyle": { + "message": "スタイルを切り替え", + "description": "Label for the checkbox to enable/disable a style" + }, "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", + "message": "現在のスタイルの内容を破棄し、インポートされたスタイルで上書きする", "description": "Label for the button to import and overwrite current style" }, "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "message": "スタイルをコマンドボタンの前に", + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "セクションを利用すると、1 つのスタイルの中で複数のコードを定義し、それぞれ異なる URL に適用することができます。たとえば、単一のスタイルで、サイトのトップページの表示方法と他のページの表示方法を変えることができます。", @@ -246,31 +348,31 @@ "description": "Label for the button to remove an 'applies' entry" }, "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", + "message": "Mozilla形式のスタイル", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { - "message": "Write style for: ", + "message": "次のスタイルを書く:", "description": "Label for toolbar pop-up that precedes the links to write a new style" }, "replace": { - "message": "Replace", + "message": "置換", "description": "Label before the replace input field in the editor shown on Ctrl-H" }, "appliesLabel": { "message": "適用先", "description": "Label for 'applies to' fields on the edit/add screen" }, + "openOptionsPopup": { + "message": "オプション", + "description": "Go to Options UI" + }, "openManage": { "message": "インストール済みのスタイルを管理", "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "更新に失敗しました - サーバーが応答コード「$code$」を返しています。", + "message": "更新に失敗しました: サーバーが応答コード「$code$」を返しています。", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,20 +388,36 @@ "message": "アップデートをインストール", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" + "optionsCheckUpdate": { + "message": "使用可能なすべての更新を確認してインストールする", + "description": "" + }, + "importReportTitle": { + "message": "スタイルのインポートが終了しました", + "description": "Title of the report shown after importing styles" + }, + "cm_matchHighlight": { + "message": "ハイライト", + "description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor." + }, + "unreachableContentScript": { + "message": "ページと通信できませんでした。タブを再読み込みしてみてください。", + "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" }, "sectionRemove": { "message": "セクションを削除", "description": "Label for the button to remove a section" }, "disableAllStyles": { - "message": "Turn all styles off", + "message": "すべてのスタイルをオフにする", "description": "Label for the checkbox that turns all enabled styles off." }, + "updateCheckSkippedMaybeLocallyEdited": { + "message": "このスタイルはローカルで編集されている可能性があります。", + "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications" + }, "undoGlobal": { - "message": "Undo (global)", + "message": "すべてのセクションで元に戻す", "description": "CSS-beautify global Undo button label" }, "updateCompleted": { @@ -310,12 +428,32 @@ "message": "チェック中...", "description": "Text to display when checking a style for an update" }, + "manageMaxTargets": { + "message": "適用先アイテムの数", + "description": "Label for the numeric input box to limit max number of applies-to targets in the new UI on manage page" + }, + "manageFaviconsHelp": { + "message": "Stylusは外部サービスを使用します https://www.google.com/s2/favicons", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, + "updateCheckSkippedLocallyEdited": { + "message": "このスタイルはローカルで編集されました。", + "description": "Text that displays when an update check skipped updating the style to avoid losing local modifications" + }, + "optionsResetButton": { + "message": "オプションをリセット", + "description": "" + }, "sectionCode": { "message": "コード", "description": "Label for the code for a section" }, + "optionsAdvancedContextDelete": { + "message": "エディタのコンテキストメニューに「削除」を追加", + "description": "" + }, "cm_smartIndent": { - "message": "Use smart indentation", + "message": "スマートインデントを使用する", "description": "Label for the checkbox controlling smart indentation option for the style editor." }, "appliesHelp": { @@ -326,64 +464,120 @@ "message": "スタイルを編集", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "editDeleteText": { + "message": "削除", + "description": "Label for the context menu item in the editor to delete selected text" + }, + "stylusUnavailableForURLdetails": { + "message": "セキュリティ上の予防措置として、ブラウザは拡張機能が他の拡張機能のページと同様に、内蔵のページ(chrome://versionやabout:addonsなど)に影響を及ぼさないようにします。各ブラウザは、独自の拡張機能ギャラリー(ChromeウェブストアやAMOなど)へのアクセスも制限しています。", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "cm_matchHighlightSelection": { + "message": "選択した単語のみ", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" + }, + "updateAllCheckSucceededSomeEdited": { + "message": "可能なローカル編集を失うのを避けるために、更新可能なスタイルがチェックされていませんでした。更新を強制的に行うには、個別にチェックするか、すべてのスタイルに対して別のチェックを実行します(ローカル編集は上書きされます)。", + "description": "Text that displays when an update all check completed and no updates are available" + }, + "stylusUnavailableForURL": { + "message": "このようなページではStylusは動作しません。", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "manageOnlyUpdates": { + "message": "更新または問題の場合のみ", + "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed" }, "addStyleTitle": { "message": "スタイルを追加", "description": "Title of the page for adding styles" }, "importReplaceLabel": { - "message": "Overwrite style", + "message": "スタイルを上書き", "description": "Label for the button to import and overwrite current style" }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" + "optionsAdvanced": { + "message": "上級者向け", + "description": "" }, "importAppendTooltip": { - "message": "Append the imported style to current style", + "message": "インポートされたスタイルを現在のスタイルに追加する", "description": "Tooltip for the button to import a style and append to the existing sections" }, + "editorStylesButton": { + "message": "エディタスタイルを見つける", + "description": "Find styles for the editor" + }, + "optionsOpen": { + "message": "開く", + "description": "" + }, "helpKeyMapHotkey": { - "message": "Press a hotkey", + "message": "ホットキーを押す", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "replaceAll": { - "message": "Replace all", + "message": "すべて置換", "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" }, "editGotoLine": { - "message": "Goto line (or line:col)", + "message": "行(または行:列)に移動", "description": "Go to line or line:column on Ctrl-G in style code editor" }, + "manageOnlyLocalTooltip": { + "message": "(userstyles.orgページからインストールされていないスタイル)", + "description": "Tooltip for the checkbox to show only locally created styles i.e. non-updatable" + }, "checkAllUpdates": { "message": "すべてのスタイルのアップデートをチェック", "description": "Label for the button to check all styles for updates" }, + "openOptionsManage": { + "message": "オプション UI", + "description": "Go to Options UI" + }, "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", + "message": "これらのルールを有効にして CSSLint で見つかった問題:", "description": "Help popup message for the CSSLint issues block on the style edit page" }, + "optionsCustomizeBadge": { + "message": "ツールバーアイコンのバッジ", + "description": "" + }, + "optionsPopupWidth": { + "message": "ポップアップの幅 (ピクセル単位)", + "description": "" + }, "confirmNo": { - "message": "No", + "message": "いいえ", "description": "'No' button in a confirm dialog" }, "undo": { - "message": "Undo", + "message": "元に戻す", "description": "Button label" }, "cm_keyMap": { - "message": "Keymap", + "message": "キーマップ", "description": "Label for the drop-down list controlling the keymap for the style editor." }, + "manageNewUI": { + "message": "新しい管理UIレイアウト", + "description": "Label for the checkbox that toggles the new UI on manage page" + }, + "importReportUndoneTitle": { + "message": "インポートが取り消されました", + "description": "Title of the message box shown after undoing the import of styles" + }, + "genericDisabledLabel": { + "message": "無効化", + "description": "Used in various lists/options to indicate that something is disabled" + }, "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", + "message": "スマートインデント付きのタブを使用する", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." }, "replaceWith": { - "message": "Replace with", + "message": "次に置換", "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." }, "deleteStyleLabel": { @@ -394,8 +588,16 @@ "message": "新しいスタイルを作成", "description": "Label for the button to go to the add style page" }, + "optionsUpdateIntervalNote": { + "message": "ユーザースタイルの自動更新チェックを無効にするには、間隔を0に設定します。", + "description": "" + }, + "backupButtons": { + "message": "バックアップ", + "description": "Heading for backup" + }, "manageOnlyEnabled": { - "message": "Only enabled styles", + "message": "有効なスタイルのみ", "description": "Checkbox to show only enabled styles" }, "editStyleLabel": { @@ -403,15 +605,19 @@ "description": "Label for the button to go to the edit style page" }, "cm_theme": { - "message": "Theme", + "message": "テーマ", "description": "Label for the style editor's CSS theme." }, + "backupMessage": { + "message": "ファイルを選択するか、このページにドラッグ&ドロップします。", + "description": "Message for backup" + }, "helpKeyMapCommand": { - "message": "Type a command name", + "message": "コマンド名を入力", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, "description": { "message": "Stylus はユーザー スタイルを管理するツールで、ウェブのスタイルを変更することができます。Stylus を利用すると、Google、Facebook、YouTube、orkut など、さまざまなサイトに対応したテーマやスキンを簡単にインストールできます。", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/nl/messages.json b/_locales/nl/messages.json index 618b81bd..0f06ade9 100644 --- a/_locales/nl/messages.json +++ b/_locales/nl/messages.json @@ -60,7 +60,7 @@ "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "Meer stijlen zoeken voor deze website.", + "message": "Meer stijlen zoeken voor deze website", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { @@ -149,13 +149,9 @@ "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" }, "updateCheckFailServerUnreachable": { - "message": "Update mislukt - server onbereikbaar.", + "message": "Update mislukt: server onbereikbaar.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, "applyAllUpdates": { "message": "Alle bijwerkingen toepassen", "description": "Label for the button to apply all detected updates" @@ -164,12 +160,6 @@ "message": "Weet u zeker dat u deze stijl wilt verwijderen?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, "styleBadRegexp": { "message": "Regexp is ongeldig.", "description": "Validation message for a bad regexp in a style" @@ -227,7 +217,7 @@ }, "popupStylesFirst": { "message": "Stijlen vóór commando's opsomming in het werkbalkknopmenu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Secties stellen u in staat om andere stukken code toe te passen op andere URL's in dezelfde stijl. Bijv.: een enkele stijl kan de homepage van een site aanpassen op een bepaalde manier, terwijl de rest van de site op een andere manier wordt aangepast.", @@ -249,10 +239,6 @@ "message": "Stijl in Mozilla-opmaak", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "Stijl schrijven voor:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -270,7 +256,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Update mislukt - server gaf code $code$ als antwoord.", + "message": "Update mislukt: server gaf code $code$ als antwoord.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -290,10 +276,6 @@ "message": "Mozilla-opmaak", "description": "Heading for the section with buttons to import/export Mozilla format of the style" }, - "stylusUnavailableForURL": { - "message": "Stylus werkt niet op pagina's als deze.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" - }, "sectionRemove": { "message": "Sectie verwijderen", "description": "Label for the button to remove a section" @@ -314,10 +296,6 @@ "message": "Bezig met controleren...", "description": "Text to display when checking a style for an update" }, - "sectionCode": { - "message": "Code", - "description": "Label for the code for a section" - }, "cm_smartIndent": { "message": "Slimme inspringing gebruiken", "description": "Label for the checkbox controlling smart indentation option for the style editor." @@ -330,9 +308,9 @@ "message": "Stijl bewerken", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "stylusUnavailableForURL": { + "message": "Stylus werkt niet op pagina's als deze.", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" }, "addStyleTitle": { "message": "Stijl toevoegen", @@ -418,4 +396,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" } -} +} \ No newline at end of file diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json index 729f3dd4..ce98da46 100644 --- a/_locales/pt_BR/messages.json +++ b/_locales/pt_BR/messages.json @@ -3,22 +3,6 @@ "message": "Tudo", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "Ativar", "description": "Label for the button to enable a style" @@ -35,42 +19,18 @@ "message": "Verificar atualizações", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Ajuda", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Procurar mais estilos para este site.", + "message": "Procurar mais estilos para este site", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Estilos instalados", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Ativado", "description": "Label for the enabled state of styles" @@ -87,14 +47,6 @@ "message": "Salvar", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "Adicionar", "description": "Label for the button to add an 'applies' entry" @@ -112,14 +64,6 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Desativar", "description": "Label for the button to disable a style" @@ -128,14 +72,6 @@ "message": "Show number of styles active for the current site on the toolbar button", "description": "Label for the checkbox controlling toolbar badge text." }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "Voltar ao gerenciamento", "description": "Label for cancel button for style editing" @@ -144,40 +80,14 @@ "message": "Você fez alterações neste estilo sem salvar.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "A atualização falhou - servidor inacessível.", + "message": "A atualização falhou: servidor inacessível.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "Tem certeza de que deseja excluir este estilo?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "Aplica-se a: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -187,15 +97,6 @@ } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { "message": "Seções", "description": "Title for the style sections section" @@ -217,18 +118,6 @@ "message": "URLs que começam com", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, "sectionHelp": { "message": "As seções permitem definir diferentes partes de código para aplicar a diferentes conjuntos de URLs no mesmo estilo. Por exemplo, um único estilo poderia alterar a página inicial de um site de uma forma, enquanto alteraria o resto do site de outra forma.", "description": "Help text for sections" @@ -245,22 +134,6 @@ "message": "Remover", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Aplica-se a", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +143,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "A atualização falhou - o servidor respondeu com código $code$.", + "message": "A atualização falhou: o servidor respondeu com código $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +159,10 @@ "message": "Instalar atualização", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "Remover seção", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Atualização concluída.", "description": "Text that displays when an update completed" @@ -314,10 +175,6 @@ "message": "Código", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "Use os controles \"Aplica-se a\" para limitar a quais URLs o código desta seção se aplica.", "description": "Help text for 'applies to' section" @@ -326,66 +183,14 @@ "message": "Editar estilo", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "addStyleTitle": { "message": "Adicionar estilo", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "Verificar atualizações para todos os estilos", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Excluir", "description": "Label for the button to delete a style" @@ -394,24 +199,12 @@ "message": "Gravar novo estilo", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "Editar", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Mude o estilo da web com o Stylus, um gerenciador de estilos do usuário. O Stylus permite instalar facilmente temas e skins para Google, Facebook, YouTube, Orkut e muitos, muitos outros sites.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index f2e9081d..157fda85 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -19,6 +19,10 @@ "message": "Автоматически проверять и устанавливать обновления стилей (интервал в часах)", "description": "" }, + "styleEnabledToggleHint": { + "message": "Нажмите Alt-Enter чтобы включить/выключить стиль и сохранить его.", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "Экспорт", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -71,6 +75,10 @@ "message": "Некоторые 'regexp()' выражения не удалось скомпилировать.", "description": "" }, + "optionsIconDark": { + "message": "Тёмный интерфейс браузера", + "description": "" + }, "importAppendLabel": { "message": "Добавить к стилю", "description": "Label for the button to import a style and append to the existing sections" @@ -116,7 +124,7 @@ "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "Найдите другие стили для этого веб-сайта.", + "message": "Найти еще стили для этого веб-сайта", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { @@ -313,6 +321,10 @@ } } }, + "optionsIconLight": { + "message": "Светлый интерфейс браузера", + "description": "" + }, "openStylesManager": { "message": "Менеджер стилей", "description": "Label for the style maanger opener in the browser action context menu." @@ -371,6 +383,10 @@ "message": "Удалить содержимое редактируемого стиля и заменить его на импортируемый стиль", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "Двойной клик = переключить максимальный размер", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { "message": "Список стилей сверху", "description": "Label for the checkbox controlling section order in the popup." @@ -399,10 +415,6 @@ "message": "Стиль в формате Mozilla", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "Создать стиль для:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -432,7 +444,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Ошибка обновления: сервер вернул код \"$code$\".", + "message": "Ошибка обновления: сервер вернул код $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -468,6 +480,10 @@ "message": "В стиле используются regexp-выражения, которые соответствуют лишь части адреса, что нарушает спецификацию CSS4 @document, требующую соответствия всего адреса целиком. Секции стиля с такими regexp не были применены к странице. Вероятно, этот стиль был создан в \"Stylish для Chrome\", который некорректно проверяет regexp (известный баг).", "description": "" }, + "styleBeautifyIndentConditional": { + "message": "Отступ внутри @media, @supports", + "description": "CSS-beautifier option" + }, "unreachableContentScript": { "message": "Нет связи со страницей. Попробуйте перезагрузить вкладку.", "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" @@ -488,6 +504,10 @@ "message": "Отменить (глобально)", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "Значок в панели", + "description": "" + }, "updateCompleted": { "message": "Обновление завершено.", "description": "Text that displays when an update completed" @@ -548,14 +568,18 @@ "message": "Удалить", "description": "Label for the context menu item in the editor to delete selected text" }, + "cm_autocompleteOnTyping": { + "message": "Подсказки при наборе кода", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "В целях обеспечения безопасности браузер запрещает расширениям изменять встроенные страницы (например, chrome://version, about:addons), страницы других расширений и \"магазин\" расширений (Chrome Web Store в chromium-браузерах, AMO в Firefox и т.д.)", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, "cm_matchHighlightSelection": { "message": "Выделенный текст", "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "updateAllCheckSucceededSomeEdited": { "message": "Не все стили были проверены, чтобы не потерять возможные локальные изменения. Форсировать обновление стиля можно индивидуально или запустив перепроверку всех обновляемых стилей (локальные изменения будут утеряны).", "description": "Text that displays when an update all check completed and no updates are available" diff --git a/_locales/sr/messages.json b/_locales/sr/messages.json index 01682cad..b55e7ad9 100644 --- a/_locales/sr/messages.json +++ b/_locales/sr/messages.json @@ -1,421 +1,411 @@ -{ - "appliesToEverything": { - "message": "Све", - "description": "Text displayed for styles that apply to all sites" - }, - "defaultTheme": { - "message": "подразумевано", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Извези", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Проблеми", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Величина картице", - "description": "Label for the text box controlling tab size option for the style editor." - }, - "enableStyleLabel": { - "message": "Омогући", - "description": "Label for the button to enable a style" - }, - "styleMissingName": { - "message": "Унесите назив", - "description": "Error displayed when user saves without providing a name" - }, - "appliesDomainOption": { - "message": "УРЛ адресе на домену", - "description": "Option to make the style apply to the entered string as a domain" - }, - "checkForUpdate": { - "message": "Проверите ажурирање", - "description": "Label for the button to check a single style for an update" - }, - "importAppendLabel": { - "message": "Додај стилу", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "Сви стилови су ажурирани.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Налепи код у Mozilla формату", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, - "helpAlt": { - "message": "Помоћ", - "description": "Alternate text for help buttons" - }, - "search": { - "message": "Претражи", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Да", - "description": "'Yes' button in a confirm dialog" - }, - "findStylesForSite": { - "message": "Пронађи још стилова за овај сајт.", - "description": "Text for a link that gets a list of styles for the current site" - }, - "manageHeading": { - "message": "Инсталирани стилови", - "description": "Heading for the manage page" - }, - "styleBeautify": { - "message": " Улепшај", - "description": "Label for the CSS-beautifier button on the edit style page" - }, - "styleEnabledLabel": { - "message": "Омогућено", - "description": "Label for the enabled state of styles" - }, - "styleToMozillaFormatHelp": { - "message": "Mozilla формат кода се може користити у Stylish за Firefox и може се послати на userstyles.org.", - "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" - }, - "sectionAdd": { - "message": "Додај нови одељак", - "description": "Label for the button to add a section" - }, - "styleSaveLabel": { - "message": "Сачувај", - "description": "Label for save button for style editing" - }, - "confirmStop": { - "message": "Заустави", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "ову УРЛ адресу", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, - "appliesAdd": { - "message": "Додај", - "description": "Label for the button to add an 'applies' entry" - }, - "appliesRegexpOption": { - "message": "УРЛ адресе које одговарају регуларном изразу", - "description": "Option to make the style apply to the entered string as a regular expression" - }, - "styleInstall": { - "message": "Инсталирати '$stylename$' у Stylus?", - "description": "Confirmation when installing a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, - "manageText": { - "message": "Преузмите стилове са userstyles.org | Помоћ", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Претражи садржај", - "description": "Label for the search filter textbox on the Manage styles page" - }, - "disableStyleLabel": { - "message": "Онемогући", - "description": "Label for the button to disable a style" - }, - "prefShowBadge": { - "message": "Прикажи број активних стилова за тренутни сајт на дугмету на алатној траци", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Прикажи број активних стилова", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Преламање текста", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, - "styleCancelEditLabel": { - "message": "Назад на управљање", - "description": "Label for cancel button for style editing" - }, - "styleChangesNotSaved": { - "message": "Направили сте измене овог стила које нисте сачували.", - "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" - }, - "importLabel": { - "message": "Увези", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, - "updateCheckFailServerUnreachable": { - "message": "Ажурирање није успело - сервер није доступан.", - "description": "Text that displays when an update check failed because the update server is unreachable" - }, - "manageFilters": { - "message": "Филтери", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Примени сва ажурирања", - "description": "Label for the button to apply all detected updates" - }, - "deleteStyleConfirm": { - "message": "Да ли сте сигурни да желите да избришете овај стил?", - "description": "Confirmation before deleting a style" - }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Регуларни израз је неисправан.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Опције", - "description": "Heading for options section on manage page." - }, - "appliesDisplay": { - "message": "Примењује се на: $applies$", - "description": "Text on the manage screen to describe what the style applies to", - "placeholders": { - "applies": { - "content": "$1" - } - } - }, - "styleUpdate": { - "message": "Да ли сте сигурни да желите да ажурирате '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, - "styleSectionsTitle": { - "message": "Одељци", - "description": "Title for the style sections section" - }, - "editStyleTitle": { - "message": "Уреди стил $stylename$", - "description": "Title of the page for editing styles", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, - "updateCheckSucceededNoUpdate": { - "message": "Стил је ажуриран.", - "description": "Text that displays when an update check completed and no update is available" - }, - "appliesUrlPrefixOption": { - "message": "УРЛ адресе које почињу са", - "description": "Option to make the style apply to the entered string as a URL prefix" - }, - "searchRegexp": { - "message": "Користи /re/ синтаксу за претрагу регуларним изразом", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Одбаци садржај тренутног стила и упиши преко њега увезени стил", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "Излистај стилове пре команди у менију дугмета на алатној траци", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, - "sectionHelp": { - "message": "Одељци вам омогућавају да дефинишете различите делове кода који се примењују на раличите скупове УРЛ-ова у истом стилу. На пример, један исти стил може променити почетну страницу једног сајта на један начин а остатак сајта на други начин.", - "description": "Help text for sections" - }, - "noStylesForSite": { - "message": "Нема инсталираних стилова за овај сајт.", - "description": "Text displayed when no styles are installed for the current site" - }, - "appliesDisplayTruncatedSuffix": { - "message": "и још", - "description": "Text added to appliesDisplay when there are more sites for the style than are displayed" - }, - "appliesRemove": { - "message": "Уклони", - "description": "Label for the button to remove an 'applies' entry" - }, - "styleToMozillaFormatTitle": { - "message": "Стил у Mozilla формату", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Упиши стил за:", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Замени", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, - "appliesLabel": { - "message": "Примењује се на", - "description": "Label for 'applies to' fields on the edit/add screen" - }, - "openManage": { - "message": "Управљај инсталираним стиловима", - "description": "Link to open the manage page." - }, - "updateCheckFailBadResponseCode": { - "message": "Ажурирање није успело - сервер је одговорио кодом $code$.", - "description": "Text that displays when an update check failed because the response code indicates an error", - "placeholders": { - "code": { - "content": "$1" - } - } - }, - "appliesSpecify": { - "message": "Детаљније", - "description": "Label for the button to make a style apply only to specific sites" - }, - "installUpdate": { - "message": "Инсталирај ажурирање", - "description": "Label for the button to install an update for a single style" - }, - "styleMozillaFormatHeading": { - "message": "Mozilla формат", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, - "stylusUnavailableForURL": { - "message": "Stylus не ради на страницама као што је ова.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" - }, - "sectionRemove": { - "message": "Уклони одељак", - "description": "Label for the button to remove a section" - }, - "disableAllStyles": { - "message": "Искључи све стилове", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Опозови (свеобухватно)", - "description": "CSS-beautify global Undo button label" - }, - "updateCompleted": { - "message": "Ажурирање је комплетирано.", - "description": "Text that displays when an update completed" - }, - "checkingForUpdate": { - "message": "Проверавање...", - "description": "Text to display when checking a style for an update" - }, - "sectionCode": { - "message": "Код", - "description": "Label for the code for a section" - }, - "cm_smartIndent": { - "message": "Користи паметно увлачење редова", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, - "appliesHelp": { - "message": "Употреба 'Примењује се на' одређује опсег УРЛ адреса на које се код у овом одељку примењује.", - "description": "Help text for 'applies to' section" - }, - "editStyleHeading": { - "message": "Уреди стил", - "description": "Title of the page for editing styles" - }, - "appliesUrlOption": { - "message": "УРЛ", - "description": "Option to make the style apply to the entered string as a URL" - }, - "addStyleTitle": { - "message": "Додај стил", - "description": "Title of the page for adding styles" - }, - "importReplaceLabel": { - "message": "Упиши преко стила", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "Дошло је до грешке користећи Stylus базу података. Да ли желите да посетите веб страницу са могућим решењима?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Додај увезени стил тренутном стилу", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Притисни пречицу", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Замени све", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Иди на ред (или line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, - "checkAllUpdates": { - "message": "Проверите ажурирања за све стилове", - "description": "Label for the button to check all styles for updates" - }, - "issuesHelp": { - "message": "Проблем пронађен од стране CSSLint са овим омогућеним правилима:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "Не", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Опозови", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Мапа тастера", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Користи картице са паметним увлачењем редова", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Замени са", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, - "deleteStyleLabel": { - "message": "Избриши", - "description": "Label for the button to delete a style" - }, - "addStyleLabel": { - "message": "Упиши нови стил", - "description": "Label for the button to go to the add style page" - }, - "manageOnlyEnabled": { - "message": "Само омогућени стилови", - "description": "Checkbox to show only enabled styles" - }, - "editStyleLabel": { - "message": "Уреди", - "description": "Label for the button to go to the edit style page" - }, - "cm_theme": { - "message": "Тема", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Укуцај име команде", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "description": { - "message": "Измените стил интернет мреже управљачем корисничких стилова. Stylus вам омогућава да лако инсталирате теме и скинове за многе популарне сајтове.", - "description": "Extension description" - } -} +{ + "appliesToEverything": { + "message": "Све", + "description": "Text displayed for styles that apply to all sites" + }, + "defaultTheme": { + "message": "подразумевано", + "description": "Default CodeMirror CSS theme option on the edit style page" + }, + "exportLabel": { + "message": "Извези", + "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" + }, + "issues": { + "message": "Проблеми", + "description": "Label for the CSSLint issues block on the style edit page" + }, + "cm_tabSize": { + "message": "Величина картице", + "description": "Label for the text box controlling tab size option for the style editor." + }, + "enableStyleLabel": { + "message": "Омогући", + "description": "Label for the button to enable a style" + }, + "styleMissingName": { + "message": "Унесите назив", + "description": "Error displayed when user saves without providing a name" + }, + "appliesDomainOption": { + "message": "УРЛ адресе на домену", + "description": "Option to make the style apply to the entered string as a domain" + }, + "checkForUpdate": { + "message": "Проверите ажурирање", + "description": "Label for the button to check a single style for an update" + }, + "importAppendLabel": { + "message": "Додај стилу", + "description": "Label for the button to import a style and append to the existing sections" + }, + "updateAllCheckSucceededNoUpdate": { + "message": "Сви стилови су ажурирани.", + "description": "Text that displays when an update all check completed and no updates are available" + }, + "styleFromMozillaFormatPrompt": { + "message": "Налепи код у Mozilla формату", + "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" + }, + "helpAlt": { + "message": "Помоћ", + "description": "Alternate text for help buttons" + }, + "search": { + "message": "Претражи", + "description": "Label before the search input field in the editor shown on Ctrl-F" + }, + "confirmYes": { + "message": "Да", + "description": "'Yes' button in a confirm dialog" + }, + "findStylesForSite": { + "message": "Пронађи још стилова за овај сајт", + "description": "Text for a link that gets a list of styles for the current site" + }, + "manageHeading": { + "message": "Инсталирани стилови", + "description": "Heading for the manage page" + }, + "styleBeautify": { + "message": "Улепшај", + "description": "Label for the CSS-beautifier button on the edit style page" + }, + "styleEnabledLabel": { + "message": "Омогућено", + "description": "Label for the enabled state of styles" + }, + "styleToMozillaFormatHelp": { + "message": "Mozilla формат кода се може користити у Stylish за Firefox и може се послати на userstyles.org.", + "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" + }, + "sectionAdd": { + "message": "Додај нови одељак", + "description": "Label for the button to add a section" + }, + "styleSaveLabel": { + "message": "Сачувај", + "description": "Label for save button for style editing" + }, + "confirmStop": { + "message": "Заустави", + "description": "'Stop' button in a confirm dialog" + }, + "writeStyleForURL": { + "message": "ову УРЛ адресу", + "description": "Text for link in toolbar pop-up to write a new style for the current URL" + }, + "appliesAdd": { + "message": "Додај", + "description": "Label for the button to add an 'applies' entry" + }, + "appliesRegexpOption": { + "message": "УРЛ адресе које одговарају регуларном изразу", + "description": "Option to make the style apply to the entered string as a regular expression" + }, + "styleInstall": { + "message": "Инсталирати '$stylename$' у Stylus?", + "description": "Confirmation when installing a style", + "placeholders": { + "stylename": { + "content": "$1" + } + } + }, + "manageText": { + "message": "Преузмите стилове са userstyles.org | Помоћ", + "description": "Help text on the manage page" + }, + "searchStyles": { + "message": "Претражи садржај", + "description": "Label for the search filter textbox on the Manage styles page" + }, + "disableStyleLabel": { + "message": "Онемогући", + "description": "Label for the button to disable a style" + }, + "prefShowBadge": { + "message": "Прикажи број активних стилова за тренутни сајт на дугмету на алатној траци", + "description": "Label for the checkbox controlling toolbar badge text." + }, + "menuShowBadge": { + "message": "Прикажи број активних стилова", + "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." + }, + "cm_lineWrapping": { + "message": "Преламање текста", + "description": "Label for the checkbox controlling word wrap option for the style editor." + }, + "styleCancelEditLabel": { + "message": "Назад на управљање", + "description": "Label for cancel button for style editing" + }, + "styleChangesNotSaved": { + "message": "Направили сте измене овог стила које нисте сачували.", + "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" + }, + "importLabel": { + "message": "Увези", + "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" + }, + "updateCheckFailServerUnreachable": { + "message": "Ажурирање није успело: сервер није доступан.", + "description": "Text that displays when an update check failed because the update server is unreachable" + }, + "manageFilters": { + "message": "Филтери", + "description": "Label for filters container" + }, + "applyAllUpdates": { + "message": "Примени сва ажурирања", + "description": "Label for the button to apply all detected updates" + }, + "deleteStyleConfirm": { + "message": "Да ли сте сигурни да желите да избришете овај стил?", + "description": "Confirmation before deleting a style" + }, + "styleBadRegexp": { + "message": "Регуларни израз је неисправан.", + "description": "Validation message for a bad regexp in a style" + }, + "optionsHeading": { + "message": "Опције", + "description": "Heading for options section on manage page." + }, + "appliesDisplay": { + "message": "Примењује се на: $applies$", + "description": "Text on the manage screen to describe what the style applies to", + "placeholders": { + "applies": { + "content": "$1" + } + } + }, + "styleUpdate": { + "message": "Да ли сте сигурни да желите да ажурирате '$stylename$'?", + "description": "Confirmation when updating a style", + "placeholders": { + "stylename": { + "content": "$1" + } + } + }, + "styleSectionsTitle": { + "message": "Одељци", + "description": "Title for the style sections section" + }, + "editStyleTitle": { + "message": "Уреди стил $stylename$", + "description": "Title of the page for editing styles", + "placeholders": { + "stylename": { + "content": "$1" + } + } + }, + "updateCheckSucceededNoUpdate": { + "message": "Стил је ажуриран.", + "description": "Text that displays when an update check completed and no update is available" + }, + "appliesUrlPrefixOption": { + "message": "УРЛ адресе које почињу са", + "description": "Option to make the style apply to the entered string as a URL prefix" + }, + "searchRegexp": { + "message": "Користи /re/ синтаксу за претрагу регуларним изразом", + "description": "Label after the search input field in the editor shown on Ctrl-F" + }, + "importReplaceTooltip": { + "message": "Одбаци садржај тренутног стила и упиши преко њега увезени стил", + "description": "Label for the button to import and overwrite current style" + }, + "popupStylesFirst": { + "message": "Излистај стилове пре команди у менију дугмета на алатној траци", + "description": "Label for the checkbox controlling section order in the popup." + }, + "sectionHelp": { + "message": "Одељци вам омогућавају да дефинишете различите делове кода који се примењују на раличите скупове УРЛ-ова у истом стилу. На пример, један исти стил може променити почетну страницу једног сајта на један начин а остатак сајта на други начин.", + "description": "Help text for sections" + }, + "noStylesForSite": { + "message": "Нема инсталираних стилова за овај сајт.", + "description": "Text displayed when no styles are installed for the current site" + }, + "appliesDisplayTruncatedSuffix": { + "message": "и још", + "description": "Text added to appliesDisplay when there are more sites for the style than are displayed" + }, + "appliesRemove": { + "message": "Уклони", + "description": "Label for the button to remove an 'applies' entry" + }, + "styleToMozillaFormatTitle": { + "message": "Стил у Mozilla формату", + "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" + }, + "writeStyleFor": { + "message": "Упиши стил за:", + "description": "Label for toolbar pop-up that precedes the links to write a new style" + }, + "replace": { + "message": "Замени", + "description": "Label before the replace input field in the editor shown on Ctrl-H" + }, + "appliesLabel": { + "message": "Примењује се на", + "description": "Label for 'applies to' fields on the edit/add screen" + }, + "openManage": { + "message": "Управљај инсталираним стиловима", + "description": "Link to open the manage page." + }, + "updateCheckFailBadResponseCode": { + "message": "Ажурирање није успело: сервер је одговорио кодом $code$.", + "description": "Text that displays when an update check failed because the response code indicates an error", + "placeholders": { + "code": { + "content": "$1" + } + } + }, + "appliesSpecify": { + "message": "Детаљније", + "description": "Label for the button to make a style apply only to specific sites" + }, + "installUpdate": { + "message": "Инсталирај ажурирање", + "description": "Label for the button to install an update for a single style" + }, + "styleMozillaFormatHeading": { + "message": "Mozilla формат", + "description": "Heading for the section with buttons to import/export Mozilla format of the style" + }, + "sectionRemove": { + "message": "Уклони одељак", + "description": "Label for the button to remove a section" + }, + "disableAllStyles": { + "message": "Искључи све стилове", + "description": "Label for the checkbox that turns all enabled styles off." + }, + "undoGlobal": { + "message": "Опозови (свеобухватно)", + "description": "CSS-beautify global Undo button label" + }, + "updateCompleted": { + "message": "Ажурирање је комплетирано.", + "description": "Text that displays when an update completed" + }, + "checkingForUpdate": { + "message": "Проверавање...", + "description": "Text to display when checking a style for an update" + }, + "sectionCode": { + "message": "Код", + "description": "Label for the code for a section" + }, + "cm_smartIndent": { + "message": "Користи паметно увлачење редова", + "description": "Label for the checkbox controlling smart indentation option for the style editor." + }, + "appliesHelp": { + "message": "Употреба 'Примењује се на' одређује опсег УРЛ адреса на које се код у овом одељку примењује.", + "description": "Help text for 'applies to' section" + }, + "editStyleHeading": { + "message": "Уреди стил", + "description": "Title of the page for editing styles" + }, + "appliesUrlOption": { + "message": "УРЛ", + "description": "Option to make the style apply to the entered string as a URL" + }, + "stylusUnavailableForURL": { + "message": "Stylus не ради на страницама као што је ова.", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "addStyleTitle": { + "message": "Додај стил", + "description": "Title of the page for adding styles" + }, + "importReplaceLabel": { + "message": "Упиши преко стила", + "description": "Label for the button to import and overwrite current style" + }, + "dbError": { + "message": "Дошло је до грешке користећи Stylus базу података. Да ли желите да посетите веб страницу са могућим решењима?", + "description": "Prompt when a DB error is encountered" + }, + "importAppendTooltip": { + "message": "Додај увезени стил тренутном стилу", + "description": "Tooltip for the button to import a style and append to the existing sections" + }, + "helpKeyMapHotkey": { + "message": "Притисни пречицу", + "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" + }, + "replaceAll": { + "message": "Замени све", + "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" + }, + "editGotoLine": { + "message": "Иди на ред (или line:col)", + "description": "Go to line or line:column on Ctrl-G in style code editor" + }, + "checkAllUpdates": { + "message": "Проверите ажурирања за све стилове", + "description": "Label for the button to check all styles for updates" + }, + "issuesHelp": { + "message": "Проблем пронађен од стране CSSLint са овим омогућеним правилима:", + "description": "Help popup message for the CSSLint issues block on the style edit page" + }, + "confirmNo": { + "message": "Не", + "description": "'No' button in a confirm dialog" + }, + "undo": { + "message": "Опозови", + "description": "Button label" + }, + "cm_keyMap": { + "message": "Мапа тастера", + "description": "Label for the drop-down list controlling the keymap for the style editor." + }, + "cm_indentWithTabs": { + "message": "Користи картице са паметним увлачењем редова", + "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." + }, + "replaceWith": { + "message": "Замени са", + "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." + }, + "deleteStyleLabel": { + "message": "Избриши", + "description": "Label for the button to delete a style" + }, + "addStyleLabel": { + "message": "Упиши нови стил", + "description": "Label for the button to go to the add style page" + }, + "manageOnlyEnabled": { + "message": "Само омогућени стилови", + "description": "Checkbox to show only enabled styles" + }, + "editStyleLabel": { + "message": "Уреди", + "description": "Label for the button to go to the edit style page" + }, + "cm_theme": { + "message": "Тема", + "description": "Label for the style editor's CSS theme." + }, + "helpKeyMapCommand": { + "message": "Укуцај име команде", + "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" + }, + "description": { + "message": "Измените стил интернет мреже управљачем корисничких стилова. Stylus вам омогућава да лако инсталирате теме и скинове за многе популарне сајтове.", + "description": "Extension description" + } +} \ No newline at end of file diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 5b015421..b42e3c2c 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -3,18 +3,6 @@ "message": "Allt", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, "cm_tabSize": { "message": "Flikstorlek", "description": "Label for the text box controlling tab size option for the style editor." @@ -35,42 +23,22 @@ "message": "Leta efter uppdatering", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, "updateAllCheckSucceededNoUpdate": { "message": "Alla stilar är fullt uppdaterade.", "description": "Text that displays when an update all check completed and no updates are available" }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Hjälp", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Hitta fler stilar för denna sida.", + "message": "Hitta fler stilar för denna sida", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Installerade Stilar", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Aktiverad", "description": "Label for the enabled state of styles" @@ -87,10 +55,6 @@ "message": "Spara", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, "writeStyleForURL": { "message": "denna URL", "description": "Text for link in toolbar pop-up to write a new style for the current URL" @@ -112,14 +76,6 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Inaktivera", "description": "Label for the button to disable a style" @@ -144,12 +100,8 @@ "message": "Du har gjort ändringar i denna stil utan att spara.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Uppdateringen misslyckades - server onåbar.", + "message": "Uppdateringen misslyckades: server onåbar.", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -164,12 +116,6 @@ "message": "Är du säker på att du vill ta bort denna stil?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, "styleBadRegexp": { "message": "Regexp:en är ogiltig", "description": "Validation message for a bad regexp in a style" @@ -217,17 +163,9 @@ "message": "URL:er som börjar på", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, "popupStylesFirst": { "message": "Lista stilar före kommandon i verktygsfältets knappmeny", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "Sektioner låter dig definiera olika sorters kod som påverkar olika URL:er i samma stil. Till exempel, en stil kan ändra en viss hemsida på ett sätt, samtidigt som den ändrar andra delar på en helt annan sida.", @@ -245,22 +183,14 @@ "message": "Ta bort", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, "manageTitle": { - "message": "Stylus", + "message": "Tyylikäs", "description": "Title for the manage page" }, "writeStyleFor": { "message": "Skriv stil för:", "description": "Label for toolbar pop-up that precedes the links to write a new style" }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Gäller för", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +200,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Uppdateringen misslyckades - servern svarade med kod $code$.", + "message": "Uppdateringen misslyckades: servern svarade med kod $code$.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,14 +216,6 @@ "message": "Installera uppdatering", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, - "stylusUnavailableForURL": { - "message": "Stylus fungerar inte på sidor som denna.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" - }, "sectionRemove": { "message": "Ta bort sektion", "description": "Label for the button to remove a section" @@ -302,10 +224,6 @@ "message": "Stäng av alla stilar", "description": "Label for the checkbox that turns all enabled styles off." }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Uppdatering slutförd.", "description": "Text that displays when an update completed" @@ -330,34 +248,18 @@ "message": "Ändra i Stil", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "stylusUnavailableForURL": { + "message": "Stylus fungerar inte på sidor som denna.", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" }, "addStyleTitle": { "message": "Lägg till stil", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, "dbError": { "message": "Ett fel inträffades vid hanteringen av Stylus-databasen. Skulle du vilja besöka en sida med eventuella lösningar?", "description": "Prompt when a DB error is encountered" }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, "editGotoLine": { "message": "Gå till rad (eller rad:kol)", "description": "Go to line or line:column on Ctrl-G in style code editor" @@ -366,18 +268,6 @@ "message": "Leta igenom alla stilar efter uppdateringar", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, "cm_keyMap": { "message": "Nyckelkarta", "description": "Label for the drop-down list controlling the keymap for the style editor." @@ -386,10 +276,6 @@ "message": "Använd flikar med smart indrag", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Ta bort", "description": "Label for the button to delete a style" @@ -406,16 +292,8 @@ "message": "Ändra", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Style:a om webben med Stylus, en användarstils-hanterare. Stylus låter dig enkelt installera teman och skal för många populära sidor.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/sv_SE/messages.json b/_locales/sv_SE/messages.json index 1016654e..294773b4 100644 --- a/_locales/sv_SE/messages.json +++ b/_locales/sv_SE/messages.json @@ -1,186 +1,101 @@ { "appliesToEverything": { - "message": "Allt", - "description": "Text displayed for styles that apply to all sites" - }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" + "message": "Allt" }, "cm_tabSize": { - "message": "Flikstorlek", - "description": "Label for the text box controlling tab size option for the style editor." + "message": "Flikstorlek" }, "enableStyleLabel": { - "message": "Aktivera", - "description": "Label for the button to enable a style" + "message": "Aktivera" }, "styleMissingName": { - "message": "Ange ett namn.", - "description": "Error displayed when user saves without providing a name" + "message": "Ange ett namn." }, "appliesDomainOption": { - "message": "URL:er på domänen", - "description": "Option to make the style apply to the entered string as a domain" + "message": "URL:er på domänen" }, "checkForUpdate": { - "message": "Leta efter uppdatering", - "description": "Label for the button to check a single style for an update" - }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" + "message": "Leta efter uppdatering" }, "updateAllCheckSucceededNoUpdate": { - "message": "Alla stilar är fullt uppdaterade.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" + "message": "Alla stilar är fullt uppdaterade." }, "helpAlt": { - "message": "Hjälp", - "description": "Alternate text for help buttons" - }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" + "message": "Hjälp" }, "findStylesForSite": { - "message": "Hitta fler stilar för denna sida.", - "description": "Text for a link that gets a list of styles for the current site" + "message": "Hitta fler stilar för denna sida" }, "manageHeading": { - "message": "Installerade Stilar", - "description": "Heading for the manage page" - }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" + "message": "Installerade Stilar" }, "styleEnabledLabel": { - "message": "Aktiverad", - "description": "Label for the enabled state of styles" + "message": "Aktiverad" }, "styleToMozillaFormatHelp": { - "message": "Mozilla-formatet av koden fungerar i Stylish till Firefox samt vid uppladdandet till userstyles.org.", - "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" + "message": "Mozilla-formatet av koden fungerar i Stylish till Firefox samt vid uppladdandet till userstyles.org." }, "sectionAdd": { - "message": "Lägg till ytterligare en sektion", - "description": "Label for the button to add a section" + "message": "Lägg till ytterligare en sektion" }, "styleSaveLabel": { - "message": "Spara", - "description": "Label for save button for style editing" - }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" + "message": "Spara" }, "writeStyleForURL": { - "message": "denna URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" + "message": "denna URL" }, "appliesAdd": { - "message": "Lägg till", - "description": "Label for the button to add an 'applies' entry" + "message": "Lägg till" }, "appliesRegexpOption": { - "message": "URL:er som matchar regexp:en", - "description": "Option to make the style apply to the entered string as a regular expression" + "message": "URL:er som matchar regexp:en" }, "styleInstall": { "message": "Installera '$stylename$' in i Stylus?", - "description": "Confirmation when installing a style", "placeholders": { "stylename": { "content": "$1" } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { - "message": "Inaktivera", - "description": "Label for the button to disable a style" + "message": "Inaktivera" }, "prefShowBadge": { - "message": "Visa antalet aktiva stilar för den nuvarande sidan på verktygsfältsikonen", - "description": "Label for the checkbox controlling toolbar badge text." + "message": "Visa antalet aktiva stilar för den nuvarande sidan på verktygsfältsikonen" }, "menuShowBadge": { - "message": "Visa antalet aktiva stilar", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." + "message": "Visa antalet aktiva stilar" }, "cm_lineWrapping": { - "message": "Radbrytning", - "description": "Label for the checkbox controlling word wrap option for the style editor." + "message": "Radbrytning" }, "styleCancelEditLabel": { - "message": "Återgå till hantera", - "description": "Label for cancel button for style editing" + "message": "Återgå till hantera" }, "styleChangesNotSaved": { - "message": "Du har gjort ändringar i denna stil utan att spara.", - "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" - }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" + "message": "Du har gjort ändringar i denna stil utan att spara." }, "updateCheckFailServerUnreachable": { - "message": "Uppdateringen misslyckades - server onåbar.", - "description": "Text that displays when an update check failed because the update server is unreachable" + "message": "Uppdateringen misslyckades: server onåbar." }, "manageFilters": { - "message": "Filter", - "description": "Label for filters container" + "message": "Filter" }, "applyAllUpdates": { - "message": "Verkställ alla uppdateringar", - "description": "Label for the button to apply all detected updates" + "message": "Verkställ alla uppdateringar" }, "deleteStyleConfirm": { - "message": "Är du säker på att du vill ta bort denna stil?", - "description": "Confirmation before deleting a style" - }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" + "message": "Är du säker på att du vill ta bort denna stil?" }, "styleBadRegexp": { - "message": "Regexp:en är ogiltig.", - "description": "Validation message for a bad regexp in a style" + "message": "Regexp:en är ogiltig." }, "optionsHeading": { - "message": "Alternativ", - "description": "Heading for options section on manage page." + "message": "Alternativ" }, "appliesDisplay": { "message": "Gäller för: $applies$", - "description": "Text on the manage screen to describe what the style applies to", "placeholders": { "applies": { "content": "$1" @@ -189,7 +104,6 @@ }, "styleUpdate": { "message": "Är du säker på att du vill uppdatera '$stylename$'?", - "description": "Confirmation when updating a style", "placeholders": { "stylename": { "content": "$1" @@ -197,12 +111,10 @@ } }, "styleSectionsTitle": { - "message": "Sektioner", - "description": "Title for the style sections section" + "message": "Sektioner" }, "editStyleTitle": { "message": "Ändra i Stil $stylename$", - "description": "Title of the page for editing styles", "placeholders": { "stylename": { "content": "$1" @@ -210,68 +122,37 @@ } }, "updateCheckSucceededNoUpdate": { - "message": "Stilen är fullt uppdaterad.", - "description": "Text that displays when an update check completed and no update is available" + "message": "Stilen är fullt uppdaterad." }, "appliesUrlPrefixOption": { - "message": "URL:er som börjar på", - "description": "Option to make the style apply to the entered string as a URL prefix" - }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" + "message": "URL:er som börjar på" }, "popupStylesFirst": { - "message": "Lista stilar före kommandon i verktygsfältets knappmeny", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "message": "Lista stilar före kommandon i verktygsfältets knappmeny" }, "sectionHelp": { - "message": "Sektioner låter dig definiera olika sorters kod som påverkar olika URL:er i samma stil. Till exempel, en stil kan ändra en viss hemsida på ett sätt, samtidigt som den ändrar andra delar på en helt annan sida.", - "description": "Help text for sections" + "message": "Sektioner låter dig definiera olika sorters kod som påverkar olika URL:er i samma stil. Till exempel, en stil kan ändra en viss hemsida på ett sätt, samtidigt som den ändrar andra delar på en helt annan sida." }, "noStylesForSite": { - "message": "Inga stilar installerade för denna sida.", - "description": "Text displayed when no styles are installed for the current site" + "message": "Inga stilar installerade för denna sida." }, "appliesDisplayTruncatedSuffix": { - "message": "och mer", - "description": "Text added to appliesDisplay when there are more sites for the style than are displayed" + "message": "och mer" }, "appliesRemove": { - "message": "Ta bort", - "description": "Label for the button to remove an 'applies' entry" - }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" + "message": "Ta bort" }, "writeStyleFor": { - "message": "Skapa stil för:", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" + "message": "Skapa stil för:" }, "appliesLabel": { - "message": "Gäller för", - "description": "Label for 'applies to' fields on the edit/add screen" + "message": "Gäller för" }, "openManage": { - "message": "Hantera installerade stilar", - "description": "Link to open the manage page." + "message": "Hantera installerade stilar" }, "updateCheckFailBadResponseCode": { - "message": "Uppdateringen misslyckades - servern svarade med kod $code$.", - "description": "Text that displays when an update check failed because the response code indicates an error", + "message": "Uppdateringen misslyckades: servern svarade med kod $code$.", "placeholders": { "code": { "content": "$1" @@ -279,143 +160,69 @@ } }, "appliesSpecify": { - "message": "Specificera", - "description": "Label for the button to make a style apply only to specific sites" + "message": "Specificera" }, "installUpdate": { - "message": "Installera uppdatering", - "description": "Label for the button to install an update for a single style" - }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" + "message": "Installera uppdatering" }, "stylusUnavailableForURL": { - "message": "Stylus fungerar inte på sidor som dessa.", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + "message": "Stylus fungerar inte på sidor som dessa." }, "sectionRemove": { - "message": "Ta bort sektion", - "description": "Label for the button to remove a section" + "message": "Ta bort sektion" }, "disableAllStyles": { - "message": "Stäng av alla stilar", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" + "message": "Stäng av alla stilar" }, "updateCompleted": { - "message": "Uppdatering slutförd.", - "description": "Text that displays when an update completed" + "message": "Uppdatering slutförd." }, "checkingForUpdate": { - "message": "Letar...", - "description": "Text to display when checking a style for an update" + "message": "Letar..." }, "sectionCode": { - "message": "Kod", - "description": "Label for the code for a section" + "message": "Kod" }, "cm_smartIndent": { - "message": "Använd smart indrag", - "description": "Label for the checkbox controlling smart indentation option for the style editor." + "message": "Använd smart indrag" }, "appliesHelp": { - "message": "Använd 'Gäller för' alternativet för att begränsa vilka URL:er koden i denna sektion gäller för.", - "description": "Help text for 'applies to' section" + "message": "Använd 'Gäller för' alternativet för att begränsa vilka URL:er koden i denna sektion gäller för." }, "editStyleHeading": { - "message": "Ändra i Stil", - "description": "Title of the page for editing styles" - }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" + "message": "Ändra i Stil" }, "addStyleTitle": { - "message": "Lägg till stil", - "description": "Title of the page for adding styles" - }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" + "message": "Lägg till stil" }, "dbError": { - "message": "Ett fel inträffades vid hanteringen av Stylus-databasen. Skulle du vilja besöka en sida med eventuella lösningar?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" + "message": "Ett fel inträffades vid hanteringen av Stylus-databasen. Skulle du vilja besöka en sida med eventuella lösningar?" }, "editGotoLine": { - "message": "Gå till rad (eller rad:kol)", - "description": "Go to line or line:column on Ctrl-G in style code editor" + "message": "Gå till rad (eller rad:kol)" }, "checkAllUpdates": { - "message": "Leta igenom alla stilar efter uppdateringar", - "description": "Label for the button to check all styles for updates" - }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" + "message": "Leta igenom alla stilar efter uppdateringar" }, "cm_keyMap": { - "message": "Nyckelkarta", - "description": "Label for the drop-down list controlling the keymap for the style editor." + "message": "Nyckelkarta" }, "cm_indentWithTabs": { - "message": "Använd flikar med smart indrag", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." + "message": "Använd flikar med smart indrag" }, "deleteStyleLabel": { - "message": "Ta bort", - "description": "Label for the button to delete a style" + "message": "Ta bort" }, "addStyleLabel": { - "message": "Skriv ny stil", - "description": "Label for the button to go to the add style page" + "message": "Skriv ny stil" }, "manageOnlyEnabled": { - "message": "Endast aktiverade stilar", - "description": "Checkbox to show only enabled styles" + "message": "Endast aktiverade stilar" }, "editStyleLabel": { - "message": "Ändra", - "description": "Label for the button to go to the edit style page" - }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" + "message": "Ändra" }, "description": { - "message": "Style:a om webben med Stylus, en användarstils-hanterare. Stylus låter dig enkelt installera teman och skal för många populära sidor.", - "description": "Extension description" + "message": "Style:a om webben med Stylus, en användarstils-hanterare. Stylus låter dig enkelt installera teman och skal för många populära sidor." } } diff --git a/_locales/te/messages.json b/_locales/te/messages.json index ed0550f2..fc2c57e3 100644 --- a/_locales/te/messages.json +++ b/_locales/te/messages.json @@ -1,417 +1,55 @@ { "appliesToEverything": { - "message": "అన్నిటికీ", - "description": "Text displayed for styles that apply to all sites" - }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." + "message": "అన్నిటికీ" }, "enableStyleLabel": { - "message": "చేతనించు", - "description": "Label for the button to enable a style" - }, - "styleMissingName": { - "message": "Enter a name.", - "description": "Error displayed when user saves without providing a name" - }, - "appliesDomainOption": { - "message": "URLs on the domain", - "description": "Option to make the style apply to the entered string as a domain" - }, - "checkForUpdate": { - "message": "Check for update", - "description": "Label for the button to check a single style for an update" - }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" + "message": "చేతనించు" }, "helpAlt": { - "message": "సహాయం", - "description": "Alternate text for help buttons" - }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, - "findStylesForSite": { - "message": "Find more styles for this site.", - "description": "Text for a link that gets a list of styles for the current site" + "message": "సహాయం" }, "manageHeading": { - "message": "స్థాపిత శైలులు", - "description": "Heading for the manage page" - }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, - "styleEnabledLabel": { - "message": "Enabled", - "description": "Label for the enabled state of styles" - }, - "styleToMozillaFormatHelp": { - "message": "The Mozilla format of the code can be used with Stylish for Firefox and can be submitted to userstyles.org.", - "description": "Help info for the Mozilla format header section that converts the code to/from Mozilla format" - }, - "sectionAdd": { - "message": "Add another section", - "description": "Label for the button to add a section" + "message": "స్థాపిత శైలులు" }, "styleSaveLabel": { - "message": "భద్రపరచు", - "description": "Label for save button for style editing" - }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" + "message": "భద్రపరచు" }, "appliesAdd": { - "message": "చేర్చు", - "description": "Label for the button to add an 'applies' entry" - }, - "appliesRegexpOption": { - "message": "URLs matching the regexp", - "description": "Option to make the style apply to the entered string as a regular expression" - }, - "styleInstall": { - "message": "Install '$stylename$' into Stylus?", - "description": "Confirmation when installing a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" + "message": "చేర్చు" }, "disableStyleLabel": { - "message": "అచేతనించు", - "description": "Label for the button to disable a style" - }, - "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, - "styleCancelEditLabel": { - "message": "Back to manage", - "description": "Label for cancel button for style editing" - }, - "styleChangesNotSaved": { - "message": "You've made changes to this style without saving.", - "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" - }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, - "updateCheckFailServerUnreachable": { - "message": "Update failed - server unreachable.", - "description": "Text that displays when an update check failed because the update server is unreachable" - }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" + "message": "అచేతనించు" }, "deleteStyleConfirm": { - "message": "మీరు నజంగానే ఈ శైలిని తొలగించాలనుకుంటున్నారా?", - "description": "Confirmation before deleting a style" - }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." + "message": "మీరు నజంగానే ఈ శైలిని తొలగించాలనుకుంటున్నారా?" }, "appliesDisplay": { "message": "వేటికి వర్తిస్తుంది; $applies$", - "description": "Text on the manage screen to describe what the style applies to", "placeholders": { "applies": { "content": "$1" } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { - "message": "విభాగాలు", - "description": "Title for the style sections section" - }, - "editStyleTitle": { - "message": "Edit Style $stylename$", - "description": "Title of the page for editing styles", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, - "updateCheckSucceededNoUpdate": { - "message": "Style is up to date.", - "description": "Text that displays when an update check completed and no update is available" - }, - "appliesUrlPrefixOption": { - "message": "URLs starting with", - "description": "Option to make the style apply to the entered string as a URL prefix" - }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, - "sectionHelp": { - "message": "Sections let you define different pieces of code to apply to different sets of URLs in the same style. For example, a single style could change the homepage of a site one way, while changing the rest of a site another way.", - "description": "Help text for sections" - }, - "noStylesForSite": { - "message": "No styles installed for this site.", - "description": "Text displayed when no styles are installed for the current site" + "message": "విభాగాలు" }, "appliesDisplayTruncatedSuffix": { - "message": "ఇంకా మరిన్ని", - "description": "Text added to appliesDisplay when there are more sites for the style than are displayed" + "message": "ఇంకా మరిన్ని" }, "appliesRemove": { - "message": "తొలగించు", - "description": "Label for the button to remove an 'applies' entry" - }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" + "message": "తొలగించు" }, "manageTitle": { - "message": "స్టైలిష్", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, - "appliesLabel": { - "message": "Applies to", - "description": "Label for 'applies to' fields on the edit/add screen" - }, - "openManage": { - "message": "Manage installed styles", - "description": "Link to open the manage page." - }, - "updateCheckFailBadResponseCode": { - "message": "Update failed - server responded with code $code$.", - "description": "Text that displays when an update check failed because the response code indicates an error", - "placeholders": { - "code": { - "content": "$1" - } - } - }, - "appliesSpecify": { - "message": "Specify", - "description": "Label for the button to make a style apply only to specific sites" - }, - "installUpdate": { - "message": "Install update", - "description": "Label for the button to install an update for a single style" - }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, - "sectionRemove": { - "message": "Remove section", - "description": "Label for the button to remove a section" - }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, - "updateCompleted": { - "message": "Update completed.", - "description": "Text that displays when an update completed" - }, - "checkingForUpdate": { - "message": "Checking...", - "description": "Text to display when checking a style for an update" - }, - "sectionCode": { - "message": "Code", - "description": "Label for the code for a section" - }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, - "appliesHelp": { - "message": "Use the 'Applies to' controls to limit what URLs the code in this section applies to.", - "description": "Help text for 'applies to' section" - }, - "editStyleHeading": { - "message": "Edit Style", - "description": "Title of the page for editing styles" - }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, - "addStyleTitle": { - "message": "Add Style", - "description": "Title of the page for adding styles" - }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, - "checkAllUpdates": { - "message": "Check all styles for updates", - "description": "Label for the button to check all styles for updates" - }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." + "message": "స్టైలిష్" }, "deleteStyleLabel": { - "message": "తొలగించు", - "description": "Label for the button to delete a style" + "message": "తొలగించు" }, "addStyleLabel": { - "message": "క్రొత్త స్టైల్ వ్రాయండి", - "description": "Label for the button to go to the add style page" - }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" + "message": "క్రొత్త స్టైల్ వ్రాయండి" }, "editStyleLabel": { - "message": "మార్చు", - "description": "Label for the button to go to the edit style page" - }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "description": { - "message": "Restyle the web with Stylus, a user styles manager. Stylus lets you easily install themes and skins for many popular sites.", - "description": "Extension description" + "message": "మార్చు" } } diff --git a/_locales/tr/messages.json b/_locales/tr/messages.json index efbc2fd3..b9b565d1 100644 --- a/_locales/tr/messages.json +++ b/_locales/tr/messages.json @@ -3,22 +3,6 @@ "message": "Her şey", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "Etkinleştir", "description": "Label for the button to enable a style" @@ -35,42 +19,18 @@ "message": "Güncellemeleri denetle", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "Yardım", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { - "message": "Bu site için başka stiller bul.", + "message": "Bu site için başka stiller bul", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { "message": "Yüklü Stiller", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "Etkin", "description": "Label for the enabled state of styles" @@ -87,14 +47,6 @@ "message": "Kaydet", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "Ekleyin", "description": "Label for the button to add an 'applies' entry" @@ -112,30 +64,10 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "Devre dışı bırak", "description": "Label for the button to disable a style" }, - "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "Yönetim sayfasına dön", "description": "Label for cancel button for style editing" @@ -144,40 +76,14 @@ "message": "Bu stilde yaptığınız, kaydedilmemiş değişiklikler var.", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "Güncellenemedi - sunucuya erişilemiyor.", + "message": "Güncellenemedi: sunucuya erişilemiyor.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "Bu stili silmek istediğinizden emin misiniz?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "Şuraya uygulanır: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -187,15 +93,6 @@ } } }, - "styleUpdate": { - "message": "Are you sure you want to update '$stylename$'?", - "description": "Confirmation when updating a style", - "placeholders": { - "stylename": { - "content": "$1" - } - } - }, "styleSectionsTitle": { "message": "Bölümler", "description": "Title for the style sections section" @@ -217,18 +114,6 @@ "message": "Şununla başlayan URL'ler:", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, "sectionHelp": { "message": "Bölümler, aynı stil içindeki farklı URL kümelerine uygulanacak farklı kod parçaları tanımlamanıza olanak sağlar. Örneğin, tek bir stil bir sitenin ana sayfasını belirli bir şekilde değiştirirken, sayfanın kalan kısmını farklı bir şekilde değiştirebilir.", "description": "Help text for sections" @@ -245,22 +130,6 @@ "message": "Kaldır", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "Şuraya uygulanır", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +139,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "Güncellenemedi - sunucu yanıt olarak $code$ kodunu gönderdi.", + "message": "Güncellenemedi: sunucu yanıt olarak $code$ kodunu gönderdi.", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +155,10 @@ "message": "Güncellemeyi yükle", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "Bölümü kaldır", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "Güncelleme tamamlandı.", "description": "Text that displays when an update completed" @@ -314,10 +171,6 @@ "message": "Kod", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "Bu bölümdeki kodun hangi URLlere uygulanacağını sınırlamak için 'Şuraya uygulanır' denetimlerini kullanın.", "description": "Help text for 'applies to' section" @@ -326,66 +179,14 @@ "message": "Stili Düzenle", "description": "Title of the page for editing styles" }, - "appliesUrlOption": { - "message": "URL", - "description": "Option to make the style apply to the entered string as a URL" - }, "addStyleTitle": { "message": "Stil Ekleyin", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "Tüm stiller için güncellemeleri denetle", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "Sil", "description": "Label for the button to delete a style" @@ -394,24 +195,12 @@ "message": "Yeni stil oluşturun", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "Düzenle", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Kullanıcı stil yöneticisi Stylus ile Web'in stilini yenileyin. Stylus Google, Facebook, YouTube, Orkut ve diğer pek çok site için temalar ve görünüm yüklemenize imkân tanır.", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/zh/messages.json b/_locales/zh/messages.json index a28bf22c..dee83899 100644 --- a/_locales/zh/messages.json +++ b/_locales/zh/messages.json @@ -3,22 +3,6 @@ "message": "所有站点", "description": "Text displayed for styles that apply to all sites" }, - "defaultTheme": { - "message": "default", - "description": "Default CodeMirror CSS theme option on the edit style page" - }, - "exportLabel": { - "message": "Export", - "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" - }, - "issues": { - "message": "Issues", - "description": "Label for the CSSLint issues block on the style edit page" - }, - "cm_tabSize": { - "message": "Tab size", - "description": "Label for the text box controlling tab size option for the style editor." - }, "enableStyleLabel": { "message": "启用", "description": "Label for the button to enable a style" @@ -35,30 +19,10 @@ "message": "检查更新", "description": "Label for the button to check a single style for an update" }, - "importAppendLabel": { - "message": "Append to style", - "description": "Label for the button to import a style and append to the existing sections" - }, - "updateAllCheckSucceededNoUpdate": { - "message": "All styles are up to date.", - "description": "Text that displays when an update all check completed and no updates are available" - }, - "styleFromMozillaFormatPrompt": { - "message": "Paste the Mozilla-format code", - "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" - }, "helpAlt": { "message": "帮助", "description": "Alternate text for help buttons" }, - "search": { - "message": "Search", - "description": "Label before the search input field in the editor shown on Ctrl-F" - }, - "confirmYes": { - "message": "Yes", - "description": "'Yes' button in a confirm dialog" - }, "findStylesForSite": { "message": "查找更多适合此站点的样式", "description": "Text for a link that gets a list of styles for the current site" @@ -67,10 +31,6 @@ "message": "已安装的样式", "description": "Heading for the manage page" }, - "styleBeautify": { - "message": "Beautify", - "description": "Label for the CSS-beautifier button on the edit style page" - }, "styleEnabledLabel": { "message": "已启用", "description": "Label for the enabled state of styles" @@ -87,14 +47,6 @@ "message": "保存", "description": "Label for save button for style editing" }, - "confirmStop": { - "message": "Stop", - "description": "'Stop' button in a confirm dialog" - }, - "writeStyleForURL": { - "message": "this URL", - "description": "Text for link in toolbar pop-up to write a new style for the current URL" - }, "appliesAdd": { "message": "添加", "description": "Label for the button to add an 'applies' entry" @@ -112,30 +64,10 @@ } } }, - "manageText": { - "message": "Get styles on userstyles.org | Get help", - "description": "Help text on the manage page" - }, - "searchStyles": { - "message": "Search contents", - "description": "Label for the search filter textbox on the Manage styles page" - }, "disableStyleLabel": { "message": "禁用", "description": "Label for the button to disable a style" }, - "prefShowBadge": { - "message": "Show number of styles active for the current site on the toolbar button", - "description": "Label for the checkbox controlling toolbar badge text." - }, - "menuShowBadge": { - "message": "Show active style count", - "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." - }, - "cm_lineWrapping": { - "message": "Word wrap", - "description": "Label for the checkbox controlling word wrap option for the style editor." - }, "styleCancelEditLabel": { "message": "返回到管理", "description": "Label for cancel button for style editing" @@ -144,40 +76,14 @@ "message": "您已经修改了此样式,但尚未保存", "description": "Text for the prompt when changes are made to a style and the user tries to leave without saving" }, - "importLabel": { - "message": "Import", - "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" - }, "updateCheckFailServerUnreachable": { - "message": "更新失败 - 服务器无法访问.", + "message": "更新失败: 服务器无法访问.", "description": "Text that displays when an update check failed because the update server is unreachable" }, - "manageFilters": { - "message": "Filters", - "description": "Label for filters container" - }, - "applyAllUpdates": { - "message": "Apply all updates", - "description": "Label for the button to apply all detected updates" - }, "deleteStyleConfirm": { "message": "确定要删除这个样式吗?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" - }, - "styleBadRegexp": { - "message": "Regexp is invalid.", - "description": "Validation message for a bad regexp in a style" - }, - "optionsHeading": { - "message": "Options", - "description": "Heading for options section on manage page." - }, "appliesDisplay": { "message": "应用到: $applies$", "description": "Text on the manage screen to describe what the style applies to", @@ -217,18 +123,6 @@ "message": "以指定地址开始", "description": "Option to make the style apply to the entered string as a URL prefix" }, - "searchRegexp": { - "message": "Use /re/ syntax for regexp search", - "description": "Label after the search input field in the editor shown on Ctrl-F" - }, - "importReplaceTooltip": { - "message": "Discard contents of current style and overwrite it with the imported style", - "description": "Label for the button to import and overwrite current style" - }, - "popupStylesFirst": { - "message": "List styles before commands in the toolbar button menu", - "description": "Label for the checkbox controlling section order in the toolbar button menu." - }, "sectionHelp": { "message": "样式节允许你定义多个样式段落并将它们应用于不同的站点匹配规则上。例如,一个样式可以更改使用一个方式来更改主页,而此时其它的样式段可以更改站点的其它部分。", "description": "Help text for sections" @@ -245,22 +139,6 @@ "message": "移除", "description": "Label for the button to remove an 'applies' entry" }, - "styleToMozillaFormatTitle": { - "message": "Style in Mozilla format", - "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" - }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, - "writeStyleFor": { - "message": "Write style for: ", - "description": "Label for toolbar pop-up that precedes the links to write a new style" - }, - "replace": { - "message": "Replace", - "description": "Label before the replace input field in the editor shown on Ctrl-H" - }, "appliesLabel": { "message": "应用到", "description": "Label for 'applies to' fields on the edit/add screen" @@ -270,7 +148,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "更新失败 - 服务器了返回代码 $code$.", + "message": "更新失败: 服务器了返回代码 $code$。", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,22 +164,10 @@ "message": "安装更新", "description": "Label for the button to install an update for a single style" }, - "styleMozillaFormatHeading": { - "message": "Mozilla Format", - "description": "Heading for the section with buttons to import/export Mozilla format of the style" - }, "sectionRemove": { "message": "移除节", "description": "Label for the button to remove a section" }, - "disableAllStyles": { - "message": "Turn all styles off", - "description": "Label for the checkbox that turns all enabled styles off." - }, - "undoGlobal": { - "message": "Undo (global)", - "description": "CSS-beautify global Undo button label" - }, "updateCompleted": { "message": "更新完成.", "description": "Text that displays when an update completed" @@ -314,10 +180,6 @@ "message": "代码", "description": "Label for the code for a section" }, - "cm_smartIndent": { - "message": "Use smart indentation", - "description": "Label for the checkbox controlling smart indentation option for the style editor." - }, "appliesHelp": { "message": "使用“应用到”来控制这个样式应用到哪些地址上。", "description": "Help text for 'applies to' section" @@ -334,58 +196,10 @@ "message": "添加样式", "description": "Title of the page for adding styles" }, - "importReplaceLabel": { - "message": "Overwrite style", - "description": "Label for the button to import and overwrite current style" - }, - "dbError": { - "message": "An error has occurred using the Stylus database. Would you like to visit a web page with possible solutions?", - "description": "Prompt when a DB error is encountered" - }, - "importAppendTooltip": { - "message": "Append the imported style to current style", - "description": "Tooltip for the button to import a style and append to the existing sections" - }, - "helpKeyMapHotkey": { - "message": "Press a hotkey", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, - "replaceAll": { - "message": "Replace all", - "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" - }, - "editGotoLine": { - "message": "Goto line (or line:col)", - "description": "Go to line or line:column on Ctrl-G in style code editor" - }, "checkAllUpdates": { "message": "检查所有样式的更新", "description": "Label for the button to check all styles for updates" }, - "issuesHelp": { - "message": "The issues found by CSSLint with these rules enabled:", - "description": "Help popup message for the CSSLint issues block on the style edit page" - }, - "confirmNo": { - "message": "No", - "description": "'No' button in a confirm dialog" - }, - "undo": { - "message": "Undo", - "description": "Button label" - }, - "cm_keyMap": { - "message": "Keymap", - "description": "Label for the drop-down list controlling the keymap for the style editor." - }, - "cm_indentWithTabs": { - "message": "Use tabs with smart indentation", - "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." - }, - "replaceWith": { - "message": "Replace with", - "description": "Label before the replace-with input field in the editor shown on Ctrl-H etc." - }, "deleteStyleLabel": { "message": "删除", "description": "Label for the button to delete a style" @@ -394,24 +208,12 @@ "message": "编写新的样式", "description": "Label for the button to go to the add style page" }, - "manageOnlyEnabled": { - "message": "Only enabled styles", - "description": "Checkbox to show only enabled styles" - }, "editStyleLabel": { "message": "编辑", "description": "Label for the button to go to the edit style page" }, - "cm_theme": { - "message": "Theme", - "description": "Label for the style editor's CSS theme." - }, - "helpKeyMapCommand": { - "message": "Type a command name", - "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" - }, "description": { "message": "Stylus,一个用户样式管理器,帮助您重新定义网页样式。Stylus易于为Google,Facebook,Youtube,Orkut以及其它各类型网站安装样式和主题。", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/zh_CN/messages.json b/_locales/zh_CN/messages.json index 03265a04..b3f92c35 100644 --- a/_locales/zh_CN/messages.json +++ b/_locales/zh_CN/messages.json @@ -19,6 +19,10 @@ "message": "每 N 小时,检查所有样式更新(0 为关闭检查)", "description": "" }, + "styleEnabledToggleHint": { + "message": "按 Alt + Enter 切换启用/禁用状态并保存样式", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "导出", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -71,6 +75,10 @@ "message": "部分正则表达式无法生效。", "description": "" }, + "optionsIconDark": { + "message": "暗色浏览器主题", + "description": "" + }, "importAppendLabel": { "message": "追加到样式", "description": "Label for the button to import a style and append to the existing sections" @@ -116,7 +124,7 @@ "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "查找适合此网站的更多样式。", + "message": "查找适合此网站的更多样式", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { @@ -269,7 +277,7 @@ "description": "Go to shortcut configuration" }, "updateCheckFailServerUnreachable": { - "message": "更新失败 - 无法访问服务器。", + "message": "更新失败: 无法访问服务器。", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -313,6 +321,10 @@ } } }, + "optionsIconLight": { + "message": "亮色浏览器主题", + "description": "" + }, "openStylesManager": { "message": "打开样式管理器", "description": "Label for the style maanger opener in the browser action context menu." @@ -371,6 +383,10 @@ "message": "放弃当前样式的内容,使用已导入样式覆盖它", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "双击来最大化/恢复高度", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { "message": "样式列表置顶", "description": "Label for the checkbox controlling section order in the popup." @@ -399,10 +415,6 @@ "message": "Mozilla 格式的样式", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "编写样式给:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -432,7 +444,7 @@ "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "更新失败 - 服务器响应代码为 $code$。", + "message": "更新失败: 服务器响应代码为 $code$。", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -468,6 +480,10 @@ "message": "这个样式有一部分正则表达式与 CSS4 @document specification完整的 URL 不匹配。这部分样式将被忽略,可能这个样式是基于 Stylish 编写的,请尝试在 Stylus 重新编写该样式。", "description": "" }, + "styleBeautifyIndentConditional": { + "message": "缩进 @media、@supports", + "description": "CSS-beautifier option" + }, "unreachableContentScript": { "message": "无法读取页面,请尝试刷新。", "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" @@ -488,6 +504,10 @@ "message": "撤销 (全局)", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "切换图标", + "description": "" + }, "updateCompleted": { "message": "更新已完成。", "description": "Text that displays when an update completed" @@ -548,6 +568,14 @@ "message": "删除", "description": "Label for the context menu item in the editor to delete selected text" }, + "cm_autocompleteOnTyping": { + "message": "输入时自动完成", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "出于安全原因,浏览器禁止扩展影响内置页面(例如 chrome://version 或 about:addons)以及其他扩展的页面。各浏览器同时也限制介入自己的扩展网站(如 Chrome 网上应用店、Firefox 附加组件)。", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, "cm_matchHighlightSelection": { "message": "选择区域", "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" @@ -732,4 +760,4 @@ "message": "Stylus 是一个调整网页外观的用户样式管理器。它可让您轻松地为许多热门网站网站安装主题和皮肤。", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/_locales/zh_TW/messages.json b/_locales/zh_TW/messages.json index e326dfc8..cead174b 100644 --- a/_locales/zh_TW/messages.json +++ b/_locales/zh_TW/messages.json @@ -7,6 +7,22 @@ "message": "默認", "description": "Default CodeMirror CSS theme option on the edit style page" }, + "styleRegexpTestTitle": { + "message": "符合的已開啟分頁清單(在 URL 上點選以將焦點轉移至它的分頁)", + "description": "RegExp test report: title of the report" + }, + "bckpInstStyles": { + "message": "匯出樣式", + "description": "" + }, + "optionsUpdateInterval": { + "message": "自動檢查並安裝可用的使用者樣式更新(以小時計)", + "description": "" + }, + "styleEnabledToggleHint": { + "message": "按下 Alt + Enter 以切換啟用/停用狀態並儲存樣式", + "description": "Help text for the '[x] enable' checkbox in the editor" + }, "exportLabel": { "message": "導出", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -15,6 +31,14 @@ "message": "問題", "description": "Label for the CSSLint issues block on the style edit page" }, + "optionsBadgeNormal": { + "message": "背景顏色", + "description": "" + }, + "updateCheckHistory": { + "message": "更新檢查的歷史", + "description": "" + }, "cm_tabSize": { "message": "Tab大小", "description": "Label for the text box controlling tab size option for the style editor." @@ -27,6 +51,14 @@ "message": "請輸入名稱。", "description": "Error displayed when user saves without providing a name" }, + "genericHistoryLabel": { + "message": "歷史", + "description": "Used in various places to show a history log of something" + }, + "shortcutsNote": { + "message": "定義鍵盤快捷鍵", + "description": "" + }, "appliesDomainOption": { "message": "該域名上的網址", "description": "Option to make the style apply to the entered string as a domain" @@ -35,32 +67,64 @@ "message": "檢查更新", "description": "Label for the button to check a single style for an update" }, + "styleNotAppliedRegexpProblemTooltip": { + "message": "因為不正確的 'regexp()' 使用導致未套用", + "description": "Tooltip in the popup for styles that were not applied at all" + }, + "styleRegexpInvalidExplanation": { + "message": "部份 'regexp()' 歸則可能不再能被編譯。", + "description": "" + }, + "optionsIconDark": { + "message": "暗色瀏覽器主題", + "description": "" + }, "importAppendLabel": { "message": "追加到樣式", "description": "Label for the button to import a style and append to the existing sections" }, + "optionsOpenManager": { + "message": "管理樣式", + "description": "" + }, "updateAllCheckSucceededNoUpdate": { "message": "樣式都是最新的。", "description": "Text that displays when an update all check completed and no updates are available" }, + "importReportLegendAdded": { + "message": "已加入", + "description": "Text after the number of styles added in the report shown after importing styles" + }, "styleFromMozillaFormatPrompt": { "message": "粘貼Mozilla格式代碼", "description": "Prompt in the dialog displayed after clicking 'Import from Mozilla format' button" }, + "dragDropMessage": { + "message": "將您的備份檔拖曳到此頁面的任何地方以匯入。", + "description": "Drag'n'drop message" + }, "helpAlt": { "message": "幫助", "description": "Alternate text for help buttons" }, + "confirmOK": { + "message": "確定", + "description": "" + }, "search": { "message": "搜索", "description": "Label before the search input field in the editor shown on Ctrl-F" }, + "manageFaviconsGray": { + "message": "灰階淡出", + "description": "Label for the checkbox that toggles grayed out mode of applies-to favicons in the new UI on manage page" + }, "confirmYes": { "message": "是", "description": "'Yes' button in a confirm dialog" }, "findStylesForSite": { - "message": "查找更多適合此網站的樣式。", + "message": "查找更多適合此網站的樣式", "description": "Text for a link that gets a list of styles for the current site" }, "manageHeading": { @@ -71,6 +135,10 @@ "message": "美化", "description": "Label for the CSS-beautifier button on the edit style page" }, + "styleRegexpProblemTooltip": { + "message": "因為不正確的 'regexp()' 使用導致未套用的樣式段數量", + "description": "Tooltip in the popup for styles that were applied only partially" + }, "styleEnabledLabel": { "message": "已啟用", "description": "Label for the enabled state of styles" @@ -83,10 +151,26 @@ "message": "再添加一個樣式段", "description": "Label for the button to add a section" }, + "styleRegexpTestPartial": { + "message": "不完全符合,因而跳過", + "description": "RegExp test report: label for the partially matching expressions" + }, "styleSaveLabel": { "message": "保存", "description": "Label for save button for style editing" }, + "confirmDelete": { + "message": "刪除", + "description": "" + }, + "confirmCancel": { + "message": "取消", + "description": "" + }, + "retrieveBckp": { + "message": "匯入樣式", + "description": "" + }, "confirmStop": { "message": "停止", "description": "'Stop' button in a confirm dialog" @@ -95,6 +179,10 @@ "message": "此網址", "description": "Text for link in toolbar pop-up to write a new style for the current URL" }, + "optionsSubheading": { + "message": "更多選項", + "description": "Subheading for options section on manage page." + }, "appliesAdd": { "message": "添加", "description": "Label for the button to add an 'applies' entry" @@ -103,6 +191,14 @@ "message": "匹配正則表示式的網址", "description": "Option to make the style apply to the entered string as a regular expression" }, + "optionsAdvancedExposeIframesNote": { + "message": "啟用寫入特定 iframe 的 CSS,如 'html[stylus-iframe] h1 { display:none }'", + "description": "" + }, + "importReportLegendUpdatedCode": { + "message": "已更新的程式碼", + "description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles" + }, "styleInstall": { "message": "安裝 '$stylename$' 到 Stylus ?", "description": "Confirmation when installing a style", @@ -112,14 +208,38 @@ } } }, + "optionsBadgeDisabled": { + "message": "停用時的背景顏色", + "description": "" + }, + "optionsCheck": { + "message": "更新樣式", + "description": "" + }, "manageText": { - "message": "Get styles on userstyles.org | Get help", + "message": "在 userstyles.org 上取得樣式 | 取得協助", "description": "Help text on the manage page" }, + "manageOnlyLocal": { + "message": "僅本機建立的樣式", + "description": "Checkbox to show only locally created styles i.e. non-updatable" + }, "searchStyles": { "message": "搜索內容", "description": "Label for the search filter textbox on the Manage styles page" }, + "optionsUpdateImportNote": { + "message": "當從舊版本或是從 Stylish 匯入樣式備份時,在樣式管理員中手動進行一次更新檢查,以確保所有樣式都被更新。", + "description": "" + }, + "checkAllUpdatesForce": { + "message": "再次檢查,我沒有編輯任何樣式!", + "description": "Label for the button to apply all detected updates" + }, + "unreachableFileHint": { + "message": "Stylus 僅在您於 chrome://extensions 啟用了 Stylus 擴充套件中對應的勾選框時才能存取 file:// 的 URL。", + "description": "Note in the toolbar popup for file:// URLs" + }, "disableStyleLabel": { "message": "禁用", "description": "Label for the button to disable a style" @@ -128,6 +248,10 @@ "message": "在工具欄按鈕上顯示當前網站已生效的樣式表數目。", "description": "Label for the checkbox controlling toolbar badge text." }, + "manageFavicons": { + "message": "Favicons 要套用到的欄位", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, "menuShowBadge": { "message": "顯示生效的樣式數目", "description": "Label (must be very short) for the checkbox in the toolbar button context menu controlling toolbar badge text." @@ -148,8 +272,12 @@ "message": "導入", "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" }, + "shortcuts": { + "message": "快捷鍵", + "description": "Go to shortcut configuration" + }, "updateCheckFailServerUnreachable": { - "message": "更新失敗 – 無法訪問伺服器。", + "message": "更新失敗: 無法訪問伺服器。", "description": "Text that displays when an update check failed because the update server is unreachable" }, "manageFilters": { @@ -160,15 +288,21 @@ "message": "應用所有更新", "description": "Label for the button to apply all detected updates" }, + "optionsReset": { + "message": "重設選項為預設值", + "description": "" + }, + "optionsCustomizeUpdate": { + "message": "更新", + "description": "" + }, "deleteStyleConfirm": { "message": "確定要刪除這個樣式嗎?", "description": "Confirmation before deleting a style" }, - "confirmDelete": { - "message": "Delete" - }, - "confirmCancel": { - "message": "Cancel" + "optionsCustomizePopup": { + "message": "彈出式視窗", + "description": "" }, "styleBadRegexp": { "message": "正規表示式無效。", @@ -187,6 +321,14 @@ } } }, + "optionsIconLight": { + "message": "亮色瀏覽器主題", + "description": "" + }, + "openStylesManager": { + "message": "開啟樣式管理員", + "description": "Label for the style maanger opener in the browser action context menu." + }, "styleUpdate": { "message": "你確定你要更新 '$stylename$' ?", "description": "Confirmation when updating a style", @@ -200,6 +342,10 @@ "message": "樣式段", "description": "Title for the style sections section" }, + "importReportLegendInvalid": { + "message": "已跳過無效的", + "description": "Text after the number of styles skipped due to being invalid (not a Stylus/Stylish backup file probably) in the report shown after importing styles" + }, "editStyleTitle": { "message": "編輯樣式 $stylename$", "description": "Title of the page for editing styles", @@ -217,17 +363,33 @@ "message": "含以下前綴的網址", "description": "Option to make the style apply to the entered string as a URL prefix" }, + "cm_matchHighlightToken": { + "message": "在游標下的 Token", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of of the word/token under cursor even if nothing is selected" + }, "searchRegexp": { "message": "使用/re/句法正則表達式搜索", "description": "Label after the search input field in the editor shown on Ctrl-F" }, + "updateCheckManualUpdateHint": { + "message": "強制更新覆蓋任何本機編輯。", + "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" + }, + "toggleStyle": { + "message": "切換樣式", + "description": "Label for the checkbox to enable/disable a style" + }, "importReplaceTooltip": { "message": "棄用當前樣式內容并用導入樣式覆蓋", "description": "Label for the button to import and overwrite current style" }, + "cm_resizeGripHint": { + "message": "雙擊以最大化/復原高度", + "description": "Tooltip for the resize grip in style editor" + }, "popupStylesFirst": { "message": "在工具欄按鈕命令行前列出樣式", - "description": "Label for the checkbox controlling section order in the toolbar button menu." + "description": "Label for the checkbox controlling section order in the popup." }, "sectionHelp": { "message": "樣式段讓你能在同一個樣式中定義適用於不同網段的不同代碼段。例如,僅僅一個樣式就能以一種樣式改變一個網站的主頁,同時以另一種樣式改變這個網站的其他頁面。", @@ -245,14 +407,14 @@ "message": "移除", "description": "Label for the button to remove an 'applies' entry" }, + "updatesCurrentlyInstalled": { + "message": "更新已安裝的:", + "description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates." + }, "styleToMozillaFormatTitle": { "message": "Mozilla格式樣式表", "description": "Title of the popup with the style code in Mozilla format, shown after pressing the Export button on Edit style page" }, - "manageTitle": { - "message": "Stylus", - "description": "Title for the manage page" - }, "writeStyleFor": { "message": "編寫樣式給:", "description": "Label for toolbar pop-up that precedes the links to write a new style" @@ -261,16 +423,28 @@ "message": "替換", "description": "Label before the replace input field in the editor shown on Ctrl-H" }, + "styleRegexpTestNone": { + "message": "沒有符合的分頁", + "description": "RegExp test report: label for expressions that didn't match any tabs" + }, + "importReportLegendUpdatedMeta": { + "message": "已更新後設資訊", + "description": "Text after the number of styles with updated meta info like name/url in the report shown after importing styles" + }, "appliesLabel": { "message": "適用於", "description": "Label for 'applies to' fields on the edit/add screen" }, + "openOptionsPopup": { + "message": "選項", + "description": "Go to Options UI" + }, "openManage": { "message": "管理已安裝樣式", "description": "Link to open the manage page." }, "updateCheckFailBadResponseCode": { - "message": "更新失敗 – 伺服器傳回代碼 $code$ 。", + "message": "更新失敗: 伺服器傳回代碼 $code$。", "description": "Text that displays when an update check failed because the response code indicates an error", "placeholders": { "code": { @@ -286,13 +460,33 @@ "message": "安裝更新", "description": "Label for the button to install an update for a single style" }, + "optionsCheckUpdate": { + "message": "檢查並安裝所有可用的更新", + "description": "" + }, + "importReportTitle": { + "message": "匯入樣式已完成", + "description": "Title of the report shown after importing styles" + }, "styleMozillaFormatHeading": { "message": "Mozilla格式", "description": "Heading for the section with buttons to import/export Mozilla format of the style" }, - "stylusUnavailableForURL": { - "message": "Stylus 不能在諸如此類的網頁上生效。", - "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + "cm_matchHighlight": { + "message": "突顯", + "description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor." + }, + "styleRegexpPartialExplanation": { + "message": "這個樣式使用部份符合的正規表達式,違反了 CSS4 @document 規範,其需要完整的 URL 符合。授影響的 CSS 不會討用在頁面上。這個樣式由 Chrome 板 Stylish 所建立,其在早期版本不正確地檢查 'regexp()' 規則(已知的臭蟲)。", + "description": "" + }, + "styleBeautifyIndentConditional": { + "message": "縮排 @media, @supports", + "description": "CSS-beautifier option" + }, + "unreachableContentScript": { + "message": "無法與頁面通訊。嘗試重新載入分頁。", + "description": "Note in the toolbar popup usually on file:// URLs after [re]loading Stylus" }, "sectionRemove": { "message": "移除樣式段", @@ -302,10 +496,18 @@ "message": "禁用所有樣式", "description": "Label for the checkbox that turns all enabled styles off." }, + "updateCheckSkippedMaybeLocallyEdited": { + "message": "這個樣式可能在本機被編輯過。", + "description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications" + }, "undoGlobal": { "message": "撤銷(全局)", "description": "CSS-beautify global Undo button label" }, + "optionsCustomizeIcon": { + "message": "工具列圖示", + "description": "" + }, "updateCompleted": { "message": "更新完成。", "description": "Text that displays when an update completed" @@ -314,14 +516,46 @@ "message": "檢查中…", "description": "Text to display when checking a style for an update" }, + "styleRegexpTestFull": { + "message": "符合的分頁", + "description": "RegExp test report: label for the fully matching expressions" + }, + "manageMaxTargets": { + "message": "已套用項目的數量", + "description": "Label for the numeric input box to limit max number of applies-to targets in the new UI on manage page" + }, + "manageFaviconsHelp": { + "message": "Stylus 使用外部服務 https://www.google.com/s2/favicons", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, + "updateCheckSkippedLocallyEdited": { + "message": "這個樣式已在本機編輯。", + "description": "Text that displays when an update check skipped updating the style to avoid losing local modifications" + }, + "optionsResetButton": { + "message": "重設選項", + "description": "" + }, "sectionCode": { "message": "代碼", "description": "Label for the code for a section" }, + "optionsAdvancedContextDelete": { + "message": "在編輯器的右鍵選單中加入「刪除」", + "description": "" + }, + "importReportLegendUpdatedBoth": { + "message": "後設資訊與程式碼均已更新", + "description": "Text after the number of styles updated entirely in the report shown after importing styles" + }, "cm_smartIndent": { "message": "使用智能縮進", "description": "Label for the checkbox controlling smart indentation option for the style editor." }, + "styleRegexpTestButton": { + "message": "正規表達式測試", + "description": "RegExp test button label in the editor shown when applies-to list has a regexp value" + }, "appliesHelp": { "message": "使用「適用於」控件來限制這個樣式段適用的網址。", "description": "Help text for 'applies to' section" @@ -330,10 +564,38 @@ "message": "編輯樣式", "description": "Title of the page for editing styles" }, + "editDeleteText": { + "message": "删除", + "description": "Label for the context menu item in the editor to delete selected text" + }, + "cm_autocompleteOnTyping": { + "message": "在輸入時自動完成", + "description": "Label for the checkbox in the style editor." + }, + "stylusUnavailableForURLdetails": { + "message": "做為安全措施,瀏覽器禁止擴充功能影響其內建頁面(像是 chrome://version 或是 about:addons)與其他擴充功能頁面。每個瀏覽器都會限制只能在其擴充功能商店上安裝擴充功能(如 Chrome 線上應用程式商店或 AMO)。", + "description": "Sub-note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "cm_matchHighlightSelection": { + "message": "僅段", + "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" + }, "appliesUrlOption": { "message": "網址", "description": "Option to make the style apply to the entered string as a URL" }, + "updateAllCheckSucceededSomeEdited": { + "message": "一些可更新的樣式不會檢查以避免遺失可能有的本機編輯。更新可以強制獨立檢查,或是對所有樣式都執行檢查(本機編輯將會被覆寫)。", + "description": "Text that displays when an update all check completed and no updates are available" + }, + "stylusUnavailableForURL": { + "message": "Stylus 不能在諸如此類的網頁上生效。", + "description": "Note in the toolbar pop-up when on a URL Stylus can't affect" + }, + "manageOnlyUpdates": { + "message": "僅有更新或是有問題的", + "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed" + }, "addStyleTitle": { "message": "添加樣式", "description": "Title of the page for adding styles" @@ -346,30 +608,74 @@ "message": "使用 Stylus 資料庫時出現錯誤。你想訪問可能包含解決辦法的網頁嗎?", "description": "Prompt when a DB error is encountered" }, + "optionsAdvanced": { + "message": "進階", + "description": "" + }, "importAppendTooltip": { "message": "追加導入的樣式到當前樣式", "description": "Tooltip for the button to import a style and append to the existing sections" }, + "editorStylesButton": { + "message": "找到編輯器樣式", + "description": "Find styles for the editor" + }, + "optionsOpen": { + "message": "開啟", + "description": "" + }, "helpKeyMapHotkey": { "message": "按下熱鍵", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" }, + "styleRegexpTestInvalid": { + "message": "已跳過無效的正規表達式", + "description": "RegExp test report: label for the invalid expressions" + }, "replaceAll": { "message": "替換所有", "description": "Label before the replace input field in the editor shown on 'replaceAll' hotkey" }, + "importReportUnchanged": { + "message": "沒有變更。", + "description": "Message in the report shown after importing styles" + }, + "optionsActions": { + "message": "動作", + "description": "" + }, "editGotoLine": { "message": "跳到行(或 line:col )", "description": "Go to line or line:column on Ctrl-G in style code editor" }, + "manageOnlyLocalTooltip": { + "message": "(不是透過 userstyles.org 網頁安裝的樣式)", + "description": "Tooltip for the checkbox to show only locally created styles i.e. non-updatable" + }, "checkAllUpdates": { "message": "檢查所有樣式更新", "description": "Label for the button to check all styles for updates" }, + "openOptionsManage": { + "message": "選項介面", + "description": "Go to Options UI" + }, "issuesHelp": { "message": "由CSSLint發現啟用這些規則會產生衝突", "description": "Help popup message for the CSSLint issues block on the style edit page" }, + "optionsCustomizeBadge": { + "message": "在工具列圖示上的徽章", + "description": "" + }, + "importReportLegendIdentical": { + "message": "已跳過相同的", + "description": "Text after the number of styles skipped due to being identical to the already installed ones in the report shown after importing styles" + }, + "optionsPopupWidth": { + "message": "彈出視窗寬度(以像素計)", + "description": "" + }, "confirmNo": { "message": "否", "description": "'No' button in a confirm dialog" @@ -382,6 +688,18 @@ "message": "鍵盤映射", "description": "Label for the drop-down list controlling the keymap for the style editor." }, + "manageNewUI": { + "message": "新的管理介面佈局", + "description": "Label for the checkbox that toggles the new UI on manage page" + }, + "importReportUndoneTitle": { + "message": "匯入已被復原", + "description": "Title of the message box shown after undoing the import of styles" + }, + "genericDisabledLabel": { + "message": "已停用", + "description": "Used in various lists/options to indicate that something is disabled" + }, "cm_indentWithTabs": { "message": "用Tab鍵智能縮進", "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." @@ -394,10 +712,26 @@ "message": "删除", "description": "Label for the button to delete a style" }, + "updateCheckManualUpdateForce": { + "message": "安裝更新(本機編輯將會被覆寫)", + "description": "Additional text displayed when an update check skipped updating the style to avoid losing local modifications" + }, + "optionsAdvancedExposeIframes": { + "message": "透過 HTML[stylus-iframe] 公開 iframes", + "description": "" + }, "addStyleLabel": { "message": "編寫新樣式", "description": "Label for the button to go to the add style page" }, + "optionsUpdateIntervalNote": { + "message": "要停用自動化使用者樣式更新檢查,設定間隔為 0", + "description": "" + }, + "backupButtons": { + "message": "備份", + "description": "Heading for backup" + }, "manageOnlyEnabled": { "message": "只顯示已啟用的樣式", "description": "Checkbox to show only enabled styles" @@ -410,6 +744,14 @@ "message": "主題", "description": "Label for the style editor's CSS theme." }, + "backupMessage": { + "message": "選取檔案並拖曳到此頁面。", + "description": "Message for backup" + }, + "importReportUndone": { + "message": "樣式已被復原", + "description": "Text after the number of styles reverted in the message box shown after undoing the import of styles" + }, "helpKeyMapCommand": { "message": "鍵入命令名稱", "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" @@ -418,4 +760,4 @@ "message": "用Stylus(一個用戶樣式管理器)重塑網頁。 Stylus 讓你能為诸多主流網站輕鬆的安裝主題和皮膚。", "description": "Extension description" } -} +} \ No newline at end of file diff --git a/background.js b/background/background.js similarity index 82% rename from background.js rename to background/background.js index 0d80d7ce..13d12e2f 100644 --- a/background.js +++ b/background/background.js @@ -26,14 +26,14 @@ chrome.webNavigation.onHistoryStateUpdated.addListener(data => chrome.webNavigation.onReferenceFragmentUpdated.addListener(data => webNavigationListener('styleReplaceAll', data)); -chrome.tabs.onAttached.addListener((tabId, data) => { +chrome.tabs.onAttached.addListener(tabId => { // When an edit page gets attached or detached, remember its state // so we can do the same to the next one to open. chrome.tabs.get(tabId, tab => { if (tab.url.startsWith(URLS.ownOrigin + 'edit.html')) { chrome.windows.get(tab.windowId, {populate: true}, win => { // If there's only one tab in this window, it's been dragged to new window - prefs.set('openEditInWindow', win.tabs.length == 1); + prefs.set('openEditInWindow', win.tabs.length === 1); }); } }); @@ -47,6 +47,11 @@ if ('commands' in chrome) { chrome.commands.onCommand.addListener(command => browserCommands[command]()); } +// ************************************************************************* +// set the default icon displayed after a tab is created until webNavigation kicks in +prefs.subscribe(() => updateIcon({id: undefined}, {}), ['iconset']); +updateIcon({id: undefined}, {}); + // ************************************************************************* { const onInstall = ({reason}) => { @@ -54,17 +59,15 @@ if ('commands' in chrome) { const manifest = chrome.runtime.getManifest(); // Open FAQs page once after installation to guide new users. // Do not display it in development mode. - if (reason == 'install' && manifest.update_url) { + if (reason === 'install' && manifest.update_url) { setTimeout(openURL, 100, { - url: `http://add0n.com/stylus.html?version=${manifest.version}&type=install` + url: 'http://add0n.com/stylus.html' }); } - // reset L10N cache on UI language change or update - const {browserUIlanguage} = tryJSONparse(localStorage.L10N) || {}; - const UIlang = chrome.i18n.getUILanguage(); - if (reason == 'update' || browserUIlanguage != UIlang) { + // reset L10N cache on update + if (reason === 'update') { localStorage.L10N = JSON.stringify({ - browserUIlanguage: UIlang, + browserUIlanguage: chrome.i18n.getUILanguage(), }); } // TODO: remove in the future @@ -95,7 +98,7 @@ if ('commands' in chrome) { // browser commands browserCommands = { openManage() { - openURL({url: '/manage.html'}); + openURL({url: 'manage.html'}); }, styleDisableAll(info) { prefs.set('disableAll', info ? info.checked : !prefs.get('disableAll')); @@ -135,7 +138,7 @@ contextMenus = Object.assign({ const item = Object.assign({id}, contextMenus[id]); const prefValue = prefs.readOnlyValues[id]; item.title = chrome.i18n.getMessage(item.title); - if (!item.type && typeof prefValue == 'boolean') { + if (!item.type && typeof prefValue === 'boolean') { item.type = 'checkbox'; item.checked = prefValue; } @@ -148,7 +151,7 @@ contextMenus = Object.assign({ }; createContextMenus(); prefs.subscribe((id, checked) => { - if (id == 'editor.contextDelete') { + if (id === 'editor.contextDelete') { if (checked) { createContextMenus([id]); } else { @@ -157,7 +160,7 @@ contextMenus = Object.assign({ } else { chrome.contextMenus.update(id, {checked}, ignoreChromeError); } - }, Object.keys(contextMenus).filter(key => typeof prefs.readOnlyValues[key] == 'boolean')); + }, Object.keys(contextMenus).filter(key => typeof prefs.readOnlyValues[key] === 'boolean')); } // ************************************************************************* @@ -173,7 +176,7 @@ contextMenus = Object.assign({ .replace(/\*/g, '.*?'), flags); for (const cs of contentScripts) { cs.matches = cs.matches.map(m => ( - m == ALL_URLS ? m : wildcardAsRegExp(m) + m === ALL_URLS ? m : wildcardAsRegExp(m) )); } @@ -188,18 +191,27 @@ contextMenus = Object.assign({ const pingCS = (cs, {id, url}) => { cs.matches.some(match => { - if ((match == ALL_URLS || url.match(match)) - && (!url.startsWith('chrome') || url == NTP)) { - chrome.tabs.sendMessage(id, PING, pong => !pong && injectCS(cs, id)); + if ((match === ALL_URLS || url.match(match)) + && (!url.startsWith('chrome') || url === NTP)) { + chrome.tabs.sendMessage(id, PING, pong => { + if (!pong) { + injectCS(cs, id); + } + ignoreChromeError(); + }); return true; } }); }; - chrome.tabs.query({}, tabs => - tabs.forEach(tab => - contentScripts.forEach(cs => - pingCS(cs, tab)))); + queryTabs().then(tabs => + tabs.forEach(tab => { + // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF + if (!FIREFOX || tab.width) { + contentScripts.forEach(cs => + setTimeout(pingCS, 0, cs, tab)); + } + })); } @@ -217,7 +229,7 @@ function webNavigationListener(method, {url, tabId, frameId}) { }); } // main page frame id is 0 - if (frameId == 0) { + if (frameId === 0) { updateIcon({id: tabId, url}, styles); } }); @@ -246,22 +258,24 @@ function updateIcon(tab, styles) { } } const disableAll = 'disableAll' in styles ? styles.disableAll : prefs.get('disableAll'); - const postfix = disableAll ? 'x' : numStyles == 0 ? 'w' : ''; + const postfix = disableAll ? 'x' : numStyles === 0 ? 'w' : ''; const color = prefs.get(disableAll ? 'badgeDisabled' : 'badgeNormal'); const text = prefs.get('show-badge') && numStyles ? String(numStyles) : ''; + const iconset = ['', 'light/'][prefs.get('iconset')] || ''; + const path = 'images/icon/' + iconset; chrome.browserAction.setIcon({ tabId: tab.id, path: { // Material Design 2016 new size is 16px - 16: `images/icon/16${postfix}.png`, - 32: `images/icon/32${postfix}.png`, + 16: `${path}16${postfix}.png`, + 32: `${path}32${postfix}.png`, // Chromium forks or non-chromium browsers may still use the traditional 19px - 19: `images/icon/19${postfix}.png`, - 38: `images/icon/38${postfix}.png`, + 19: `${path}19${postfix}.png`, + 38: `${path}38${postfix}.png`, // TODO: add Edge preferred sizes: 20, 25, 30, 40 }, }, () => { - if (chrome.runtime.lastError) { + if (chrome.runtime.lastError || tab.id === undefined) { return; } // Vivaldi bug workaround: setBadgeText must follow setBadgeBackgroundColor @@ -276,7 +290,6 @@ function updateIcon(tab, styles) { function onRuntimeMessage(request, sender, sendResponse) { switch (request.method) { - case 'getStyles': getStyles(request).then(sendResponse); return KEEP_CHANNEL_OPEN; diff --git a/storage.js b/background/storage.js similarity index 85% rename from storage.js rename to background/storage.js index 3ffb020f..7531f0fb 100644 --- a/storage.js +++ b/background/storage.js @@ -4,7 +4,8 @@ const RX_NAMESPACE = new RegExp([/[\s\r\n]*/, /(@namespace[\s\r\n]+(?:[^\s\r\n]+[\s\r\n]+)?url\(http:\/\/.*?\);)/, /[\s\r\n]*/].map(rx => rx.source).join(''), 'g'); const RX_CSS_COMMENTS = /\/\*[\s\S]*?\*\//g; -const SLOPPY_REGEXP_PREFIX = '\0'; +// eslint-disable-next-line no-var +var SLOPPY_REGEXP_PREFIX = '\0'; // Note, only 'var'-declared variables are visible from another extension page // eslint-disable-next-line no-var @@ -62,7 +63,7 @@ function dbExec(method, data) { reject(event); }, onupgradeneeded(event) { - if (event.oldVersion == 0) { + if (event.oldVersion === 0) { event.target.result.createObjectStore('styles', { keyPath: 'id', autoIncrement: true, @@ -88,13 +89,8 @@ function getStyles(options) { return dbExec('getAll').then(event => { cachedStyles.list = event.target.result || []; cachedStyles.byId.clear(); - const t0 = performance.now(); - let hasTimeToCompile = true; for (const style of cachedStyles.list) { cachedStyles.byId.set(style.id, style); - if (hasTimeToCompile) { - hasTimeToCompile = !compileStyleRegExps({style}) || performance.now() - t0 > 100; - } } cachedStyles.mutex.inProgress = false; @@ -115,15 +111,17 @@ function filterStyles({ asHash = null, strictRegexp = true, // used by the popup to detect bad regexps } = {}) { - enabled = enabled === null || typeof enabled == 'boolean' ? enabled : - typeof enabled == 'string' ? enabled == 'true' : null; + enabled = enabled === null || typeof enabled === 'boolean' ? enabled : + typeof enabled === 'string' ? enabled === 'true' : null; id = id === null ? null : Number(id); - if (enabled === null - && url === null - && id === null - && matchUrl === null - && asHash != true) { + if ( + enabled === null && + url === null && + id === null && + matchUrl === null && + asHash !== true + ) { return cachedStyles.list; } const blankHash = asHash && { @@ -193,10 +191,11 @@ function filterStylesInternal({ const needSections = asHash || matchUrl !== null; - for (let i = 0, style; (style = styles[i]); i++) { - if ((enabled === null || style.enabled == enabled) - && (url === null || style.url == url) - && (id === null || style.id == id)) { + let style; + for (let i = 0; (style = styles[i]); i++) { + if ((enabled === null || style.enabled === enabled) + && (url === null || style.url === url) + && (id === null || style.id === id)) { const sections = needSections && getApplicableSections({style, matchUrl, strictRegexp, stopOnFirst: !asHash}); if (asHash) { @@ -234,17 +233,18 @@ function saveStyle(style) { if (!style.name) { delete style.name; } - let existed, codeIsUpdated; - if (reason == 'update' || reason == 'update-digest') { + let existed; + let codeIsUpdated; + if (reason === 'update' || reason === 'update-digest') { return calcStyleDigest(style).then(digest => { style.originalDigest = digest; return decide(); }); } - if (reason == 'import') { + if (reason === 'import') { style.originalDigest = style.originalDigest || style.styleDigest; // TODO: remove in the future delete style.styleDigest; // TODO: remove in the future - if (typeof style.originalDigest != 'string' || style.originalDigest.length != 40) { + if (typeof style.originalDigest !== 'string' || style.originalDigest.length !== 40) { delete style.originalDigest; } } @@ -257,10 +257,10 @@ function saveStyle(style) { return dbExec('get', id).then((event, store) => { const oldStyle = event.target.result; existed = Boolean(oldStyle); - if (reason == 'update-digest' && oldStyle.originalDigest == style.originalDigest) { + if (reason === 'update-digest' && oldStyle.originalDigest === style.originalDigest) { return style; } - codeIsUpdated = !existed || style.sections && !styleSectionsEqual(style, oldStyle); + codeIsUpdated = !existed || 'sections' in style && !styleSectionsEqual(style, oldStyle); style = Object.assign({}, oldStyle, style); return write(style, store); }); @@ -291,12 +291,11 @@ function saveStyle(style) { } function done(event) { - if (reason == 'update-digest') { + if (reason === 'update-digest') { return style; } style.id = style.id || event.target.result; invalidateCache(existed ? {updated: style} : {added: style}); - compileStyleRegExps({style}); if (notify) { notifyAllTabs({ method: existed ? 'styleUpdated' : 'styleAdded', @@ -370,14 +369,14 @@ function getApplicableSections({style, matchUrl, strictRegexp = true, stopOnFirs function arraySomeMatches(array, matchUrl, strictRegexp) { for (const regexp of array) { for (let pass = 1; pass <= (strictRegexp ? 1 : 2); pass++) { - const cacheKey = pass == 1 ? regexp : SLOPPY_REGEXP_PREFIX + regexp; + const cacheKey = pass === 1 ? regexp : SLOPPY_REGEXP_PREFIX + regexp; let rx = cachedStyles.regexps.get(cacheKey); - if (rx == false) { + if (rx === false) { // invalid regexp break; } if (!rx) { - const anchored = pass == 1 ? '^(?:' + regexp + ')$' : '^' + regexp + '$'; + const anchored = pass === 1 ? '^(?:' + regexp + ')$' : '^' + regexp + '$'; rx = tryRegExp(anchored); cachedStyles.regexps.set(cacheKey, rx || false); if (!rx) { @@ -418,7 +417,7 @@ function styleSectionsEqual({sections: a}, {sections: b}) { if (!a || !b) { return undefined; } - if (a.length != b.length) { + if (a.length !== b.length) { return false; } const checkedInB = []; @@ -435,16 +434,16 @@ function styleSectionsEqual({sections: a}, {sections: b}) { return false; } } - return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a == b); + return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b); } function equalOrEmpty(a, b, telltale, comparator) { - const typeA = a && typeof a[telltale] == 'function'; - const typeB = b && typeof b[telltale] == 'function'; + const typeA = a && typeof a[telltale] === 'function'; + const typeB = b && typeof b[telltale] === 'function'; return ( (a === null || a === undefined || (typeA && !a.length)) && (b === null || b === undefined || (typeB && !b.length)) - ) || typeA && typeB && a.length == b.length && comparator(a, b); + ) || typeA && typeB && a.length === b.length && comparator(a, b); } function arrayMirrors(array1, array2) { @@ -463,29 +462,6 @@ function styleSectionsEqual({sections: a}, {sections: b}) { } -function compileStyleRegExps({style, compileAll}) { - const t0 = performance.now(); - for (const section of style.sections || []) { - for (const regexp of section.regexps) { - for (let pass = 1; pass <= (compileAll ? 2 : 1); pass++) { - const cacheKey = pass == 1 ? regexp : SLOPPY_REGEXP_PREFIX + regexp; - if (cachedStyles.regexps.has(cacheKey)) { - continue; - } - // according to CSS4 @document specification the entire URL must match - const anchored = pass == 1 ? '^(?:' + regexp + ')$' : '^' + regexp + '$'; - const rx = tryRegExp(anchored); - cachedStyles.regexps.set(cacheKey, rx || false); - if (!compileAll && performance.now() - t0 > 100) { - return false; - } - } - } - } - return true; -} - - function invalidateCache({added, updated, deletedId} = {}) { if (!cachedStyles.list) { return; @@ -551,12 +527,12 @@ function cleanupCachedFilters({force = false} = {}) { function getDomains(url) { - if (url.indexOf('file:') == 0) { + if (url.indexOf('file:') === 0) { return []; } let d = /.*?:\/*([^/:]+)/.exec(url)[1]; const domains = [d]; - while (d.indexOf('.') != -1) { + while (d.indexOf('.') !== -1) { d = d.substring(d.indexOf('.') + 1); domains.push(d); } diff --git a/update.js b/background/update.js similarity index 95% rename from update.js rename to background/update.js index a536098c..04f368f5 100644 --- a/update.js +++ b/background/update.js @@ -68,17 +68,17 @@ var updater = { }); function maybeFetchMd5(digest) { - if (!ignoreDigest && style.originalDigest && style.originalDigest != digest) { + if (!ignoreDigest && style.originalDigest && style.originalDigest !== digest) { return Promise.reject(updater.EDITED); } return download(style.md5Url); } function maybeFetchCode(md5) { - if (!md5 || md5.length != 32) { + if (!md5 || md5.length !== 32) { return Promise.reject(updater.ERROR_MD5); } - if (md5 == style.originalMd5 && style.originalDigest && !ignoreDigest) { + if (md5 === style.originalMd5 && style.originalDigest && !ignoreDigest) { return Promise.reject(updater.SAME_MD5); } return download(style.updateUrl); @@ -109,8 +109,8 @@ var updater = { return json && json.sections && json.sections.length - && typeof json.sections.every == 'function' - && typeof json.sections[0].code == 'string'; + && typeof json.sections.every === 'function' + && typeof json.sections[0].code === 'string'; } }, diff --git a/codemirror/addon/search/searchcursor.js b/codemirror/addon/search/searchcursor.js deleted file mode 100644 index b70242ee..00000000 --- a/codemirror/addon/search/searchcursor.js +++ /dev/null @@ -1,189 +0,0 @@ -// 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"; - var Pos = CodeMirror.Pos; - - function SearchCursor(doc, query, pos, caseFold) { - this.atOccurrence = false; this.doc = doc; - if (caseFold == null && typeof query == "string") caseFold = false; - - pos = pos ? doc.clipPos(pos) : Pos(0, 0); - this.pos = {from: pos, to: pos}; - - // The matches method is filled in based on the type of query. - // It takes a position and a direction, and returns an object - // describing the next occurrence of the query, or null if no - // more matches were found. - if (typeof query != "string") { // Regexp match - if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g"); - this.matches = function(reverse, pos) { - if (reverse) { - query.lastIndex = 0; - var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; - for (;;) { - query.lastIndex = cutOff; - var newMatch = query.exec(line); - if (!newMatch) break; - match = newMatch; - start = match.index; - cutOff = match.index + (match[0].length || 1); - if (cutOff == line.length) break; - } - var matchLen = (match && match[0].length) || 0; - if (!matchLen) { - if (start == 0 && line.length == 0) {match = undefined;} - else if (start != doc.getLine(pos.line).length) { - matchLen++; - } - } - } else { - query.lastIndex = pos.ch; - var line = doc.getLine(pos.line), match = query.exec(line); - var matchLen = (match && match[0].length) || 0; - var start = match && match.index; - if (start + matchLen != line.length && !matchLen) matchLen = 1; - } - if (match && matchLen) - return {from: Pos(pos.line, start), - to: Pos(pos.line, start + matchLen), - match: match}; - }; - } else { // String query - var origQuery = query; - if (caseFold) query = query.toLowerCase(); - var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; - var target = query.split("\n"); - // Different methods for single-line and multi-line queries - if (target.length == 1) { - if (!query.length) { - // Empty string would match anything and never progress, so - // we define it to match nothing instead. - this.matches = function() {}; - } else { - this.matches = function(reverse, pos) { - if (reverse) { - var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig); - var match = line.lastIndexOf(query); - if (match > -1) { - match = adjustPos(orig, line, match); - return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; - } - } else { - var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig); - var match = line.indexOf(query); - if (match > -1) { - match = adjustPos(orig, line, match) + pos.ch; - return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; - } - } - }; - } - } else { - var origTarget = origQuery.split("\n"); - this.matches = function(reverse, pos) { - var last = target.length - 1; - if (reverse) { - if (pos.line - (target.length - 1) < doc.firstLine()) return; - if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return; - var to = Pos(pos.line, origTarget[last].length); - for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln) - if (target[i] != fold(doc.getLine(ln))) return; - var line = doc.getLine(ln), cut = line.length - origTarget[0].length; - if (fold(line.slice(cut)) != target[0]) return; - return {from: Pos(ln, cut), to: to}; - } else { - if (pos.line + (target.length - 1) > doc.lastLine()) return; - var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length; - if (fold(line.slice(cut)) != target[0]) return; - var from = Pos(pos.line, cut); - for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln) - if (target[i] != fold(doc.getLine(ln))) return; - if (fold(doc.getLine(ln).slice(0, origTarget[last].length)) != target[last]) return; - return {from: from, to: Pos(ln, origTarget[last].length)}; - } - }; - } - } - } - - SearchCursor.prototype = { - findNext: function() {return this.find(false);}, - findPrevious: function() {return this.find(true);}, - - find: function(reverse) { - var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to); - function savePosAndFail(line) { - var pos = Pos(line, 0); - self.pos = {from: pos, to: pos}; - self.atOccurrence = false; - return false; - } - - for (;;) { - if (this.pos = this.matches(reverse, pos)) { - this.atOccurrence = true; - return this.pos.match || true; - } - if (reverse) { - if (!pos.line) return savePosAndFail(0); - pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length); - } - else { - var maxLine = this.doc.lineCount(); - if (pos.line == maxLine - 1) return savePosAndFail(maxLine); - pos = Pos(pos.line + 1, 0); - } - } - }, - - from: function() {if (this.atOccurrence) return this.pos.from;}, - to: function() {if (this.atOccurrence) return this.pos.to;}, - - replace: function(newText, origin) { - if (!this.atOccurrence) return; - var lines = CodeMirror.splitLines(newText); - this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin); - this.pos.to = Pos(this.pos.from.line + lines.length - 1, - lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); - } - }; - - // Maps a position in a case-folded line back to a position in the original line - // (compensating for codepoints increasing in number during folding) - function adjustPos(orig, folded, pos) { - if (orig.length == folded.length) return pos; - for (var pos1 = Math.min(pos, orig.length);;) { - var len1 = orig.slice(0, pos1).toLowerCase().length; - if (len1 < pos) ++pos1; - else if (len1 > pos) --pos1; - else return pos1; - } - } - - CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { - return new SearchCursor(this.doc, query, pos, caseFold); - }); - CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) { - return new SearchCursor(this, query, pos, caseFold); - }); - - CodeMirror.defineExtension("selectMatches", function(query, caseFold) { - var ranges = []; - var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold); - while (cur.findNext()) { - if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break; - ranges.push({anchor: cur.from(), head: cur.to()}); - } - if (ranges.length) - this.setSelections(ranges, 0); - }); -}); diff --git a/apply.js b/content/apply.js similarity index 91% rename from apply.js rename to content/apply.js index 54ff5047..0cb626e0 100644 --- a/apply.js +++ b/content/apply.js @@ -12,6 +12,7 @@ var styleElements = new Map(); var disabledElements = new Map(); var retiredStyleTimers = new Map(); var docRewriteObserver; +var docHeadObserver; requestStyles(); chrome.runtime.onMessage.addListener(applyOnMessage); @@ -27,7 +28,7 @@ function requestStyles(options, callback = applyStyles) { // dynamic about: and javascript: iframes don't have an URL yet // so we'll try the parent frame which is guaranteed to have a real URL try { - if (window != parent) { + if (window !== parent) { matchUrl = parent.location.href; } } catch (e) {} @@ -48,7 +49,7 @@ function requestStyles(options, callback = applyStyles) { function applyOnMessage(request, sender, sendResponse) { - if (request.styles == 'DIY') { + if (request.styles === 'DIY') { // Do-It-Yourself tells our built-in pages to fetch the styles directly // which is faster because IPC messaging JSON-ifies everything internally requestStyles({}, styles => { @@ -59,7 +60,6 @@ function applyOnMessage(request, sender, sendResponse) { } switch (request.method) { - case 'styleDeleted': removeStyle(request); break; @@ -114,7 +114,7 @@ function doDisableAll(disable = disableAll) { disableAll = disable; Array.prototype.forEach.call(document.styleSheets, stylesheet => { if (stylesheet.ownerNode.matches(`STYLE.stylus[id^="${ID_PREFIX}"]`) - && stylesheet.disabled != disable) { + && stylesheet.disabled !== disable) { stylesheet.disabled = disable; } }); @@ -122,14 +122,14 @@ function doDisableAll(disable = disableAll) { function doExposeIframes(state = exposeIframes) { - if (state === exposeIframes || window == parent) { + if (state === exposeIframes || window === parent) { return; } exposeIframes = state; const attr = document.documentElement.getAttribute('stylus-iframe'); - if (state && attr != '') { + if (state && attr !== '') { document.documentElement.setAttribute('stylus-iframe', ''); - } else if (!state && attr == '') { + } else if (!state && attr === '') { document.documentElement.removeAttribute('stylus-iframe'); } } @@ -193,7 +193,7 @@ function applyStyles(styles) { } if (document.head && document.head.firstChild - && document.head.firstChild.id == 'xml-viewer-style') { + && document.head.firstChild.id === 'xml-viewer-style') { // when site response is application/xml Chrome displays our style elements // under document.documentElement as plain text so we need to move them into HEAD // which is already autogenerated at this moment @@ -211,6 +211,17 @@ function applyStyles(styles) { } }); } + if (styleElements.size && !document.head && !docHeadObserver) { + // HEAD is not yet present so we'll wait for it and move the style elements + docHeadObserver = new MutationObserver(() => { + docHeadObserver.disconnect(); + docHeadObserver = null; + for (const el of styleElements.values()) { + ROOT.insertBefore(el, document.body); + } + }); + docHeadObserver.observe(ROOT, {childList: true}); + } } @@ -282,7 +293,7 @@ function initDocRewriteObserver() { for (let m = mutations.length; --m >= 0;) { const added = mutations[m].addedNodes; for (let n = added.length; --n >= 0;) { - if (added[n].localName == 'html') { + if (added[n].localName === 'html') { reinjectStyles(); return; } @@ -292,7 +303,7 @@ function initDocRewriteObserver() { docRewriteObserver.observe(document, {childList: true}); // detect dynamic iframes rewritten after creation by the embedder i.e. externally setTimeout(() => { - if (document.documentElement != ROOT) { + if (document.documentElement !== ROOT) { reinjectStyles(); } }); diff --git a/content/install.js b/content/install.js new file mode 100644 index 00000000..0445af7f --- /dev/null +++ b/content/install.js @@ -0,0 +1,360 @@ +'use strict'; + +const CHROMIUM = /Chromium/.test(navigator.userAgent); // non-Windows Chromium +const FIREFOX = /Firefox/.test(navigator.userAgent); +const VIVALDI = /Vivaldi/.test(navigator.userAgent); +const OPERA = /OPR/.test(navigator.userAgent); + +document.addEventListener('stylishUpdate', onUpdateClicked); +document.addEventListener('stylishUpdateChrome', onUpdateClicked); +document.addEventListener('stylishUpdateOpera', onUpdateClicked); + +document.addEventListener('stylishInstall', onInstallClicked); +document.addEventListener('stylishInstallChrome', onInstallClicked); +document.addEventListener('stylishInstallOpera', onInstallClicked); + +chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { + // orphaned content script check + if (msg.method === 'ping') { + sendResponse(true); + } +}); + +// TODO: remove the following statement when USO is fixed +document.documentElement.appendChild(document.createElement('script')).text = '(' + + function () { + let settings; + document.addEventListener('stylusFixBuggyUSOsettings', function _({detail}) { + document.removeEventListener('stylusFixBuggyUSOsettings', _); + settings = /\?/.test(detail) && new URLSearchParams(new URL(detail).search); + }); + const originalResponseJson = Response.prototype.json; + Response.prototype.json = function (...args) { + return originalResponseJson.call(this, ...args).then(json => { + Response.prototype.json = originalResponseJson; + if (!settings || typeof ((json || {}).style_settings || {}).every !== 'function') { + return json; + } + const images = new Map(); + for (const jsonSetting of json.style_settings) { + let value = settings.get('ik-' + jsonSetting.install_key); + if (!value + || !jsonSetting.style_setting_options + || !jsonSetting.style_setting_options[0]) { + continue; + } + if (value.startsWith('ik-')) { + value = value.replace(/^ik-/, ''); + const defaultItem = jsonSetting.style_setting_options.find(item => item.default); + if (!defaultItem || defaultItem.install_key !== value) { + if (defaultItem) { + defaultItem.default = false; + } + jsonSetting.style_setting_options.some(item => { + if (item.install_key === value) { + item.default = true; + return true; + } + }); + } + } else if (jsonSetting.setting_type === 'image') { + jsonSetting.style_setting_options.some(item => { + if (item.default) { + item.default = false; + return true; + } + }); + images.set(jsonSetting.install_key, value); + } else { + const item = jsonSetting.style_setting_options[0]; + if (item.value !== value && item.install_key === 'placeholder') { + item.value = value; + } + } + } + if (images.size) { + new MutationObserver((_, observer) => { + if (!document.getElementById('style-settings')) { + return; + } + observer.disconnect(); + for (const [name, url] of images.entries()) { + const elRadio = document.querySelector(`input[name="ik-${name}"][value="user-url"]`); + const elUrl = elRadio && document.getElementById(elRadio.id.replace('url-choice', 'user-url')); + if (elUrl) { + elUrl.value = url; + } + } + }).observe(document, {childList: true, subtree: true}); + } + return json; + }); + }; + } + ')()'; + +// TODO: remove the following statement when USO pagination is fixed +if (location.search.includes('category=')) { + document.addEventListener('DOMContentLoaded', function _() { + document.removeEventListener('DOMContentLoaded', _); + new MutationObserver((_, observer) => { + if (!document.getElementById('pagination')) { + return; + } + observer.disconnect(); + const category = '&' + location.search.match(/category=[^&]+/)[0]; + const links = document.querySelectorAll('#pagination a[href*="page="]:not([href*="category="])'); + for (let i = 0; i < links.length; i++) { + links[i].href += category; + } + }).observe(document, {childList: true, subtree: true}); + }); +} + +new MutationObserver((mutations, observer) => { + if (document.body) { + observer.disconnect(); + // TODO: remove the following statement when USO pagination title is fixed + document.title = document.title.replace(/^\d+&category=/, ''); + chrome.runtime.sendMessage({ + method: 'getStyles', + url: getMeta('stylish-id-url') || location.href + }, checkUpdatability); + } +}).observe(document.documentElement, {childList: true}); + +/* since we are using "stylish-code-chrome" meta key on all browsers and + US.o does not provide "advanced settings" on this url if browser is not Chrome, + we need to fix this URL using "stylish-update-url" meta key +*/ +function getStyleURL() { + const url = getMeta('stylish-code-chrome'); + // TODO: remove when USO is fixed + const directUrl = getMeta('stylish-update-url'); + if (directUrl.includes('?') && !url.includes('?')) { + /* get custom settings from the update url */ + return Object.assign(new URL(url), { + search: (new URL(directUrl)).search + }).href; + } + return url; +} + +function checkUpdatability([installedStyle]) { + // TODO: remove the following statement when USO is fixed + document.dispatchEvent(new CustomEvent('stylusFixBuggyUSOsettings', { + detail: installedStyle && installedStyle.updateUrl, + })); + if (!installedStyle) { + sendEvent('styleCanBeInstalledChrome'); + return; + } + const md5Url = getMeta('stylish-md5-url'); + if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) { + getResource(md5Url).then(md5 => { + reportUpdatable(md5 !== installedStyle.originalMd5); + }); + } else { + getResource(getStyleURL()).then(code => { + reportUpdatable(code === null || + !styleSectionsEqual(JSON.parse(code), installedStyle)); + }); + } + + function reportUpdatable(isUpdatable) { + sendEvent( + isUpdatable + ? 'styleCanBeUpdatedChrome' + : 'styleAlreadyInstalledChrome', + { + updateUrl: installedStyle.updateUrl + } + ); + } +} + + +function sendEvent(type, detail = null) { + if (FIREFOX) { + type = type.replace('Chrome', ''); + } else if (OPERA || VIVALDI) { + type = type.replace('Chrome', 'Opera'); + } + detail = {detail}; + if (typeof cloneInto !== 'undefined') { + // Firefox requires explicit cloning, however USO can't process our messages anyway + // because USO tries to use a global "event" variable deprecated in Firefox + detail = cloneInto(detail, document); // eslint-disable-line no-undef + } + onDOMready().then(() => { + document.dispatchEvent(new CustomEvent(type, detail)); + }); +} + + +function onInstallClicked() { + if (!orphanCheck || !orphanCheck()) { + return; + } + getResource(getMeta('stylish-description')) + .then(name => saveStyleCode('styleInstall', name)) + .then(() => getResource(getMeta('stylish-install-ping-url-chrome'))); +} + + +function onUpdateClicked() { + if (!orphanCheck || !orphanCheck()) { + return; + } + chrome.runtime.sendMessage({ + method: 'getStyles', + url: getMeta('stylish-id-url') || location.href, + }, ([style]) => { + saveStyleCode('styleUpdate', style.name, {id: style.id}); + }); +} + + +function saveStyleCode(message, name, addProps) { + return new Promise(resolve => { + if (!confirm(chrome.i18n.getMessage(message, [name]))) { + return; + } + enableUpdateButton(false); + getResource(getStyleURL()).then(code => { + chrome.runtime.sendMessage( + Object.assign(JSON.parse(code), addProps, { + method: 'saveStyle', + reason: 'update', + }), + style => { + if (message === 'styleUpdate' && style.updateUrl.includes('?')) { + enableUpdateButton(true); + } else { + sendEvent('styleInstalledChrome'); + } + } + ); + resolve(); + }); + }); + + function enableUpdateButton(state) { + const button = document.getElementById('update_style_button'); + if (button) { + button.style.cssText = state ? '' : + 'pointer-events: none !important; opacity: .25 !important;'; + } + } +} + + +function getMeta(name) { + const e = document.querySelector(`link[rel="${name}"]`); + return e ? e.getAttribute('href') : null; +} + + +function getResource(url) { + return new Promise(resolve => { + if (url.startsWith('#')) { + resolve(document.getElementById(url.slice(1)).textContent); + } else { + chrome.runtime.sendMessage({method: 'download', url}, resolve); + } + }); +} + + +function styleSectionsEqual({sections: a}, {sections: b}) { + if (!a || !b) { + return undefined; + } + if (a.length !== b.length) { + return false; + } + const checkedInB = []; + return a.every(sectionA => b.some(sectionB => { + if (!checkedInB.includes(sectionB) && propertiesEqual(sectionA, sectionB)) { + checkedInB.push(sectionB); + return true; + } + })); + + function propertiesEqual(secA, secB) { + for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) { + if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) { + return false; + } + } + return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a === b); + } + + function equalOrEmpty(a, b, telltale, comparator) { + const typeA = a && typeof a[telltale] === 'function'; + const typeB = b && typeof b[telltale] === 'function'; + return ( + (a === null || a === undefined || (typeA && !a.length)) && + (b === null || b === undefined || (typeB && !b.length)) + ) || typeA && typeB && a.length === b.length && comparator(a, b); + } + + function arrayMirrors(array1, array2) { + for (const el of array1) { + if (array2.indexOf(el) < 0) { + return false; + } + } + for (const el of array2) { + if (array1.indexOf(el) < 0) { + return false; + } + } + return true; + } +} + + +function onDOMready() { + if (document.readyState !== 'loading') { + return Promise.resolve(); + } + return new Promise(resolve => { + document.addEventListener('DOMContentLoaded', function _() { + document.removeEventListener('DOMContentLoaded', _); + resolve(); + }); + }); +} + + +function orphanCheck() { + const port = chrome.runtime.connect(); + if (port) { + port.disconnect(); + return true; + } + // we're orphaned due to an extension update + // we can detach event listeners + document.removeEventListener('stylishUpdate', onUpdateClicked); + document.removeEventListener('stylishUpdateChrome', onUpdateClicked); + document.removeEventListener('stylishUpdateOpera', onUpdateClicked); + + document.removeEventListener('stylishInstall', onInstallClicked); + document.removeEventListener('stylishInstallChrome', onInstallClicked); + document.removeEventListener('stylishInstallOpera', onInstallClicked); + + // we can't detach chrome.runtime.onMessage because it's no longer connected internally + // we can destroy global functions in this context to free up memory + [ + 'checkUpdatability', + 'getMeta', + 'getResource', + 'onDOMready', + 'onInstallClicked', + 'onUpdateClicked', + 'orphanCheck', + 'saveStyleCode', + 'sendEvent', + 'styleSectionsEqual', + ].forEach(fn => (window[fn] = null)); +} diff --git a/edit.html b/edit.html index 5d8df5a9..5df54d54 100644 --- a/edit.html +++ b/edit.html @@ -1,749 +1,213 @@ - - + + - - - - - - + + + + + + + - - - + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - + - - - - + + + + - - - + + + - - - + + + + - - - - - - - - - - - - - - - - - -
-

-
-
-
-
-
- - - - - - - - - - - - - - + diff --git a/edit.js b/edit.js deleted file mode 100644 index dbbedf18..00000000 --- a/edit.js +++ /dev/null @@ -1,1947 +0,0 @@ -/* eslint no-tabs: 0, no-var: 0, indent: [2, tab, {VariableDeclarator: 0, SwitchCase: 1}], quotes: 0 */ -/* global CodeMirror */ -"use strict"; - -var styleId = null; -var dirty = {}; // only the actually dirty items here -var editors = []; // array of all CodeMirror instances -var saveSizeOnClose; -var useHistoryBack; // use browser history back when "back to manage" is clicked - -// direct & reverse mapping of @-moz-document keywords and internal property names -var propertyToCss = {urls: "url", urlPrefixes: "url-prefix", domains: "domain", regexps: "regexp"}; -var CssToProperty = {"url": "urls", "url-prefix": "urlPrefixes", "domain": "domains", "regexp": "regexps"}; - -// if background page hasn't been loaded yet, increase the chances it has before DOMContentLoaded -onBackgroundReady(); - -// make querySelectorAll enumeration code readable -["forEach", "some", "indexOf", "map"].forEach(function(method) { - NodeList.prototype[method]= Array.prototype[method]; -}); - -// Chrome pre-34 -Element.prototype.matches = Element.prototype.matches || Element.prototype.webkitMatchesSelector; - -// Chrome pre-41 polyfill -Element.prototype.closest = Element.prototype.closest || function(selector) { - for (var e = this; e && !e.matches(selector); e = e.parentElement) {} - return e; -}; - -Array.prototype.rotate = function(amount) { // negative amount == rotate left - var r = this.slice(-amount, this.length); - Array.prototype.push.apply(r, this.slice(0, this.length - r.length)); - return r; -}; - -Object.defineProperty(Array.prototype, "last", {get: function() { return this[this.length - 1]; }}); - -// preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs() -new MutationObserver((mutations, observer) => { - const themeElement = document.getElementById("cm-theme"); - if (themeElement) { - themeElement.href = prefs.get("editor.theme") == "default" ? "" - : "codemirror/theme/" + prefs.get("editor.theme") + ".css"; - observer.disconnect(); - } -}).observe(document, {subtree: true, childList: true}); - -getCodeMirrorThemes(); - -// reroute handling to nearest editor when keypress resolves to one of these commands -var hotkeyRerouter = { - commands: { - save: true, jumpToLine: true, nextEditor: true, prevEditor: true, - find: true, findNext: true, findPrev: true, replace: true, replaceAll: true - }, - setState: function(enable) { - setTimeout(function() { - document[(enable ? "add" : "remove") + "EventListener"]("keydown", hotkeyRerouter.eventHandler); - }, 0); - }, - eventHandler: function(event) { - var keyName = CodeMirror.keyName(event); - if ("handled" == CodeMirror.lookupKey(keyName, CodeMirror.getOption("keyMap"), handleCommand) - || "handled" == CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand)) { - event.preventDefault(); - event.stopPropagation(); - } - function handleCommand(command) { - if (hotkeyRerouter.commands[command] === true) { - CodeMirror.commands[command](getEditorInSight(event.target)); - return true; - } - } - } -}; - -function onChange(event) { - var node = event.target; - if ("savedValue" in node) { - var currentValue = "checkbox" === node.type ? node.checked : node.value; - setCleanItem(node, node.savedValue === currentValue); - } else { - // the manually added section's applies-to is dirty only when the value is non-empty - setCleanItem(node, node.localName != "input" || !node.value.trim()); - delete node.savedValue; // only valid when actually saved - } - updateTitle(); -} - -// Set .dirty on stylesheet contributors that have changed -function setDirtyClass(node, isDirty) { - node.classList.toggle("dirty", isDirty); -} - -function setCleanItem(node, isClean) { - if (!node.id) { - node.id = Date.now().toString(32).substr(-6); - } - - if (isClean) { - delete dirty[node.id]; - // code sections have .CodeMirror property - if (node.CodeMirror) { - node.savedValue = node.CodeMirror.changeGeneration(); - } else { - node.savedValue = "checkbox" === node.type ? node.checked : node.value; - } - } else { - dirty[node.id] = true; - } - - setDirtyClass(node, !isClean); -} - -function isCleanGlobal() { - var clean = Object.keys(dirty).length == 0; - setDirtyClass(document.body, !clean); - var saveBtn = document.getElementById("save-button") - if (clean){ - //saveBtn.removeAttribute('disabled'); - }else{ - //saveBtn.setAttribute('disabled', true); - } - return clean; -} - -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 -} - -function setCleanSection(section) { - section.querySelectorAll(".style-contributor").forEach(function(node) { setCleanItem(node, true) }); - - // #header section has no codemirror - var cm = section.CodeMirror; - if (cm) { - section.savedValue = cm.changeGeneration(); - indicateCodeChange(cm); - } -} - -function initCodeMirror() { - var CM = CodeMirror; - var isWindowsOS = navigator.appVersion.indexOf("Windows") > 0; - - // CodeMirror miserably fails on keyMap="" so let's ensure it's not - if (!prefs.get('editor.keyMap')) { - prefs.reset('editor.keyMap'); - } - - // default option values - Object.assign(CM.defaults, { - mode: 'css', - lineNumbers: true, - lineWrapping: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], - matchBrackets: true, - highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, - lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get("editor.lintDelay")}, - lintReportDelay: prefs.get("editor.lintReportDelay"), - styleActiveLine: true, - theme: "default", - keyMap: prefs.get("editor.keyMap"), - extraKeys: { // independent of current keyMap - "Alt-PageDown": "nextEditor", - "Alt-PageUp": "prevEditor" - } - }, prefs.get("editor.options")); - - // additional commands - CM.commands.jumpToLine = jumpToLine; - CM.commands.nextEditor = function(cm) { nextPrevEditor(cm, 1) }; - CM.commands.prevEditor = function(cm) { nextPrevEditor(cm, -1) }; - CM.commands.save = save; - CM.commands.blockComment = function(cm) { - cm.blockComment(cm.getCursor("from"), cm.getCursor("to"), {fullLines: false}); - }; - - // "basic" keymap only has basic keys by design, so we skip it - - var extraKeysCommands = {}; - Object.keys(CM.defaults.extraKeys).forEach(function(key) { - extraKeysCommands[CM.defaults.extraKeys[key]] = true; - }); - if (!extraKeysCommands.jumpToLine) { - CM.keyMap.sublime["Ctrl-G"] = "jumpToLine"; - CM.keyMap.emacsy["Ctrl-G"] = "jumpToLine"; - CM.keyMap.pcDefault["Ctrl-J"] = "jumpToLine"; - CM.keyMap.macDefault["Cmd-J"] = "jumpToLine"; - } - if (!extraKeysCommands.autocomplete) { - CM.keyMap.pcDefault["Ctrl-Space"] = "autocomplete"; // will be used by "sublime" on PC via fallthrough - CM.keyMap.macDefault["Alt-Space"] = "autocomplete"; // OSX uses Ctrl-Space and Cmd-Space for something else - CM.keyMap.emacsy["Alt-/"] = "autocomplete"; // copied from "emacs" keymap - // "vim" and "emacs" define their own autocomplete hotkeys - } - if (!extraKeysCommands.blockComment) { - CM.keyMap.sublime["Shift-Ctrl-/"] = "blockComment"; - } - - if (isWindowsOS) { - // "pcDefault" keymap on Windows should have F3/Shift-F3 - if (!extraKeysCommands.findNext) { - CM.keyMap.pcDefault["F3"] = "findNext"; - } - if (!extraKeysCommands.findPrev) { - CM.keyMap.pcDefault["Shift-F3"] = "findPrev"; - } - - // try to remap non-interceptable Ctrl-(Shift-)N/T/W hotkeys - ["N", "T", "W"].forEach(function(char) { - [{from: "Ctrl-", to: ["Alt-", "Ctrl-Alt-"]}, - {from: "Shift-Ctrl-", to: ["Ctrl-Alt-", "Shift-Ctrl-Alt-"]} // Note: modifier order in CM is S-C-A - ].forEach(function(remap) { - var oldKey = remap.from + char; - Object.keys(CM.keyMap).forEach(function(keyMapName) { - var keyMap = CM.keyMap[keyMapName]; - var command = keyMap[oldKey]; - if (!command) { - return; - } - remap.to.some(function(newMod) { - var newKey = newMod + char; - if (!(newKey in keyMap)) { - delete keyMap[oldKey]; - keyMap[newKey] = command; - return true; - } - }); - }); - }); - }); - } - - // user option values - CM.getOption = function (o) { - return CodeMirror.defaults[o]; - }; - CM.setOption = function (o, v) { - CodeMirror.defaults[o] = v; - editors.forEach(function(editor) { - editor.setOption(o, v); - }); - }; - - CM.prototype.getSection = function() { - return this.display.wrapper.parentNode; - }; - - // initialize global editor controls - function optionsHtmlFromArray(options) { - return options.map(function(opt) { return ""; }).join(""); - } - var themeControl = document.getElementById("editor.theme"); - const themeList = localStorage.codeMirrorThemes; - if (themeList) { - themeControl.innerHTML = optionsHtmlFromArray(themeList.split(/\s+/)); - } else { - // Chrome is starting up and shows our edit.html, but the background page isn't loaded yet - const theme = prefs.get("editor.theme"); - themeControl.innerHTML = optionsHtmlFromArray([theme == "default" ? t("defaultTheme") : theme]); - getCodeMirrorThemes().then(() => { - const themes = (localStorage.codeMirrorThemes || '').split(/\s+/); - themeControl.innerHTML = optionsHtmlFromArray(themes); - themeControl.selectedIndex = Math.max(0, themes.indexOf(theme)); - }); - } - document.getElementById("editor.keyMap").innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort()); - document.getElementById("options").addEventListener("change", acmeEventListener, false); - setupLivePrefs(); - - hotkeyRerouter.setState(true); -} - -function acmeEventListener(event) { - var el = event.target; - var option = el.dataset.option; - //console.log("acmeEventListener heard %s on %s", event.type, el.id); - if (!option) { - console.error("acmeEventListener: no 'cm_option' %O", el); - return; - } - var value = el.type == "checkbox" ? el.checked : el.value; - switch (option) { - case "tabSize": - CodeMirror.setOption("indentUnit", value); - break; - case "theme": - var themeLink = document.getElementById("cm-theme"); - // use non-localized "default" internally - if (!value || value == "default" || value == t("defaultTheme")) { - value = "default"; - if (prefs.get(el.id) != value) { - prefs.set(el.id, value); - } - themeLink.href = ""; - el.selectedIndex = 0; - break; - } - var url = chrome.runtime.getURL("codemirror/theme/" + value + ".css"); - if (themeLink.href == url) { // preloaded in initCodeMirror() - break; - } - // avoid flicker: wait for the second stylesheet to load, then apply the theme - document.head.insertAdjacentHTML("beforeend", - ''); - (function() { - setTimeout(function() { - CodeMirror.setOption(option, value); - themeLink.remove(); - document.getElementById("cm-theme2").id = "cm-theme"; - }, 100); - })(); - return; - case "highlightSelectionMatches": - switch (value) { - case 'token': - case 'selection': - document.body.dataset[option] = value; - value = {showToken: value == 'token' && /[#.\-\w]/, annotateScrollbar: true}; - break; - default: - value = null; - } - } - CodeMirror.setOption(option, value); -} - -// replace given textarea with the CodeMirror editor -function setupCodeMirror(textarea, index) { - var cm = CodeMirror.fromTextArea(textarea, {lint: null}); - - cm.on("change", indicateCodeChange); - cm.on("blur", function(cm) { - editors.lastActive = cm; - hotkeyRerouter.setState(true); - setTimeout(function() { - var cm = editors.lastActive; - var childFocused = cm.display.wrapper.contains(document.activeElement); - cm.display.wrapper.classList.toggle("CodeMirror-active", childFocused); - }, 0); - }); - cm.on("focus", function() { - hotkeyRerouter.setState(false); - cm.display.wrapper.classList.add("CodeMirror-active"); - }); - cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event)); - - var resizeGrip = cm.display.wrapper.appendChild(document.createElement("div")); - resizeGrip.className = "resize-grip"; - resizeGrip.addEventListener("mousedown", function(e) { - e.preventDefault(); - var cm = e.target.parentNode.CodeMirror; - var minHeight = cm.defaultTextHeight() - + cm.display.lineDiv.offsetParent.offsetTop /* .CodeMirror-lines padding */ - + cm.display.wrapper.offsetHeight - cm.display.wrapper.scrollHeight /* borders */; - function resize(e) { - cm.setSize(null, Math.max(minHeight, cm.display.wrapper.scrollHeight + e.movementY)); - } - document.addEventListener("mousemove", resize); - document.addEventListener("mouseup", function resizeStop() { - document.removeEventListener("mouseup", resizeStop); - document.removeEventListener("mousemove", resize); - }); - }); - // resizeGrip has enough space when scrollbars.horiz is visible - if (cm.display.scrollbars.horiz.style.display != "") { - cm.display.scrollbars.vert.style.marginBottom = "0"; - } - // resizeGrip space adjustment in case a long line was entered/deleted by a user - new MutationObserver(function(mutations) { - var hScrollbar = mutations[0].target; - var hScrollbarVisible = hScrollbar.style.display != ""; - var vScrollbar = hScrollbar.parentNode.CodeMirror.display.scrollbars.vert; - vScrollbar.style.marginBottom = hScrollbarVisible ? "0" : ""; - }).observe(cm.display.scrollbars.horiz, { - attributes: true, - attributeFilter: ["style"] - }); - - editors.splice(index || editors.length, 0, cm); - return cm; -} - -function indicateCodeChange(cm) { - var section = cm.getSection(); - setCleanItem(section, cm.isClean(section.savedValue)); - updateTitle(); - updateLintReport(cm); -} - -function getSectionForChild(e) { - return e.closest("#sections > div"); -} - -function getSections() { - return document.querySelectorAll("#sections > div"); -} - -// remind Chrome to repaint a previously invisible editor box by toggling any element's transform -// this bug is present in some versions of Chrome (v37-40 or something) -document.addEventListener("scroll", function(event) { - var style = document.getElementById("name").style; - style.webkitTransform = style.webkitTransform ? "" : "scale(1)"; -}); - -// Shift-Ctrl-Wheel scrolls entire page even when mouse is over a code editor -document.addEventListener("wheel", function(event) { - if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) { - // Chrome scrolls horizontally when Shift is pressed but on some PCs this might be different - window.scrollBy(0, event.deltaX || event.deltaY); - event.preventDefault(); - } -}); - -chrome.tabs.query({currentWindow: true}, function(tabs) { - var windowId = tabs[0].windowId; - if (prefs.get("openEditInWindow")) { - if (sessionStorage.saveSizeOnClose && 'left' in prefs.get('windowPosition', {})) { - // window was reopened via Ctrl-Shift-T etc. - chrome.windows.update(windowId, prefs.get('windowPosition')); - } - if (tabs.length == 1 && window.history.length == 1) { - chrome.windows.getAll(function(windows) { - if (windows.length > 1) { - sessionStorageHash("saveSizeOnClose").set(windowId, true); - saveSizeOnClose = true; - } - }); - } else { - saveSizeOnClose = sessionStorageHash("saveSizeOnClose").value[windowId]; - } - } - chrome.tabs.onRemoved.addListener(function(tabId, info) { - sessionStorageHash("manageStylesHistory").unset(tabId); - if (info.windowId == windowId && info.isWindowClosing) { - sessionStorageHash("saveSizeOnClose").unset(windowId); - } - }); -}); - -getActiveTab().then(tab => { - useHistoryBack = sessionStorageHash("manageStylesHistory").value[tab.id] == location.href; -}); - -function goBackToManage(event) { - if (useHistoryBack) { - event.stopPropagation(); - event.preventDefault(); - history.back(); - } -} - -window.onbeforeunload = function() { - if (saveSizeOnClose) { - prefs.set("windowPosition", { - left: screenLeft, - top: screenTop, - width: outerWidth, - height: outerHeight - }); - } - document.activeElement.blur(); - if (isCleanGlobal()) { - return; - } - updateLintReport(null, 0); - return confirm(t('styleChangesNotSaved')); -}; - -function addAppliesTo(list, name, value) { - var showingEverything = list.querySelector(".applies-to-everything") != null; - // blow away "Everything" if it's there - if (showingEverything) { - list.removeChild(list.firstChild); - } - var e; - if (name && value) { - e = template.appliesTo.cloneNode(true); - e.querySelector("[name=applies-type]").value = name; - e.querySelector("[name=applies-value]").value = value; - e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); - } else if (showingEverything || list.hasChildNodes()) { - e = template.appliesTo.cloneNode(true); - if (list.hasChildNodes()) { - e.querySelector("[name=applies-type]").value = list.querySelector("li:last-child [name='applies-type']").value; - } - e.querySelector(".remove-applies-to").addEventListener("click", removeAppliesTo, false); - } else { - e = template.appliesToEverything.cloneNode(true); - } - e.querySelector(".add-applies-to").addEventListener("click", function() {addAppliesTo(this.parentNode.parentNode)}, false); - list.appendChild(e); -} - -function addSection(event, section) { - var div = template.section.cloneNode(true); - div.querySelector(".applies-to-help").addEventListener("click", showAppliesToHelp, false); - div.querySelector(".remove-section").addEventListener("click", removeSection, false); - div.querySelector(".add-section").addEventListener("click", addSection, false); - div.querySelector(".beautify-section").addEventListener("click", beautify); - - var codeElement = div.querySelector(".code"); - var appliesTo = div.querySelector(".applies-to-list"); - var appliesToAdded = false; - - if (section) { - codeElement.value = section.code; - for (var i in propertyToCss) { - if (section[i]) { - section[i].forEach(function(url) { - addAppliesTo(appliesTo, propertyToCss[i], url); - appliesToAdded = true; - }); - } - } - } - if (!appliesToAdded) { - addAppliesTo(appliesTo); - } - - appliesTo.addEventListener("change", onChange); - appliesTo.addEventListener("input", onChange); - - toggleTestRegExpVisibility(); - appliesTo.addEventListener('change', toggleTestRegExpVisibility); - div.querySelector('.test-regexp').onclick = showRegExpTester; - function toggleTestRegExpVisibility() { - const show = [...appliesTo.children].some(item => - !item.matches('.applies-to-everything') && - item.querySelector('.applies-type').value == 'regexp' && - item.querySelector('.applies-value').value.trim()); - div.classList.toggle('has-regexp', show); - appliesTo.oninput = appliesTo.oninput || show && (event => { - if (event.target.matches('.applies-value') - && event.target.parentElement.querySelector('.applies-type').value == 'regexp') { - showRegExpTester(null, div); - } - }); - } - - var sections = document.getElementById("sections"); - if (event) { - var clickedSection = getSectionForChild(event.target); - sections.insertBefore(div, clickedSection.nextElementSibling); - var newIndex = getSections().indexOf(clickedSection) + 1; - var cm = setupCodeMirror(codeElement, newIndex); - makeSectionVisible(cm); - cm.focus() - renderLintReport(); - } else { - sections.appendChild(div); - var cm = setupCodeMirror(codeElement); - } - - div.CodeMirror = cm; - setCleanSection(div); - return div; -} - -function removeAppliesTo(event) { - var appliesTo = event.target.parentNode, - appliesToList = appliesTo.parentNode; - removeAreaAndSetDirty(appliesTo); - if (!appliesToList.hasChildNodes()) { - addAppliesTo(appliesToList); - } -} - -function removeSection(event) { - var section = getSectionForChild(event.target); - var cm = section.CodeMirror; - removeAreaAndSetDirty(section); - editors.splice(editors.indexOf(cm), 1); - renderLintReport(); -} - -function removeAreaAndSetDirty(area) { - var contributors = area.querySelectorAll('.style-contributor'); - if(!contributors.length){ - setCleanItem(area, false); - } - contributors.some(function(node) { - if (node.savedValue) { - // it's a saved section, so make it dirty and stop the enumeration - setCleanItem(area, false); - return true; - } else { - // it's an empty section, so undirty the applies-to items, - // otherwise orphaned ids would keep the style dirty - setCleanItem(node, true); - } - }); - updateTitle(); - area.parentNode.removeChild(area); -} - -function makeSectionVisible(cm) { - var section = cm.getSection(); - var bounds = section.getBoundingClientRect(); - if ((bounds.bottom > window.innerHeight && bounds.top > 0) || (bounds.top < 0 && bounds.bottom < window.innerHeight)) { - if (bounds.top < 0) { - window.scrollBy(0, bounds.top - 1); - } else { - window.scrollBy(0, bounds.bottom - window.innerHeight + 1); - } - } -} - -function setupGlobalSearch() { - var originalCommand = { - find: CodeMirror.commands.find, - findNext: CodeMirror.commands.findNext, - findPrev: CodeMirror.commands.findPrev, - replace: CodeMirror.commands.replace - }; - var originalOpenDialog = CodeMirror.prototype.openDialog; - var originalOpenConfirm = CodeMirror.prototype.openConfirm; - - var curState; // cm.state.search for last used 'find' - - function shouldIgnoreCase(query) { // treat all-lowercase non-regexp queries as case-insensitive - return typeof query == "string" && query == query.toLowerCase(); - } - - function updateState(cm, newState) { - if (!newState) { - if (cm.state.search) { - return cm.state.search; - } - if (!curState) { - return null; - } - newState = curState; - } - cm.state.search = { - query: newState.query, - overlay: newState.overlay, - annotate: cm.showMatchesOnScrollbar(newState.query, shouldIgnoreCase(newState.query)) - } - cm.addOverlay(newState.overlay); - return cm.state.search; - } - - // temporarily overrides the original openDialog with the provided template's innerHTML - function customizeOpenDialog(cm, template, callback) { - cm.openDialog = function(tmpl, cb, opt) { - // invoke 'callback' and bind 'this' to the original callback - originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt); - }; - setTimeout(function() { cm.openDialog = originalOpenDialog; }, 0); - refocusMinidialog(cm); - } - - function focusClosestCM(activeCM) { - editors.lastActive = activeCM; - var cm = getEditorInSight(); - if (cm != activeCM) { - cm.focus(); - } - return cm; - - } - - function find(activeCM) { - activeCM = focusClosestCM(activeCM); - customizeOpenDialog(activeCM, template.find, function(query) { - this(query); - curState = activeCM.state.search; - if (editors.length == 1 || !curState.query) { - return; - } - editors.forEach(function(cm) { - if (cm != activeCM) { - cm.execCommand("clearSearch"); - updateState(cm, curState); - } - }); - if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) == 0) { - findNext(activeCM); - } - }); - originalCommand.find(activeCM); - } - - function findNext(activeCM, reverse) { - var state = updateState(activeCM); - if (!state || !state.query) { - find(activeCM); - return; - } - var pos = activeCM.getCursor(reverse ? "from" : "to"); - activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor - - var rxQuery = typeof state.query == "object" - ? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? "i" : ""); - - if (document.activeElement && document.activeElement.name == "applies-value" - && searchAppliesTo(activeCM)) { - return; - } - for (var i=0, cm=activeCM; i < editors.length; i++) { - state = updateState(cm); - if (!cm.hasFocus()) { - pos = reverse ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(0, 0); - } - var searchCursor = cm.getSearchCursor(state.query, pos, shouldIgnoreCase(state.query)); - if (searchCursor.find(reverse)) { - if (editors.length > 1) { - makeSectionVisible(cm); - cm.focus(); - } - // speedup the original findNext - state.posFrom = reverse ? searchCursor.to() : searchCursor.from(); - state.posTo = CodeMirror.Pos(state.posFrom.line, state.posFrom.ch); - originalCommand[reverse ? "findPrev" : "findNext"](cm); - return; - } else if (!reverse && searchAppliesTo(cm)) { - return; - } - cm = editors[(editors.indexOf(cm) + (reverse ? -1 + editors.length : 1)) % editors.length]; - if (reverse && searchAppliesTo(cm)) { - return; - } - } - // nothing found so far, so call the original search with wrap-around - originalCommand[reverse ? "findPrev" : "findNext"](activeCM); - - function searchAppliesTo(cm) { - var inputs = [].slice.call(cm.getSection().querySelectorAll(".applies-value")); - if (reverse) { - inputs = inputs.reverse(); - } - inputs.splice(0, inputs.indexOf(document.activeElement) + 1); - return inputs.some(function(input) { - var match = rxQuery.exec(input.value); - if (match) { - input.focus(); - var end = match.index + match[0].length; - // scroll selected part into view in long inputs, - // works only outside of current event handlers chain, hence timeout=0 - setTimeout(function() { - input.setSelectionRange(end, end); - input.setSelectionRange(match.index, end) - }, 0); - return true; - } - }); - } - } - - function findPrev(cm) { - findNext(cm, true); - } - - function replace(activeCM, all) { - var queue, query, replacement; - activeCM = focusClosestCM(activeCM); - customizeOpenDialog(activeCM, template[all ? "replaceAll" : "replace"], function(txt) { - query = txt; - customizeOpenDialog(activeCM, template.replaceWith, function(txt) { - replacement = txt; - queue = editors.rotate(-editors.indexOf(activeCM)); - all ? editors.forEach(doReplace) : doReplace(); - }); - this(query); - }); - originalCommand.replace(activeCM, all); - - function doReplace() { - var cm = queue.shift(); - if (!cm) { - if (!all) { - editors.lastActive.focus(); - } - return; - } - // hide the first two dialogs (replace, replaceWith) - cm.openDialog = function(tmpl, callback, opt) { - cm.openDialog = function(tmpl, callback, opt) { - cm.openDialog = originalOpenDialog; - if (all) { - callback(replacement); - } else { - doConfirm(cm); - callback(replacement); - if (!cm.getWrapperElement().querySelector(".CodeMirror-dialog")) { - // no dialog == nothing found in the current CM, move to the next - doReplace(); - } - } - }; - callback(query); - }; - originalCommand.replace(cm, all); - } - function doConfirm(cm) { - var wrapAround = false; - var origPos = cm.getCursor(); - cm.openConfirm = function overrideConfirm(tmpl, callbacks, opt) { - var ovrCallbacks = callbacks.map(function(callback) { - return function() { - makeSectionVisible(cm); - cm.openConfirm = overrideConfirm; - setTimeout(function() { cm.openConfirm = originalOpenConfirm; }, 0); - - var pos = cm.getCursor(); - callback(); - var cmp = CodeMirror.cmpPos(cm.getCursor(), pos); - wrapAround |= cmp <= 0; - - var dlg = cm.getWrapperElement().querySelector(".CodeMirror-dialog"); - if (!dlg || cmp == 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) { - if (dlg) { - dlg.remove(); - } - doReplace(); - } - } - }); - originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt); - }; - } - } - - function replaceAll(cm) { - replace(cm, true); - } - - CodeMirror.commands.find = find; - CodeMirror.commands.findNext = findNext; - CodeMirror.commands.findPrev = findPrev; - CodeMirror.commands.replace = replace; - CodeMirror.commands.replaceAll = replaceAll; -} - -function jumpToLine(cm) { - var cur = cm.getCursor(); - refocusMinidialog(cm); - cm.openDialog(template.jumpToLine.innerHTML, function(str) { - var m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/); - if (m) { - cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch); - } - }, {value: cur.line+1}); -} - -function refocusMinidialog(cm) { - var section = cm.getSection(); - if (!section.querySelector(".CodeMirror-dialog")) { - return; - } - // close the currently opened minidialog - cm.focus(); - // make sure to focus the input in newly opened minidialog - setTimeout(function() { - section.querySelector(".CodeMirror-dialog").focus(); - }, 0); -} - -function nextPrevEditor(cm, direction) { - cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length]; - makeSectionVisible(cm); - cm.focus(); -} - -function getEditorInSight(nearbyElement) { - // priority: 1. associated CM for applies-to element 2. last active if visible 3. first visible - var cm; - if (nearbyElement && nearbyElement.className.indexOf("applies-") >= 0) { - cm = getSectionForChild(nearbyElement).CodeMirror; - } else { - cm = editors.lastActive; - } - if (!cm || offscreenDistance(cm) > 0) { - var sorted = editors - .map(function(cm, index) { return {cm: cm, distance: offscreenDistance(cm), index: index} }) - .sort(function(a, b) { return a.distance - b.distance || a.index - b.index }); - cm = sorted[0].cm; - if (sorted[0].distance > 0) { - makeSectionVisible(cm) - } - } - return cm; - - function offscreenDistance(cm) { - var LINES_VISIBLE = 2; // closest editor should have at least # lines visible - var bounds = cm.getSection().getBoundingClientRect(); - if (bounds.top < 0) { - return -bounds.top; - } else if (bounds.top < window.innerHeight - cm.defaultTextHeight() * LINES_VISIBLE) { - return 0; - } else { - return bounds.top - bounds.height; - } - } -} - -function updateLintReport(cm, delay) { - if (delay == 0) { - // immediately show pending csslint messages in onbeforeunload and save - update(cm); - return; - } - if (delay > 0) { - setTimeout(cm => { cm.performLint(); update(cm) }, delay, cm); - return; - } - var state = cm.state.lint; - if (!state) { - return; - } - // user is editing right now: postpone updating the report for the new issues (default: 500ms lint + 4500ms) - // or update it as soon as possible (default: 500ms lint + 100ms) in case an existing issue was just fixed - clearTimeout(state.reportTimeout); - state.reportTimeout = setTimeout(update, state.options.delay + 100, cm); - state.postponeNewIssues = delay == undefined || delay == null; - - function update(cm) { - var scope = cm ? [cm] : editors; - var changed = false; - var fixedOldIssues = false; - scope.forEach(function(cm) { - var state = cm.state.lint || {}; - var oldMarkers = state.markedLast || {}; - var newMarkers = {}; - var html = !state.marked || state.marked.length == 0 ? "" : "" + - state.marked.map(function(mark) { - var info = mark.__annotation; - var isActiveLine = info.from.line == cm.getCursor().line; - var pos = isActiveLine ? "cursor" : (info.from.line + "," + info.from.ch); - var message = escapeHtml(info.message.replace(/ at line \d.+$/, "")); - if (message.length > 100) { - message = message.substr(0, 100) + "..."; - } - if (isActiveLine || oldMarkers[pos] == message) { - delete oldMarkers[pos]; - } - newMarkers[pos] = message; - return "" + - "" + - info.severity + "" + - "" + (info.from.line+1) + "" + - ":" + - "" + (info.from.ch+1) + "" + - "" + message + ""; - }).join("") + ""; - state.markedLast = newMarkers; - fixedOldIssues |= state.reportDisplayed && Object.keys(oldMarkers).length > 0; - if (state.html != html) { - state.html = html; - changed = true; - } - }); - if (changed) { - clearTimeout(state ? state.renderTimeout : undefined); - if (!state || !state.postponeNewIssues || fixedOldIssues) { - renderLintReport(true); - } else { - state.renderTimeout = setTimeout(function() { - renderLintReport(true); - }, CodeMirror.defaults.lintReportDelay); - } - } - } - function escapeHtml(html) { - var chars = {"&": "&", "<": "<", ">": ">", '"': '"', "'": ''', "/": '/'}; - return html.replace(/[&<>"'\/]/g, function(char) { return chars[char] }); - } -} - -function renderLintReport(someBlockChanged) { - var container = document.getElementById("lint"); - var content = container.children[1]; - var label = t("sectionCode"); - var newContent = content.cloneNode(false); - var issueCount = 0; - editors.forEach(function(cm, index) { - if (cm.state.lint && cm.state.lint.html) { - var newBlock = newContent.appendChild(document.createElement("table")); - var html = "" + label + " " + (index+1) + "" + cm.state.lint.html; - newBlock.innerHTML = html; - newBlock.cm = cm; - issueCount += newBlock.rows.length; - - var block = content.children[newContent.children.length - 1]; - var blockChanged = !block || cm != block.cm || html != block.innerHTML; - someBlockChanged |= blockChanged; - cm.state.lint.reportDisplayed = blockChanged; - } - }); - if (someBlockChanged || newContent.children.length != content.children.length) { - document.getElementById('issue-count').textContent = issueCount; - container.replaceChild(newContent, content); - container.style.display = newContent.children.length ? "block" : "none"; - resizeLintReport(null, newContent); - } -} - -function resizeLintReport(event, content) { - content = content || document.getElementById("lint").children[1]; - if (content.children.length) { - var bounds = content.getBoundingClientRect(); - var newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + "px"; - if (newMaxHeight != content.style.maxHeight) { - content.style.maxHeight = newMaxHeight; - } - } -} - -function gotoLintIssue(event) { - var issue = event.target.closest("tr"); - if (!issue) { - return; - } - var block = issue.closest("table"); - makeSectionVisible(block.cm); - block.cm.focus(); - block.cm.setSelection({ - line: parseInt(issue.querySelector("td[role='line']").textContent) - 1, - ch: parseInt(issue.querySelector("td[role='col']").textContent) - 1 - }); -} - -function toggleLintReport() { - document.getElementById("lint").classList.toggle("collapsed"); -} - -function beautify(event) { - if (exports.css_beautify) { // thanks to csslint's definition of 'exports' - doBeautify(); - } else { - var script = document.head.appendChild(document.createElement("script")); - script.src = "beautify/beautify-css-mod.js"; - script.onload = doBeautify; - } - function doBeautify() { - var tabs = prefs.get("editor.indentWithTabs"); - var options = prefs.get("editor.beautify"); - options.indent_size = tabs ? 1 : prefs.get("editor.tabSize"); - options.indent_char = tabs ? "\t" : " "; - - var section = getSectionForChild(event.target); - var scope = section ? [section.CodeMirror] : editors; - - showHelp(t("styleBeautify"), "
" + - optionHtml(".selector1,", "selector_separator_newline") + - optionHtml(".selector2,", "newline_before_open_brace") + - optionHtml("{", "newline_after_open_brace") + - optionHtml("border: none;", "newline_between_properties", true) + - optionHtml("display: block;", "newline_before_close_brace", true) + - optionHtml("}", "newline_between_rules") + - "
" + - "
"); - - var undoButton = document.querySelector("#help-popup button[role='undo']"); - undoButton.textContent = t(scope.length == 1 ? "undo" : "undoGlobal"); - undoButton.addEventListener("click", function() { - var undoable = false; - scope.forEach(function(cm) { - if (cm.beautifyChange && cm.beautifyChange[cm.changeGeneration()]) { - delete cm.beautifyChange[cm.changeGeneration()]; - cm.undo(); - cm.scrollIntoView(cm.getCursor()); - undoable |= cm.beautifyChange[cm.changeGeneration()]; - } - }); - undoButton.disabled = !undoable; - }); - - scope.forEach(function(cm) { - setTimeout(function() { - const pos = options.translate_positions = - [].concat.apply([], cm.doc.sel.ranges.map(r => - [Object.assign({}, r.anchor), Object.assign({}, r.head)])); - var text = cm.getValue(); - var newText = exports.css_beautify(text, options); - if (newText != text) { - if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) { - // clear the list if last change wasn't a css-beautify - cm.beautifyChange = {}; - } - cm.setValue(newText); - const selections = []; - for (let i = 0; i < pos.length; i += 2) { - selections.push({anchor: pos[i], head: pos[i + 1]}); - } - cm.setSelections(selections); - cm.beautifyChange[cm.changeGeneration()] = true; - undoButton.disabled = false; - } - }, 0); - }); - - document.querySelector(".beautify-options").addEventListener("change", function(event) { - var value = event.target.selectedIndex > 0; - options[event.target.dataset.option] = value; - prefs.set("editor.beautify", options); - event.target.parentNode.setAttribute("newline", value.toString()); - doBeautify(); - }); - - function optionHtml(label, optionName, indent) { - var value = options[optionName]; - return "
" + - "" + label + "" + - "
"; - } - } -} - -document.addEventListener("DOMContentLoaded", init); - -function init() { - initCodeMirror(); - var params = getParams(); - if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses - // This is an add - tE("heading", "addStyleTitle"); - var section = {code: ""} - for (var i in CssToProperty) { - if (params[i]) { - section[CssToProperty[i]] = [params[i]]; - } - } - window.onload = () => { - window.onload = null; - addSection(null, section); - editors[0].setOption('lint', CodeMirror.defaults.lint); - // default to enabled - document.getElementById("enabled").checked = true - initHooks(); - }; - return; - } - // This is an edit - tE("heading", "editStyleHeading", null, false); - getStylesSafe({id: params.id}).then(styles => { - let style = styles[0]; - if (!style) { - style = {id: null, sections: []}; - history.replaceState({}, document.title, location.pathname); - } - styleId = style.id; - setStyleMeta(style); - window.onload = () => { - window.onload = null; - initWithStyle({style}); - }; - if (document.readyState != 'loading') { - window.onload(); - } - }); -} - -function setStyleMeta(style) { - document.getElementById("name").value = style.name; - document.getElementById("enabled").checked = style.enabled; - document.getElementById("url").href = style.url; -} - -function initWithStyle({style, codeIsUpdated}) { - setStyleMeta(style); - - if (codeIsUpdated === false) { - setCleanGlobal(); - updateTitle(); - return; - } - - // if this was done in response to an update, we need to clear existing sections - getSections().forEach(function(div) { div.remove(); }); - var queue = style.sections.length ? style.sections.slice() : [{code: ""}]; - var queueStart = new Date().getTime(); - // after 100ms the sections will be added asynchronously - while (new Date().getTime() - queueStart <= 100 && queue.length) { - add(); - } - (function processQueue() { - if (queue.length) { - add(); - setTimeout(processQueue, 0); - } - })(); - initHooks(); - - function add() { - var sectionDiv = addSection(null, queue.shift()); - maximizeCodeHeight(sectionDiv, !queue.length); - const cm = sectionDiv.CodeMirror; - setTimeout(() => { - cm.setOption('lint', CodeMirror.defaults.lint); - updateLintReport(cm, 0); - }, prefs.get("editor.lintDelay")); - } -} - -function initHooks() { - document.querySelectorAll("#header .style-contributor").forEach(function(node) { - node.addEventListener("change", onChange); - node.addEventListener("input", onChange); - }); - document.getElementById("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); - } - - document.querySelectorAll( - 'input:not([type]), input[type="text"], input[type="search"], input[type="number"]') - .forEach(e => e.addEventListener('mousedown', toggleContextMenuDelete)); - - setupGlobalSearch(); - setCleanGlobal(); - updateTitle(); -} - - -function toggleContextMenuDelete(event) { - if (event.button == 2 && prefs.get('editor.contextDelete')) { - chrome.contextMenus.update('editor.contextDelete', { - enabled: Boolean( - this.selectionStart != this.selectionEnd || - this.somethingSelected && this.somethingSelected() - ), - }, ignoreChromeError); - } -} - - -function maximizeCodeHeight(sectionDiv, isLast) { - var cm = sectionDiv.CodeMirror; - var stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []}; - if (!stats.cmActualHeight) { - stats.cmActualHeight = getComputedHeight(cm.display.wrapper); - } - if (!stats.sectionMarginTop) { - stats.sectionMarginTop = parseFloat(getComputedStyle(sectionDiv).marginTop); - } - var sectionTop = sectionDiv.getBoundingClientRect().top - stats.sectionMarginTop; - if (!stats.firstSectionTop) { - stats.firstSectionTop = sectionTop; - } - var extrasHeight = getComputedHeight(sectionDiv) - stats.cmActualHeight; - var cmMaxHeight = window.innerHeight - extrasHeight - sectionTop - stats.sectionMarginTop; - var cmDesiredHeight = cm.display.sizer.clientHeight + 2*cm.defaultTextHeight(); - var cmGrantableHeight = Math.max(stats.cmActualHeight, Math.min(cmMaxHeight, cmDesiredHeight)); - stats.deltas.push(cmGrantableHeight - stats.cmActualHeight); - stats.totalHeight += cmGrantableHeight + extrasHeight; - if (!isLast) { - return; - } - stats.totalHeight += stats.firstSectionTop; - if (stats.totalHeight <= window.innerHeight) { - editors.forEach(function(cm, index) { - cm.setSize(null, stats.deltas[index] + stats.cmActualHeight); - }); - return; - } - // scale heights to fill the gap between last section and bottom edge of the window - var sections = document.getElementById("sections"); - var available = window.innerHeight - sections.getBoundingClientRect().bottom - - parseFloat(getComputedStyle(sections).marginBottom); - if (available <= 0) { - return; - } - var totalDelta = stats.deltas.reduce(function(sum, d) { return sum + d; }, 0); - var q = available / totalDelta; - var baseHeight = stats.cmActualHeight - stats.sectionMarginTop; - stats.deltas.forEach(function(delta, index) { - editors[index].setSize(null, baseHeight + Math.floor(q * delta)); - }); -} - -function updateTitle() { - var DIRTY_TITLE = "* $"; - - var name = document.getElementById("name").savedValue; - var clean = isCleanGlobal(); - var title = styleId === null ? t("addStyleTitle") : t('editStyleTitle', [name]); - document.title = clean ? title : DIRTY_TITLE.replace("$", title); -} - -function validate() { - var name = document.getElementById("name").value; - if (name == "") { - return t("styleMissingName"); - } - // validate the regexps - if (document.querySelectorAll(".applies-to-list").some(function(list) { - return list.childNodes.some(function(li) { - if (li.className == template.appliesToEverything.className) { - return false; - } - var valueElement = li.querySelector("[name=applies-value]"); - var type = li.querySelector("[name=applies-type]").value; - var value = valueElement.value; - if (type && value) { - if (type == "regexp") { - try { - new RegExp(value); - } catch (ex) { - valueElement.focus(); - return true; - } - } - } - return false; - }); - })) { - return t("styleBadRegexp"); - } - return null; -} - -function save() { - updateLintReport(null, 0); - - // save the contents of the CodeMirror editors back into the textareas - for (var i=0; i < editors.length; i++) { - editors[i].save(); - } - - var error = validate(); - if (error) { - alert(error); - return; - } - var name = document.getElementById("name").value; - var enabled = document.getElementById("enabled").checked; - saveStyleSafe({ - id: styleId, - name: name, - enabled: enabled, - reason: 'editSave', - sections: getSectionsHashes() - }) - .then(saveComplete); -} - -function getSectionsHashes() { - var sections = []; - getSections().forEach(function(div) { - var meta = getMeta(div); - var code = div.CodeMirror.getValue(); - if (/^\s*$/.test(code) && Object.keys(meta).length == 0) { - return; - } - meta.code = code; - sections.push(meta); - }); - return sections; -} - -function getMeta(e) { - var meta = {urls: [], urlPrefixes: [], domains: [], regexps: []}; - e.querySelector(".applies-to-list").childNodes.forEach(function(li) { - if (li.className == template.appliesToEverything.className) { - return; - } - var type = li.querySelector("[name=applies-type]").value; - var value = li.querySelector("[name=applies-value]").value; - if (type && value) { - var property = CssToProperty[type]; - meta[property].push(value); - } - }); - return meta; -} - -function saveComplete(style) { - styleId = style.id; - setCleanGlobal(); - - // Go from new style URL to edit style URL - if (location.href.indexOf("id=") == -1) { - history.replaceState({}, document.title, "edit.html?id=" + style.id); - tE("heading", "editStyleHeading", null, false); - } - updateTitle(); -} - -function showMozillaFormat() { - var popup = showCodeMirrorPopup(t("styleToMozillaFormatTitle"), "", {readOnly: true}); - popup.codebox.setValue(toMozillaFormat()); - popup.codebox.execCommand("selectAll"); -} - -function toMozillaFormat() { - return getSectionsHashes().map(function(section) { - var cssMds = []; - for (var i in propertyToCss) { - if (section[i]) { - cssMds = cssMds.concat(section[i].map(function (v){ - return propertyToCss[i] + "(\"" + v.replace(/\\/g, "\\\\") + "\")"; - })); - } - } - return cssMds.length ? "@-moz-document " + cssMds.join(", ") + " {\n" + section.code + "\n}" : section.code; - }).join("\n\n"); -} - -function fromMozillaFormat() { - var popup = showCodeMirrorPopup(t("styleFromMozillaFormatPrompt"), tHTML("
\ - \ - \ -
").innerHTML); - - var contents = popup.querySelector(".contents"); - contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild); - popup.codebox.focus(); - - popup.querySelector("[name='import-append']").addEventListener("click", doImport); - popup.querySelector("[name='import-replace']").addEventListener("click", doImport); - - popup.codebox.on("change", function() { - clearTimeout(popup.mozillaTimeout); - popup.mozillaTimeout = setTimeout(function() { - popup.classList.toggle("ready", trimNewLines(popup.codebox.getValue())); - }, 100); - }); - - function doImport() { - var replaceOldStyle = this.name == "import-replace"; - popup.querySelector(".dismiss").onclick(); - var mozStyle = trimNewLines(popup.codebox.getValue()); - var parser = new parserlib.css.Parser(), lines = mozStyle.split("\n"); - var sectionStack = [{code: "", start: {line: 1, col: 1}}]; - var errors = "", oldSectionCount = editors.length; - var firstAddedCM; - - parser.addListener("startdocument", function(e) { - var outerText = getRange(sectionStack.last.start, (--e.col, e)); - var gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/); - var section = {code: "", start: backtrackTo(this, parserlib.css.Tokens.LBRACE, "end")}; - // move last comment before @-moz-document inside the section - if (gapComment && !gapComment[1].match(/\/\*\s*AGENT_SHEET\s*\*\//)) { - section.code = gapComment[1] + "\n"; - outerText = trimNewLines(outerText.substring(0, gapComment.index)); - } - if (outerText.trim()) { - sectionStack.last.code = outerText; - doAddSection(sectionStack.last); - sectionStack.last.code = ""; - } - e.functions.forEach(function(f) { - var m = f.match(/^(url|url-prefix|domain|regexp)\((['"]?)(.+?)\2?\)$/); - var aType = CssToProperty[m[1]]; - var aValue = aType != "regexps" ? m[3] : m[3].replace(/\\\\/g, "\\"); - (section[aType] = section[aType] || []).push(aValue); - }); - sectionStack.push(section); - }); - - parser.addListener("enddocument", function(e) { - var end = backtrackTo(this, parserlib.css.Tokens.RBRACE, "start"); - var section = sectionStack.pop(); - section.code += getRange(section.start, end); - sectionStack.last.start = (++end.col, end); - doAddSection(section); - }); - - parser.addListener("endstylesheet", function() { - // add nonclosed outer sections (either broken or the last global one) - var endOfText = {line: lines.length, col: lines.last.length + 1}; - sectionStack.last.code += getRange(sectionStack.last.start, endOfText); - sectionStack.forEach(doAddSection); - - delete maximizeCodeHeight.stats; - editors.forEach(function(cm) { - maximizeCodeHeight(cm.getSection(), cm == editors.last); - }); - - makeSectionVisible(firstAddedCM); - firstAddedCM.focus(); - - if (errors) { - showHelp(t("issues"), errors); - } - }); - - parser.addListener("error", function(e) { - errors += e.line + ":" + e.col + " " + e.message.replace(/ at line \d.+$/, "") + "
"; - }); - - parser.parse(mozStyle); - - function getRange( start, end) { - const L1 = start.line - 1, C1 = start.col - 1; - const L2 = end.line - 1, C2 = end.col - 1; - if (L1 == L2) { - return lines[L1].substr(C1, C2 - C1 + 1); - } else { - const middle = lines.slice(L1 + 1, L2).join('\n'); - return lines[L1].substr(C1) + '\n' + middle + - (L2 >= lines.length ? '' : ((middle ? '\n' : '') + lines[L2].substring(0, C2))); - } - } - function doAddSection(section) { - section.code = section.code.trim(); - // don't add empty sections - if (!section.code - && !section.urls - && !section.urlPrefixes - && !section.domains - && !section.regexps) { - return; - } - if (!firstAddedCM) { - if (!initFirstSection(section)) { - return; - } - } - setCleanItem(addSection(null, section), false); - firstAddedCM = firstAddedCM || editors.last; - } - // do onetime housekeeping as the imported text is confirmed to be a valid style - function initFirstSection(section) { - // skip adding the first global section when there's no code/comments - if (!section.code.replace("@namespace url(http://www.w3.org/1999/xhtml);", "") /* ignore boilerplate NS */ - .replace(/[\s\n]/g, "")) { /* ignore all whitespace including new lines */ - return false; - } - if (replaceOldStyle) { - editors.slice(0).reverse().forEach(function(cm) { - removeSection({target: cm.getSection().firstElementChild}); - }); - } else if (!editors.last.getValue()) { - // nuke the last blank section - if (editors.last.getSection().querySelector(".applies-to-everything")) { - removeSection({target: editors.last.getSection()}); - } - } - return true; - } - } - function backtrackTo(parser, tokenType, startEnd) { - var tokens = parser._tokenStream._lt; - for (var i = tokens.length - 2; i >= 0; --i) { - if (tokens[i].type == tokenType) { - return {line: tokens[i][startEnd+"Line"], col: tokens[i][startEnd+"Col"]}; - } - } - } - function trimNewLines(s) { - return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, ""); - } -} - -function showSectionHelp() { - showHelp(t("styleSectionsTitle"), t("sectionHelp")); -} - -function showAppliesToHelp() { - showHelp(t("appliesLabel"), t("appliesHelp")); -} - -function showToMozillaHelp() { - showHelp(t("styleMozillaFormatHeading"), t("styleToMozillaFormatHelp")); -} - -function showKeyMapHelp() { - var keyMap = mergeKeyMaps({}, prefs.get("editor.keyMap"), CodeMirror.defaults.extraKeys); - var keyMapSorted = Object.keys(keyMap) - .map(function(key) { return {key: key, cmd: keyMap[key]} }) - .concat([{key: "Shift-Ctrl-Wheel", cmd: "scrollWindow"}]) - .sort(function(a, b) { return a.cmd < b.cmd || (a.cmd == b.cmd && a.key < b.key) ? -1 : 1 }); - showHelp(t("cm_keyMap") + ": " + prefs.get("editor.keyMap"), - '' + - '' + - '' + - "" + keyMapSorted.map(function(value) { - return ""; - }).join("") + - "" + - "
" + value.key + "" + value.cmd + "
"); - - var table = document.querySelector("#help-popup table"); - table.addEventListener("input", filterTable); - - var inputs = table.querySelectorAll("input"); - inputs[0].addEventListener("keydown", hotkeyHandler); - inputs[1].focus(); - - function hotkeyHandler(event) { - var keyName = CodeMirror.keyName(event); - if (keyName == "Esc" || keyName == "Tab" || keyName == "Shift-Tab") { - return; - } - event.preventDefault(); - event.stopPropagation(); - // normalize order of modifiers, - // for modifier-only keys ("Ctrl-Shift") a dummy main key has to be temporarily added - var keyMap = {}; - keyMap[keyName.replace(/(Shift|Ctrl|Alt|Cmd)$/, "$&-dummy")] = ""; - var normalizedKey = Object.keys(CodeMirror.normalizeKeyMap(keyMap))[0]; - this.value = normalizedKey.replace("-dummy", ""); - filterTable(event); - } - function filterTable(event) { - var input = event.target; - var query = stringAsRegExp(input.value, "gi"); - var col = input.parentNode.cellIndex; - inputs[1 - col].value = ""; - table.tBodies[0].childNodes.forEach(function(row) { - var cell = row.children[col]; - cell.innerHTML = cell.textContent.replace(query, "$&"); - row.style.display = query.test(cell.textContent) ? "" : "none"; - // clear highlight from the other column - cell = row.children[1 - col]; - cell.innerHTML = cell.textContent; - }); - } - function mergeKeyMaps(merged, ...more) { - more.forEach(keyMap => { - if (typeof keyMap == "string") { - keyMap = CodeMirror.keyMap[keyMap]; - } - Object.keys(keyMap).forEach(function(key) { - var cmd = keyMap[key]; - // filter out '...', 'attach', etc. (hotkeys start with an uppercase letter) - if (!merged[key] && !key.match(/^[a-z]/) && cmd != "...") { - if (typeof cmd == "function") { - // for 'emacs' keymap: provide at least something meaningful (hotkeys and the function body) - // for 'vim*' keymaps: almost nothing as it doesn't rely on CM keymap mechanism - cmd = cmd.toString().replace(/^function.*?\{[\s\r\n]*([\s\S]+?)[\s\r\n]*\}$/, "$1"); - merged[key] = cmd.length <= 200 ? cmd : cmd.substr(0, 200) + "..."; - } else { - merged[key] = cmd; - } - } - }); - if (keyMap.fallthrough) { - merged = mergeKeyMaps(merged, keyMap.fallthrough); - } - }); - return merged; - } -} - -function showLintHelp() { - showHelp(t("issues"), t("issuesHelp") + "" - ); -} - -function showRegExpTester(event, section = getSectionForChild(this)) { - const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain='; - const OWN_ICON = chrome.runtime.getManifest().icons['16']; - const cachedRegexps = showRegExpTester.cachedRegexps = - showRegExpTester.cachedRegexps || new Map(); - const regexps = [...section.querySelector('.applies-to-list').children] - .map(item => - !item.matches('.applies-to-everything') && - item.querySelector('.applies-type').value == 'regexp' && - item.querySelector('.applies-value').value.trim()) - .filter(item => item) - .map(text => { - const rxData = Object.assign({text}, cachedRegexps.get(text)); - if (!rxData.urls) { - cachedRegexps.set(text, Object.assign(rxData, { - rx: tryRegExp(text), - urls: new Map(), - })); - } - return rxData; - }); - chrome.tabs.onUpdated.addListener(function _(tabId, info) { - if (document.querySelector('.regexp-report')) { - if (info.url) { - showRegExpTester(event, section); - } - } else { - chrome.tabs.onUpdated.removeListener(_); - } - }); - chrome.tabs.query({}, tabs => { - const supported = tabs.map(tab => tab.url) - .filter(url => URLS.supported.test(url)); - const unique = [...new Set(supported).values()]; - for (const rxData of regexps) { - const {rx, urls} = rxData; - if (rx) { - const urlsNow = new Map(); - for (const url of unique) { - const match = urls.get(url) || (url.match(rx) || [])[0]; - if (match) { - urlsNow.set(url, match); - } - } - rxData.urls = urlsNow; - } - } - const moreInfoLink = template.regexpTestPartial.outerHTML; - const stats = { - full: {data: [], label: t('styleRegexpTestFull')}, - partial: {data: [], label: t('styleRegexpTestPartial') + moreInfoLink}, - none: {data: [], label: t('styleRegexpTestNone')}, - invalid: {data: [], label: t('styleRegexpTestInvalid')}, - }; - for (const {text, rx, urls} of regexps) { - if (!rx) { - stats.invalid.data.push({text}); - continue; - } - if (!urls.size) { - stats.none.data.push({text}); - continue; - } - const full = []; - const partial = []; - for (const [url, match] of urls.entries()) { - const faviconUrl = url.startsWith(URLS.ownOrigin) - ? OWN_ICON - : GET_FAVICON_URL + new URL(url).hostname; - const icon = ``; - if (match.length == url.length) { - full.push(`
${icon + url}
`); - } else { - partial.push(`
${icon}${match}` + - url.substr(match.length) + '
'); - } - } - if (full.length) { - stats.full.data.push({text, urls: full}); - } - if (partial.length) { - stats.partial.data.push({text, urls: partial}); - } - } - showHelp(t('styleRegexpTestTitle'), - '
' + - Object.keys(stats).map(type => (!stats[type].data.length ? '' : - `
- ${stats[type].label}` + - stats[type].data.map(({text, urls}) => (!urls ? text : - `
${text}${urls.join('')}
` - )).join('
') + - '
' - )).join('') + - '
'); - document.querySelector('.regexp-report').onclick = event => { - const target = event.target.closest('a, .regexp-report div'); - if (target) { - openURL({url: target.href || target.textContent}); - event.preventDefault(); - } - }; - }); -} - -function showHelp(title, text) { - var div = document.getElementById("help-popup"); - div.classList.remove("big"); - div.querySelector(".contents").innerHTML = text; - div.querySelector(".title").innerHTML = title; - - if (getComputedStyle(div).display == "none") { - document.addEventListener("keydown", closeHelp); - div.querySelector(".dismiss").onclick = closeHelp; // avoid chaining on multiple showHelp() calls - } - - div.style.display = "block"; - return div; - - function closeHelp(e) { - if (!e - || e.type == "click" - || ((e.keyCode || e.which) == 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey)) { - div.style.display = ""; - document.querySelector(".contents").innerHTML = ""; - document.removeEventListener("keydown", closeHelp); - } - } -} - -function showCodeMirrorPopup(title, html, options) { - var popup = showHelp(title, html); - popup.classList.add("big"); - - popup.codebox = CodeMirror(popup.querySelector(".contents"), Object.assign({ - mode: "css", - lineNumbers: true, - lineWrapping: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"], - matchBrackets: true, - lint: {getAnnotations: CodeMirror.lint.css, delay: 0}, - styleActiveLine: true, - theme: prefs.get("editor.theme"), - keyMap: prefs.get("editor.keyMap") - }, options)); - popup.codebox.focus(); - popup.codebox.on("focus", function() { hotkeyRerouter.setState(false) }); - popup.codebox.on("blur", function() { hotkeyRerouter.setState(true) }); - return popup; -} - -function getParams() { - var params = {}; - var urlParts = location.href.split("?", 2); - if (urlParts.length == 1) { - return params; - } - urlParts[1].split("&").forEach(function(keyValue) { - var splitKeyValue = keyValue.split("=", 2); - params[decodeURIComponent(splitKeyValue[0])] = decodeURIComponent(splitKeyValue[1]); - }); - return params; -} - -chrome.runtime.onMessage.addListener(onRuntimeMessage); - -function onRuntimeMessage(request) { - switch (request.method) { - case "styleUpdated": - if (styleId && styleId == request.style.id && request.reason != 'editSave') { - if ((request.style.sections[0] || {}).code === null) { - // the code-less style came from notifyAllTabs - onBackgroundReady().then(() => { - request.style = BG.cachedStyles.byId.get(request.style.id); - initWithStyle(request); - }); - } else { - initWithStyle(request); - } - } - break; - case "styleDeleted": - if (styleId && styleId == request.id) { - window.onbeforeunload = function() {}; - window.close(); - break; - } - break; - case "prefChanged": - if ('editor.smartIndent' in request.prefs) { - CodeMirror.setOption('smartIndent', request.prefs['editor.smartIndent']); - } - break; - case 'editDeleteText': - document.execCommand('delete'); - break; - } -} - -function getComputedHeight(el) { - var compStyle = getComputedStyle(el); - return el.getBoundingClientRect().height + - parseFloat(compStyle.marginTop) + parseFloat(compStyle.marginBottom); -} - - -function getCodeMirrorThemes() { - if (!chrome.runtime.getPackageDirectoryEntry) { - const themes = [ - chrome.i18n.getMessage('defaultTheme'), - '3024-day', - '3024-night', - 'abcdef', - 'ambiance', - 'ambiance-mobile', - 'base16-dark', - 'base16-light', - 'bespin', - 'blackboard', - 'cobalt', - 'colorforth', - 'dracula', - 'duotone-dark', - 'duotone-light', - 'eclipse', - 'elegant', - 'erlang-dark', - 'hopscotch', - 'icecoder', - 'isotope', - 'lesser-dark', - 'liquibyte', - 'material', - 'mbo', - 'mdn-like', - 'midnight', - 'monokai', - 'neat', - 'neo', - 'night', - 'panda-syntax', - 'paraiso-dark', - 'paraiso-light', - 'pastel-on-dark', - 'railscasts', - 'rubyblue', - 'seti', - 'solarized', - 'the-matrix', - 'tomorrow-night-bright', - 'tomorrow-night-eighties', - 'ttcn', - 'twilight', - 'vibrant-ink', - 'xq-dark', - 'xq-light', - 'yeti', - 'zenburn', - ]; - localStorage.codeMirrorThemes = themes.join(' '); - return Promise.resolve(themes); - } - return new Promise(resolve => { - chrome.runtime.getPackageDirectoryEntry(rootDir => { - rootDir.getDirectory('codemirror/theme', {create: false}, themeDir => { - themeDir.createReader().readEntries(entries => { - const themes = [ - chrome.i18n.getMessage('defaultTheme') - ].concat( - entries.filter(entry => entry.isFile) - .sort((a, b) => (a.name < b.name ? -1 : 1)) - .map(entry => entry.name.replace(/\.css$/, '')) - ); - localStorage.codeMirrorThemes = themes.join(' '); - resolve(themes); - }); - }); - }); - }); -} diff --git a/edit/edit.css b/edit/edit.css new file mode 100644 index 00000000..f41e93cb --- /dev/null +++ b/edit/edit.css @@ -0,0 +1,576 @@ +body { + margin: 0; + font: 12px arial,sans-serif; +} +/************ header ************/ +#header { + width: 280px; + height: 100vh; + overflow: auto; + position: fixed; + top: 0; + padding: 15px; + border-right: 1px dashed #AAA; + -webkit-box-shadow: 0 0 3rem -1.2rem black; + box-sizing: border-box; +} +#header h1 { + margin-top: 0; +} +#sections { + padding-left: 280px; +} +#sections h2 { + margin-top: 1rem; + margin-left: 1.7rem; +} +.aligned { + display: table-row; +} +.aligned > *:not(svg) { + display: table-cell; + margin-top: 0.1rem; + min-height: 1.4rem; +} +input[type="checkbox"] { + margin-left: 0.1rem; +} +/* basic info */ +#basic-info { + margin-bottom: 1rem; +} +#name { + width: 100%; +} +#basic-info-name { + display: flex; + align-items: center; +} +#url { + margin-left: 0.25rem; +} +#url:not([href^="http"]) { + display: none; +} +#save-button { + opacity: .5; + pointer-events: none; +} +.dirty #save-button { + opacity: 1; + pointer-events: all; +} +.svg-icon { + cursor: pointer; + vertical-align: middle; + transition: fill .5s; + width: 16px; + height: 16px; +} +.svg-icon:not(.dismiss) { + margin-left: 0.2rem; +} +h2 .svg-icon, label .svg-icon { + margin-top: -1px; +} +.svg-icon.info { + width: 14px; + height: 16px; +} +.svg-icon:hover, +.svg-icon.info { + fill: #666; +} +.svg-icon, +.svg-icon.info:hover { + fill: #000; +} +#enabled { + margin-left: 0; + vertical-align: middle; +} +#enabled-label { + vertical-align: middle; +} +/* actions */ +#actions > * { + margin-right: 0.5rem; + margin-bottom: 0.5rem; +} +/* options */ +#options [type="number"] { + max-width: 2.5rem; + text-align: right; +} +#options .option > * { + padding-right: 0.25rem; +} +/************ content ***********/ +#sections > div { + margin: 0.7rem; + padding: 1rem; +} +#sections > div:not(:first-of-type) { + border-top: 2px solid black; +} +#sections > div:only-of-type .remove-section { + display: none; +} +#sections > div > button:not(:first-of-type) { + margin-left: 0.2rem; +} +.dirty > label::before { + content: "*"; + font-weight: bold; +} +#sections { + counter-reset: codebox; +} +#sections > div > label::after { + counter-increment: codebox; + content: counter(codebox); + margin-left: 0.25rem; +} +/* code */ +.CodeMirror-hint:hover { + color: white; + background: #08f; +} +.code { + height: 10rem; + width: 40rem; +} +.CodeMirror { + border: solid #CCC 1px; +} +.CodeMirror-scroll { + height: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 6px; /* resize-grip height */ +} +.CodeMirror-lint-mark-warning { + background: none; +} +.CodeMirror-vscrollbar { + margin-bottom: 7px; /* make space for resize-grip */ +} +.CodeMirror-hscrollbar { + bottom: 7px; /* make space for resize-grip */ +} +.CodeMirror-scrollbar-filler { + bottom: 7px; /* make space for resize-grip */ +} +.CodeMirror-dialog { + -webkit-animation: highlight 3s ease-out; +} +.CodeMirror-focused { + outline: -webkit-focus-ring-color auto 5px; + outline-offset: -2px; +} +.CodeMirror-search-field { + width: 10em; +} +.CodeMirror-jump-field { + width: 5em; +} +.CodeMirror-search-hint { + color: #888; +} +body[data-match-highlight="token"] .cm-matchhighlight-approved .cm-matchhighlight, +body[data-match-highlight="token"] .CodeMirror-selection-highlight-scrollbar { + animation: fadein-match-highlighter 1s cubic-bezier(.97,.01,.42,.98); + animation-fill-mode: both; +} +body[data-match-highlight="selection"] .cm-matchhighlight-approved .cm-matchhighlight, +body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar { + background-color: rgba(1, 151, 193, 0.1); +} +@-webkit-keyframes highlight { + from { + background-color: #ff9; + } + to { + background-color: none; + } +} +@keyframes fadein { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@keyframes fadein-match-highlighter { + from { background-color: transparent; } + to { background-color: rgba(1, 151, 193, 0.1); } +} +.resize-grip { + position: absolute; + display: block; + height: 6px; + content: ""; + left: 0; + right: 0; + bottom: 0; + z-index: 9; + cursor: n-resize; + background-color: inherit; + border-top-width: 1px; + border-top-style: solid; + border-top-color: inherit; +} +.resize-grip:after { + content: ""; + bottom: 2px; + left: 0; + right: 0; + margin: 0 8px; + display: block; + position: absolute; + border-top-width: 2px; + border-top-style: dotted; + border-top-color: inherit; +} +/* applies-to */ +.applies-to { + display: flex; +} +.applies-to label { + flex: auto; + margin-top: 0.2rem; +} +.applies-to ul { + flex: auto; + flex-grow: 99; + margin: 0; + padding: 0; +} +.applies-to li { + display: flex; + list-style-type: none; + align-items: center; + margin-bottom: 0.35rem; +} +.applies-to li > *:not(button) { + flex: auto; + min-height: 1.4rem; + margin-left: 0.35rem; +} +.applies-to li .add-applies-to { + visibility: hidden; + text-align: left; +} +.applies-to li:last-child .add-applies-to { + visibility: visible +} +.applies-to li .add-applies-to:first-child { + margin-left: 1rem; +} +.applies-to li .applies-value { + flex-grow: 99; + padding-left: 0.2rem; +} +.applies-to img { + vertical-align: bottom; +} +.test-regexp { + display: none; +} +.has-regexp .test-regexp { + display: inline-block; +} +.regexp-report summary, .regexp-report div { + cursor: pointer; + outline: none; +} +.regexp-report mark { + background-color: rgba(255, 255, 0, .5); +} +.regexp-report details { + margin-left: 1rem; +} +.regexp-report details:not(:last-child) { + margin-bottom: 1rem; +} +.regexp-report summary { + font-weight: bold; + margin-left: -1rem; + margin-bottom: .5rem; + outline: none; + cursor: default; +} +.regexp-report details[data-type="full"] { + color: darkgreen; +} +.regexp-report details[data-type="partial"] { + color: darkgray; +} +.regexp-report details[data-type="invalid"] { + color: maroon; +} +.regexp-report details details { + margin-left: 2rem; + margin-top: .5rem; +} +.regexp-report .svg-icon { + position: absolute; + margin-top: -1px; +} +.regexp-report details div:hover { + text-decoration: underline; + text-decoration-skip: ink; +} +.regexp-report details div img { + width: 16px; + max-height: 16px; + position: absolute; + margin-left: -20px; + margin-top: -1px; + animation: fadein 1s cubic-bezier(.03, .67, .08, .94); + animation-fill-mode: both; +} +/************ help popup ************/ +#help-popup { + top: 3rem; + right: 3rem; + max-width: 50vw; + position: fixed; + display: none; + background-color: white; + box-shadow: 3px 3px 30px rgba(0, 0, 0, 0.5); + padding: 0.5rem; + z-index: 99; +} +#help-popup.big { + max-width: 100%; + left: 3rem; +} +#help-popup.big .CodeMirror { + min-height: 2rem; + height: 70vh; +} +#help-popup .title { + font-weight: bold; + background-color: rgba(0,0,0,0.05); + margin: -0.5rem -0.5rem 0.5rem; + padding: .5rem 32px .5rem .5rem; +} +#help-popup .contents { + max-height: calc(100vh - 8rem); + overflow-y: auto; +} +#help-popup .dismiss { + position: absolute; + right: 4px; + top: .5em; +} + +.keymap-list { + font-size: 85%; + line-height: 1.0; + border-spacing: 0; + word-break: break-all; +} +.keymap-list input { + width: 100%; +} +.keymap-list tr:nth-child(odd) { + background-color: rgba(0, 0, 0, 0.07); +} +.keymap-list td:first-child { + white-space: nowrap; + font-family: monospace; + padding-right: 0.5rem; +} + +#help-popup button[name^="import"] { + line-height: 1.5rem; + padding: 0 0.5rem; + margin: 0.5rem 0 0 0.5rem; + pointer-events: none; + opacity: 0.5; + float: right; +} +#help-popup.ready button[name^="import"] { + pointer-events: all; + opacity: 1.0; +} + +/************ lint ************/ +#lint { + display: none; +} +#lint > div { + overflow-y: auto; +} +#lint table { + font-size: 100%; + border-spacing: 0; + margin-bottom: 1rem; + line-height: 1.0; +} +#lint table:last-child { + margin-bottom: 0; +} +#lint caption { + text-align: left; + font-weight: bold; +} +#lint tbody { + font-size: 85%; + cursor: pointer; +} +#lint tr:hover { + background-color: rgba(0, 0, 0, 0.1); +} +#lint td[role="severity"] { + font-size: 0; + width: 16px; + padding-right: 0.25rem; +} +#lint td[role="line"], #lint td[role="sep"] { + text-align: right; + padding-right: 0; +} +#lint td[role="col"] { + text-align: left; + padding-right: 0.25rem; +} +#lint td[role="message"] { + text-align: left; +} + +/************ CSS beautifier ************/ +.beautify-options { + white-space: nowrap; + font-family: monospace; +} +.beautify-options div { + float: left; +} +.beautify-options div[newline="true"] + div { + clear: left; +} +.beautify-options div[newline="true"] + div span[indent] { + padding-left: 2rem; +} +.beautify-options:after { + clear: both; + display: block; + content: " "; + height: 1rem; +} +.beautify-options span { + font-weight: bold; +} +.beautify-options select { + border: none; + background-color: rgba(0, 0, 0, 0.05); +} + +/************ reponsive layouts ************/ +@media(max-width:737px) { + #header { + width: auto; + height: auto; + position: inherit; + border-right: none; + border-bottom: 1px dashed #AAA; + } + #header section:not(:last-child) { + margin-bottom: 0.4rem; + } + #header input[type="checkbox"] { + vertical-align: middle; + } + h2 { + display: none; + } + #basic-info { + display: flex; + align-items: baseline; + } + #basic-info > * { + flex: auto; + } + #basic-info > *:first-child { + flex-grow: 99; + display: flex; + } + #basic-info > *:not(:last-child) { + margin-right: 0.8rem; + } + #basic-info #name { + width: auto; + flex-grow: 99; + } + #actions { + margin-top: 1rem; + } + #actions > * { + display: inline-block; + } + #options { + -webkit-column-count: 2; + } + #options .aligned > *:not(svg) { + margin: 1px 0 0 0; /* workaround the flowing-padding column bug in webkit */ + padding-right: 0.4rem; + vertical-align: baseline; + min-height: 1.4rem; + } + .option { + -webkit-column-break-inside: avoid; + } + .option label { + line-height: 1.25rem; + margin: 0; + } + #options [type="number"] { + text-align: left; /* workaround the column flow bug in webkit */ + padding-left: 0.2rem; + } + #options #tabSize-label { + position: relative; + top: 0.2rem; + } + #lint h2 { + display: block; + cursor: default; + margin-bottom: 0; + } + #lint > div { + max-height: 0; + } + #lint.collapsed > div { + display: none; + } + #lint:hover > div { + margin-top: 1em; + max-height: 30vh; + } + #sections { + padding-left: 0; + } + #sections > div { + padding: 0; + } + #sections > *:not(h2) { + padding-left: 0.4rem; + } + .applies-type { + width: 30%; + } +} +@media(max-width:500px) { + #options { + -webkit-column-count: 1; + } + #options #tabSize-label { + position: static; + } +} diff --git a/edit/edit.js b/edit/edit.js new file mode 100644 index 00000000..dbd873fd --- /dev/null +++ b/edit/edit.js @@ -0,0 +1,2121 @@ +/* eslint brace-style: 0, operator-linebreak: 0 */ +/* global CodeMirror exports parserlib CSSLint */ +'use strict'; + +let styleId = null; +let dirty = {}; // only the actually dirty items here +const editors = []; // array of all CodeMirror instances +let saveSizeOnClose; +let useHistoryBack; // use browser history back when 'back to manage' is clicked + +// direct & reverse mapping of @-moz-document keywords and internal property names +const propertyToCss = {urls: 'url', urlPrefixes: 'url-prefix', domains: 'domain', regexps: 'regexp'}; +const CssToProperty = {'url': 'urls', 'url-prefix': 'urlPrefixes', 'domain': 'domains', 'regexp': 'regexps'}; + +// if background page hasn't been loaded yet, increase the chances it has before DOMContentLoaded +onBackgroundReady(); + +// make querySelectorAll enumeration code readable +['forEach', 'some', 'indexOf', 'map'].forEach(method => { + NodeList.prototype[method] = Array.prototype[method]; +}); + +// Chrome pre-34 +Element.prototype.matches = Element.prototype.matches || Element.prototype.webkitMatchesSelector; + +// Chrome pre-41 polyfill +Element.prototype.closest = Element.prototype.closest || function (selector) { + let e; + // eslint-disable-next-line no-empty + for (e = this; e && !e.matches(selector); e = e.parentElement) {} + return e; +}; + +// eslint-disable-next-line no-extend-native +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; +}; + +// eslint-disable-next-line no-extend-native +Object.defineProperty(Array.prototype, 'last', {get: function () { return this[this.length - 1]; }}); + +// preload the theme so that CodeMirror can calculate its metrics in DOMContentLoaded->setupLivePrefs() +new MutationObserver((mutations, observer) => { + const themeElement = document.getElementById('cm-theme'); + if (themeElement) { + themeElement.href = prefs.get('editor.theme') === 'default' ? '' + : 'vendor/codemirror/theme/' + prefs.get('editor.theme') + '.css'; + observer.disconnect(); + } +}).observe(document, {subtree: true, childList: true}); + +getCodeMirrorThemes(); + +// reroute handling to nearest editor when keypress resolves to one of these commands +const hotkeyRerouter = { + commands: { + save: true, jumpToLine: true, nextEditor: true, prevEditor: true, + find: true, findNext: true, findPrev: true, replace: true, replaceAll: true, + toggleStyle: true, + }, + setState: enable => { + setTimeout(() => { + document[(enable ? 'add' : 'remove') + 'EventListener']('keydown', hotkeyRerouter.eventHandler); + }, 0); + }, + eventHandler: event => { + const keyName = CodeMirror.keyName(event); + if ( + CodeMirror.lookupKey(keyName, CodeMirror.getOption('keyMap'), handleCommand) === 'handled' || + CodeMirror.lookupKey(keyName, CodeMirror.defaults.extraKeys, handleCommand) === 'handled' + ) { + event.preventDefault(); + event.stopPropagation(); + } + function handleCommand(command) { + if (hotkeyRerouter.commands[command] === true) { + CodeMirror.commands[command](getEditorInSight(event.target)); + return true; + } + } + } +}; + +function onChange(event) { + const node = event.target; + if ('savedValue' in node) { + const currentValue = node.type === 'checkbox' ? node.checked : node.value; + setCleanItem(node, node.savedValue === currentValue); + } else { + // the manually added section's applies-to is dirty only when the value is non-empty + setCleanItem(node, node.localName !== 'input' || !node.value.trim()); + delete node.savedValue; // only valid when actually saved + } + updateTitle(); +} + +// Set .dirty on stylesheet contributors that have changed +function setDirtyClass(node, isDirty) { + node.classList.toggle('dirty', isDirty); +} + +function setCleanItem(node, isClean) { + if (!node.id) { + node.id = Date.now().toString(32).substr(-6); + } + + if (isClean) { + delete dirty[node.id]; + // code sections have .CodeMirror property + if (node.CodeMirror) { + node.savedValue = node.CodeMirror.changeGeneration(); + } else { + node.savedValue = node.type === 'checkbox' ? node.checked : node.value; + } + } else { + dirty[node.id] = true; + } + + setDirtyClass(node, !isClean); +} + +function isCleanGlobal() { + const clean = Object.keys(dirty).length === 0; + setDirtyClass(document.body, !clean); + // let saveBtn = document.getElementById('save-button') + // if (clean){ + // //saveBtn.removeAttribute('disabled'); + // }else{ + // //saveBtn.setAttribute('disabled', true); + // } + return clean; +} + +function setCleanGlobal() { + document.querySelectorAll('#header, #sections > div').forEach(setCleanSection); + dirty = {}; // forget the dirty applies-to ids from a deleted section after the style was saved +} + +function setCleanSection(section) { + section.querySelectorAll('.style-contributor').forEach(node => { setCleanItem(node, true); }); + + // #header section has no codemirror + const cm = section.CodeMirror; + if (cm) { + section.savedValue = cm.changeGeneration(); + indicateCodeChange(cm); + } +} + +function initCodeMirror() { + const CM = CodeMirror; + const isWindowsOS = navigator.appVersion.indexOf('Windows') > 0; + + // CodeMirror miserably fails on keyMap='' so let's ensure it's not + if (!prefs.get('editor.keyMap')) { + prefs.reset('editor.keyMap'); + } + + // default option values + Object.assign(CM.defaults, { + mode: 'css', + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], + matchBrackets: true, + highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, + hintOptions: {}, + lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get('editor.lintDelay')}, + lintReportDelay: prefs.get('editor.lintReportDelay'), + styleActiveLine: true, + theme: 'default', + keyMap: prefs.get('editor.keyMap'), + extraKeys: { // independent of current keyMap + 'Alt-Enter': 'toggleStyle', + 'Alt-PageDown': 'nextEditor', + 'Alt-PageUp': 'prevEditor' + } + }, prefs.get('editor.options')); + + // additional commands + CM.commands.jumpToLine = jumpToLine; + CM.commands.nextEditor = cm => { nextPrevEditor(cm, 1); }; + CM.commands.prevEditor = cm => { nextPrevEditor(cm, -1); }; + CM.commands.save = save; + CM.commands.blockComment = cm => { + cm.blockComment(cm.getCursor('from'), cm.getCursor('to'), {fullLines: false}); + }; + CM.commands.toggleStyle = toggleStyle; + + // 'basic' keymap only has basic keys by design, so we skip it + + const extraKeysCommands = {}; + Object.keys(CM.defaults.extraKeys).forEach(key => { + extraKeysCommands[CM.defaults.extraKeys[key]] = true; + }); + if (!extraKeysCommands.jumpToLine) { + CM.keyMap.sublime['Ctrl-G'] = 'jumpToLine'; + CM.keyMap.emacsy['Ctrl-G'] = 'jumpToLine'; + CM.keyMap.pcDefault['Ctrl-J'] = 'jumpToLine'; + CM.keyMap.macDefault['Cmd-J'] = 'jumpToLine'; + } + if (!extraKeysCommands.autocomplete) { + CM.keyMap.pcDefault['Ctrl-Space'] = 'autocomplete'; // will be used by 'sublime' on PC via fallthrough + CM.keyMap.macDefault['Alt-Space'] = 'autocomplete'; // OSX uses Ctrl-Space and Cmd-Space for something else + CM.keyMap.emacsy['Alt-/'] = 'autocomplete'; // copied from 'emacs' keymap + // 'vim' and 'emacs' define their own autocomplete hotkeys + } + if (!extraKeysCommands.blockComment) { + CM.keyMap.sublime['Shift-Ctrl-/'] = 'blockComment'; + } + + if (isWindowsOS) { + // 'pcDefault' keymap on Windows should have F3/Shift-F3 + if (!extraKeysCommands.findNext) { + CM.keyMap.pcDefault['F3'] = 'findNext'; + } + if (!extraKeysCommands.findPrev) { + CM.keyMap.pcDefault['Shift-F3'] = 'findPrev'; + } + + // 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 + ].forEach(remap => { + const oldKey = remap.from + char; + Object.keys(CM.keyMap).forEach(keyMapName => { + const keyMap = CM.keyMap[keyMapName]; + const command = keyMap[oldKey]; + if (!command) { + return; + } + remap.to.some(newMod => { + const newKey = newMod + char; + if (!(newKey in keyMap)) { + delete keyMap[oldKey]; + keyMap[newKey] = command; + return true; + } + }); + }); + }); + }); + } + + // user option values + CM.getOption = o => CodeMirror.defaults[o]; + CM.setOption = (o, v) => { + CodeMirror.defaults[o] = v; + editors.forEach(editor => { + editor.setOption(o, v); + }); + }; + + CM.prototype.getSection = function () { + return this.display.wrapper.parentNode; + }; + + // initialize global editor controls + function optionsHtmlFromArray(options) { + return options.map(opt => '').join(''); + } + const themeControl = document.getElementById('editor.theme'); + const themeList = localStorage.codeMirrorThemes; + if (themeList) { + themeControl.innerHTML = optionsHtmlFromArray(themeList.split(/\s+/)); + } else { + // Chrome is starting up and shows our edit.html, but the background page isn't loaded yet + const theme = prefs.get('editor.theme'); + themeControl.innerHTML = optionsHtmlFromArray([theme === 'default' ? t('defaultTheme') : theme]); + getCodeMirrorThemes().then(() => { + const themes = (localStorage.codeMirrorThemes || '').split(/\s+/); + themeControl.innerHTML = optionsHtmlFromArray(themes); + themeControl.selectedIndex = Math.max(0, themes.indexOf(theme)); + }); + } + document.getElementById('editor.keyMap').innerHTML = optionsHtmlFromArray(Object.keys(CM.keyMap).sort()); + document.getElementById('options').addEventListener('change', acmeEventListener, false); + setupLivePrefs(); + + hotkeyRerouter.setState(true); +} + +function acmeEventListener(event) { + const el = event.target; + const option = el.id.replace(/^editor\./, ''); + //console.log('acmeEventListener heard %s on %s', event.type, el.id); + if (!option) { + console.error('acmeEventListener: no "cm_option" %O', el); + return; + } + let value = el.type === 'checkbox' ? el.checked : el.value; + switch (option) { + case 'tabSize': + CodeMirror.setOption('indentUnit', Number(value)); + break; + case 'theme': { + const themeLink = document.getElementById('cm-theme'); + // use non-localized 'default' internally + if (!value || value === 'default' || value === t('defaultTheme')) { + value = 'default'; + if (prefs.get(el.id) !== value) { + prefs.set(el.id, value); + } + themeLink.href = ''; + el.selectedIndex = 0; + break; + } + const url = chrome.runtime.getURL('vendor/codemirror/theme/' + value + '.css'); + if (themeLink.href === url) { // preloaded in initCodeMirror() + break; + } + // avoid flicker: wait for the second stylesheet to load, then apply the theme + document.head.insertAdjacentHTML('beforeend', + ''); + (() => { + setTimeout(() => { + CodeMirror.setOption(option, value); + themeLink.remove(); + document.getElementById('cm-theme2').id = 'cm-theme'; + }, 100); + })(); + return; + } + case 'autocompleteOnTyping': + editors.forEach(cm => { + const onOff = el.checked ? 'on' : 'off'; + cm[onOff]('change', autocompleteOnTyping); + cm[onOff]('pick', autocompletePicked); + }); + return; + case 'matchHighlight': + switch (value) { + case 'token': + case 'selection': + document.body.dataset[option] = value; + value = {showToken: value === 'token' && /[#.\-\w]/, annotateScrollbar: true}; + break; + default: + value = null; + } + } + CodeMirror.setOption(option, value); +} + +// replace given textarea with the CodeMirror editor +function setupCodeMirror(textarea, index) { + const cm = CodeMirror.fromTextArea(textarea, {lint: null}); + const wrapper = cm.display.wrapper; + + cm.on('change', indicateCodeChange); + if (prefs.get('editor.autocompleteOnTyping')) { + cm.on('change', autocompleteOnTyping); + cm.on('pick', autocompletePicked); + } + cm.on('blur', () => { + editors.lastActive = cm; + hotkeyRerouter.setState(true); + setTimeout(() => { + wrapper.classList.toggle('CodeMirror-active', wrapper.contains(document.activeElement)); + }); + }); + cm.on('focus', () => { + hotkeyRerouter.setState(false); + wrapper.classList.add('CodeMirror-active'); + }); + cm.on('mousedown', (cm, event) => toggleContextMenuDelete.call(cm, event)); + + let lastClickTime = 0; + const resizeGrip = wrapper.appendChild(template.resizeGrip.cloneNode(true)); + resizeGrip.onmousedown = event => { + if (event.button !== 0) { + return; + } + event.preventDefault(); + if (Date.now() - lastClickTime < 500) { + lastClickTime = 0; + toggleSectionHeight(cm); + return; + } + lastClickTime = Date.now(); + const minHeight = cm.defaultTextHeight() + + cm.display.lineDiv.offsetParent.offsetTop + /* .CodeMirror-lines padding */ + wrapper.offsetHeight - wrapper.clientHeight; /* borders */ + wrapper.style.pointerEvents = 'none'; + document.body.style.cursor = 's-resize'; + function resize(e) { + const cmPageY = wrapper.getBoundingClientRect().top + window.scrollY; + const height = Math.max(minHeight, e.pageY - cmPageY); + if (height !== wrapper.clientHeight) { + cm.setSize(null, height); + } + } + document.addEventListener('mousemove', resize); + document.addEventListener('mouseup', function resizeStop() { + document.removeEventListener('mouseup', resizeStop); + document.removeEventListener('mousemove', resize); + wrapper.style.pointerEvents = ''; + document.body.style.cursor = ''; + }); + }; + + editors.splice(index || editors.length, 0, cm); + return cm; +} + +function indicateCodeChange(cm) { + const section = cm.getSection(); + setCleanItem(section, cm.isClean(section.savedValue)); + updateTitle(); + updateLintReport(cm); +} + +function getSectionForChild(e) { + return e.closest('#sections > div'); +} + +function getSections() { + return document.querySelectorAll('#sections > div'); +} + +// remind Chrome to repaint a previously invisible editor box by toggling any element's transform +// this bug is present in some versions of Chrome (v37-40 or something) +document.addEventListener('scroll', () => { + const style = document.getElementById('name').style; + style.webkitTransform = style.webkitTransform ? '' : 'scale(1)'; +}); + +// Shift-Ctrl-Wheel scrolls entire page even when mouse is over a code editor +document.addEventListener('wheel', event => { + if (event.shiftKey && event.ctrlKey && !event.altKey && !event.metaKey) { + // Chrome scrolls horizontally when Shift is pressed but on some PCs this might be different + window.scrollBy(0, event.deltaX || event.deltaY); + event.preventDefault(); + } +}); + +queryTabs({currentWindow: true}).then(tabs => { + const windowId = tabs[0].windowId; + if (prefs.get('openEditInWindow')) { + if ( + sessionStorage.saveSizeOnClose && + 'left' in prefs.get('windowPosition', {}) && + !isWindowMaximized() + ) { + // window was reopened via Ctrl-Shift-T etc. + chrome.windows.update(windowId, prefs.get('windowPosition')); + } + if (tabs.length === 1 && window.history.length === 1) { + chrome.windows.getAll(windows => { + if (windows.length > 1) { + sessionStorageHash('saveSizeOnClose').set(windowId, true); + saveSizeOnClose = true; + } + }); + } else { + saveSizeOnClose = sessionStorageHash('saveSizeOnClose').value[windowId]; + } + } + chrome.tabs.onRemoved.addListener((tabId, info) => { + sessionStorageHash('manageStylesHistory').unset(tabId); + if (info.windowId === windowId && info.isWindowClosing) { + sessionStorageHash('saveSizeOnClose').unset(windowId); + } + }); +}); + +getActiveTab().then(tab => { + useHistoryBack = sessionStorageHash('manageStylesHistory').value[tab.id] === location.href; +}); + +function goBackToManage(event) { + if (useHistoryBack) { + event.stopPropagation(); + event.preventDefault(); + history.back(); + } else if (styleId) { + sessionStorage.justEditedStyleId = styleId; + } +} + +function isWindowMaximized() { + return window.screenLeft === 0 && + window.screenTop === 0 && + window.outerWidth === screen.availWidth && + window.outerHeight === screen.availHeight; +} + +window.onbeforeunload = () => { + if (saveSizeOnClose && !isWindowMaximized()) { + prefs.set('windowPosition', { + left: screenLeft, + top: screenTop, + width: outerWidth, + height: outerHeight + }); + } + document.activeElement.blur(); + if (isCleanGlobal()) { + return; + } + updateLintReport(null, 0); + return confirm(t('styleChangesNotSaved')); +}; + +function addAppliesTo(list, name, value) { + const showingEverything = list.querySelector('.applies-to-everything') !== null; + // blow away 'Everything' if it's there + if (showingEverything) { + list.removeChild(list.firstChild); + } + 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); + } else if (showingEverything || list.hasChildNodes()) { + e = template.appliesTo.cloneNode(true); + if (list.hasChildNodes()) { + e.querySelector('[name=applies-type]').value = list.querySelector('li:last-child [name="applies-type"]').value; + } + e.querySelector('.remove-applies-to').addEventListener('click', removeAppliesTo, false); + } else { + e = template.appliesToEverything.cloneNode(true); + } + e.querySelector('.add-applies-to').addEventListener('click', function () { + addAppliesTo(this.parentNode.parentNode); + }, false); + list.appendChild(e); +} + +function addSection(event, section) { + 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); + + const codeElement = div.querySelector('.code'); + const appliesTo = div.querySelector('.applies-to-list'); + let appliesToAdded = false; + + if (section) { + codeElement.value = section.code; + for (const i in propertyToCss) { + if (section[i]) { + section[i].forEach(url => { + addAppliesTo(appliesTo, propertyToCss[i], url); + appliesToAdded = true; + }); + } + } + } + if (!appliesToAdded) { + addAppliesTo(appliesTo); + } + + appliesTo.addEventListener('change', onChange); + appliesTo.addEventListener('input', onChange); + + toggleTestRegExpVisibility(); + appliesTo.addEventListener('change', toggleTestRegExpVisibility); + div.querySelector('.test-regexp').onclick = showRegExpTester; + function toggleTestRegExpVisibility() { + const show = [...appliesTo.children].some(item => + !item.matches('.applies-to-everything') && + item.querySelector('.applies-type').value === 'regexp' && + item.querySelector('.applies-value').value.trim()); + div.classList.toggle('has-regexp', show); + appliesTo.oninput = appliesTo.oninput || show && (event => { + if ( + event.target.matches('.applies-value') && + event.target.parentElement.querySelector('.applies-type').value === 'regexp' + ) { + showRegExpTester(null, div); + } + }); + } + + const sections = document.getElementById('sections'); + let cm; + if (event) { + const clickedSection = getSectionForChild(event.target); + sections.insertBefore(div, clickedSection.nextElementSibling); + const newIndex = getSections().indexOf(clickedSection) + 1; + cm = setupCodeMirror(codeElement, newIndex); + makeSectionVisible(cm); + cm.focus(); + renderLintReport(); + } else { + sections.appendChild(div); + cm = setupCodeMirror(codeElement); + } + + div.CodeMirror = cm; + setCleanSection(div); + return div; +} + +function removeAppliesTo(event) { + const appliesTo = event.target.parentNode; + const appliesToList = appliesTo.parentNode; + removeAreaAndSetDirty(appliesTo); + if (!appliesToList.hasChildNodes()) { + addAppliesTo(appliesToList); + } +} + +function removeSection(event) { + const section = getSectionForChild(event.target); + const cm = section.CodeMirror; + removeAreaAndSetDirty(section); + editors.splice(editors.indexOf(cm), 1); + renderLintReport(); +} + +function removeAreaAndSetDirty(area) { + const contributors = area.querySelectorAll('.style-contributor'); + if (!contributors.length) { + setCleanItem(area, false); + } + contributors.some(node => { + if (node.savedValue) { + // it's a saved section, so make it dirty and stop the enumeration + setCleanItem(area, false); + return true; + } else { + // it's an empty section, so undirty the applies-to items, + // otherwise orphaned ids would keep the style dirty + setCleanItem(node, true); + } + }); + updateTitle(); + area.parentNode.removeChild(area); +} + +function makeSectionVisible(cm) { + const section = cm.getSection(); + const bounds = section.getBoundingClientRect(); + if ( + (bounds.bottom > window.innerHeight && bounds.top > 0) || + (bounds.top < 0 && bounds.bottom < window.innerHeight) + ) { + if (bounds.top < 0) { + window.scrollBy(0, bounds.top - 1); + } else { + window.scrollBy(0, bounds.bottom - window.innerHeight + 1); + } + } +} + +function setupGlobalSearch() { + const originalCommand = { + find: CodeMirror.commands.find, + findNext: CodeMirror.commands.findNext, + findPrev: CodeMirror.commands.findPrev, + replace: CodeMirror.commands.replace + }; + const originalOpenDialog = CodeMirror.prototype.openDialog; + const originalOpenConfirm = CodeMirror.prototype.openConfirm; + + let curState; // cm.state.search for last used 'find' + + function shouldIgnoreCase(query) { // treat all-lowercase non-regexp queries as case-insensitive + return typeof query === 'string' && query === query.toLowerCase(); + } + + function updateState(cm, newState) { + if (!newState) { + if (cm.state.search) { + return cm.state.search; + } + if (!curState) { + return null; + } + newState = curState; + } + cm.state.search = { + query: newState.query, + overlay: newState.overlay, + annotate: cm.showMatchesOnScrollbar(newState.query, shouldIgnoreCase(newState.query)) + }; + cm.addOverlay(newState.overlay); + return cm.state.search; + } + + // temporarily overrides the original openDialog with the provided template's innerHTML + function customizeOpenDialog(cm, template, callback) { + cm.openDialog = (tmpl, cb, opt) => { + // invoke 'callback' and bind 'this' to the original callback + originalOpenDialog.call(cm, template.innerHTML, callback.bind(cb), opt); + }; + setTimeout(() => { cm.openDialog = originalOpenDialog; }, 0); + refocusMinidialog(cm); + } + + function focusClosestCM(activeCM) { + editors.lastActive = activeCM; + const cm = getEditorInSight(); + if (cm !== activeCM) { + cm.focus(); + } + return cm; + } + + function find(activeCM) { + activeCM = focusClosestCM(activeCM); + customizeOpenDialog(activeCM, template.find, function (query) { + this(query); + curState = activeCM.state.search; + if (editors.length === 1 || !curState.query) { + return; + } + editors.forEach(cm => { + if (cm !== activeCM) { + cm.execCommand('clearSearch'); + updateState(cm, curState); + } + }); + if (CodeMirror.cmpPos(curState.posFrom, curState.posTo) === 0) { + findNext(activeCM); + } + }); + originalCommand.find(activeCM); + } + + function findNext(activeCM, reverse) { + let state = updateState(activeCM); + if (!state || !state.query) { + find(activeCM); + return; + } + let pos = activeCM.getCursor(reverse ? 'from' : 'to'); + activeCM.setSelection(activeCM.getCursor()); // clear the selection, don't move the cursor + + const rxQuery = typeof state.query === 'object' + ? state.query : stringAsRegExp(state.query, shouldIgnoreCase(state.query) ? 'i' : ''); + + if ( + document.activeElement && + document.activeElement.name === 'applies-value' && + searchAppliesTo(activeCM) + ) { + return; + } + let cm = activeCM; + for (let i = 0; i < editors.length; i++) { + state = updateState(cm); + if (!cm.hasFocus()) { + pos = reverse ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(0, 0); + } + const searchCursor = cm.getSearchCursor(state.query, pos, shouldIgnoreCase(state.query)); + if (searchCursor.find(reverse)) { + if (editors.length > 1) { + makeSectionVisible(cm); + cm.focus(); + } + // speedup the original findNext + state.posFrom = reverse ? searchCursor.to() : searchCursor.from(); + state.posTo = CodeMirror.Pos(state.posFrom.line, state.posFrom.ch); + originalCommand[reverse ? 'findPrev' : 'findNext'](cm); + return; + } else if (!reverse && searchAppliesTo(cm)) { + return; + } + cm = editors[(editors.indexOf(cm) + (reverse ? -1 + editors.length : 1)) % editors.length]; + if (reverse && searchAppliesTo(cm)) { + return; + } + } + // nothing found so far, so call the original search with wrap-around + originalCommand[reverse ? 'findPrev' : 'findNext'](activeCM); + + function searchAppliesTo(cm) { + let inputs = [].slice.call(cm.getSection().querySelectorAll('.applies-value')); + if (reverse) { + inputs = inputs.reverse(); + } + inputs.splice(0, inputs.indexOf(document.activeElement) + 1); + return inputs.some(input => { + const match = rxQuery.exec(input.value); + if (match) { + input.focus(); + const end = match.index + match[0].length; + // scroll selected part into view in long inputs, + // works only outside of current event handlers chain, hence timeout=0 + setTimeout(() => { + input.setSelectionRange(end, end); + input.setSelectionRange(match.index, end); + }, 0); + return true; + } + }); + } + } + + function findPrev(cm) { + findNext(cm, true); + } + + function replace(activeCM, all) { + let queue; + let query; + let replacement; + activeCM = focusClosestCM(activeCM); + customizeOpenDialog(activeCM, template[all ? 'replaceAll' : 'replace'], txt => { + query = txt; + customizeOpenDialog(activeCM, template.replaceWith, txt => { + replacement = txt; + queue = editors.rotate(-editors.indexOf(activeCM)); + if (all) { + editors.forEach(doReplace); + } else { + doReplace(); + } + }); + this(query); + }); + originalCommand.replace(activeCM, all); + + function doReplace() { + const cm = queue.shift(); + if (!cm) { + if (!all) { + editors.lastActive.focus(); + } + return; + } + // hide the first two dialogs (replace, replaceWith) + cm.openDialog = (tmpl, callback) => { + cm.openDialog = (tmpl, callback) => { + cm.openDialog = originalOpenDialog; + if (all) { + callback(replacement); + } else { + doConfirm(cm); + callback(replacement); + if (!cm.getWrapperElement().querySelector('.CodeMirror-dialog')) { + // no dialog == nothing found in the current CM, move to the next + doReplace(); + } + } + }; + callback(query); + }; + originalCommand.replace(cm, all); + } + function doConfirm(cm) { + let wrapAround = false; + const origPos = cm.getCursor(); + cm.openConfirm = function overrideConfirm(tmpl, callbacks, opt) { + const ovrCallbacks = callbacks.map(callback => () => { + makeSectionVisible(cm); + cm.openConfirm = overrideConfirm; + setTimeout(() => { cm.openConfirm = originalOpenConfirm; }, 0); + + const pos = cm.getCursor(); + callback(); + const cmp = CodeMirror.cmpPos(cm.getCursor(), pos); + wrapAround |= cmp <= 0; + + const dlg = cm.getWrapperElement().querySelector('.CodeMirror-dialog'); + if (!dlg || cmp === 0 || wrapAround && CodeMirror.cmpPos(cm.getCursor(), origPos) >= 0) { + if (dlg) { + dlg.remove(); + } + doReplace(); + } + }); + originalOpenConfirm.call(cm, template.replaceConfirm.innerHTML, ovrCallbacks, opt); + }; + } + } + + function replaceAll(cm) { + replace(cm, true); + } + + CodeMirror.commands.find = find; + CodeMirror.commands.findNext = findNext; + CodeMirror.commands.findPrev = findPrev; + CodeMirror.commands.replace = replace; + CodeMirror.commands.replaceAll = replaceAll; +} + +function jumpToLine(cm) { + const cur = cm.getCursor(); + refocusMinidialog(cm); + cm.openDialog(template.jumpToLine.innerHTML, str => { + const m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/); + if (m) { + cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch); + } + }, {value: cur.line + 1}); +} + +function toggleStyle() { + $('#enabled').checked = !$('#enabled').checked; + save(); +} + +function toggleSectionHeight(cm) { + if (cm.state.toggleHeightSaved) { + // restore previous size + cm.setSize(null, cm.state.toggleHeightSaved); + cm.state.toggleHeightSaved = 0; + } else { + // maximize + const wrapper = cm.display.wrapper; + const allBounds = $('#sections').getBoundingClientRect(); + const pageExtrasHeight = allBounds.top + window.scrollY + + parseFloat(getComputedStyle($('#sections')).paddingBottom); + const sectionExtrasHeight = cm.getSection().clientHeight - wrapper.offsetHeight; + cm.state.toggleHeightSaved = wrapper.clientHeight; + cm.setSize(null, window.innerHeight - sectionExtrasHeight - pageExtrasHeight); + const bounds = cm.getSection().getBoundingClientRect(); + if (bounds.top < 0 || bounds.bottom > window.innerHeight) { + window.scrollBy(0, bounds.top); + } + } +} + +function autocompleteOnTyping(cm, info, debounced) { + if ( + cm.state.completionActive || + info.origin && !info.origin.includes('input') || + !info.text.last + ) { + return; + } + if (cm.state.autocompletePicked) { + cm.state.autocompletePicked = false; + return; + } + if (!debounced) { + debounce(autocompleteOnTyping, 100, cm, info, true); + return; + } + if (info.text.last.match(/[-\w!]+$/)) { + cm.state.autocompletePicked = false; + cm.options.hintOptions.completeSingle = false; + cm.execCommand('autocomplete'); + setTimeout(() => { + cm.options.hintOptions.completeSingle = true; + }); + } +} + +function autocompletePicked(cm) { + cm.state.autocompletePicked = true; +} + +function refocusMinidialog(cm) { + const section = cm.getSection(); + if (!section.querySelector('.CodeMirror-dialog')) { + return; + } + // close the currently opened minidialog + cm.focus(); + // make sure to focus the input in newly opened minidialog + setTimeout(() => { + section.querySelector('.CodeMirror-dialog').focus(); + }, 0); +} + +function nextPrevEditor(cm, direction) { + cm = editors[(editors.indexOf(cm) + direction + editors.length) % editors.length]; + makeSectionVisible(cm); + cm.focus(); +} + +function getEditorInSight(nearbyElement) { + // priority: 1. associated CM for applies-to element 2. last active if visible 3. first visible + let cm; + if (nearbyElement && nearbyElement.className.indexOf('applies-') >= 0) { + cm = getSectionForChild(nearbyElement).CodeMirror; + } else { + cm = editors.lastActive; + } + if (!cm || offscreenDistance(cm) > 0) { + const sorted = editors + .map((cm, index) => ({cm: cm, distance: offscreenDistance(cm), index: index})) + .sort((a, b) => a.distance - b.distance || a.index - b.index); + cm = sorted[0].cm; + if (sorted[0].distance > 0) { + makeSectionVisible(cm); + } + } + return cm; + + function offscreenDistance(cm) { + const LINES_VISIBLE = 2; // closest editor should have at least # lines visible + const bounds = cm.getSection().getBoundingClientRect(); + if (bounds.top < 0) { + return -bounds.top; + } else if (bounds.top < window.innerHeight - cm.defaultTextHeight() * LINES_VISIBLE) { + return 0; + } else { + return bounds.top - bounds.height; + } + } +} + +function updateLintReport(cm, delay) { + if (delay === 0) { + // immediately show pending csslint messages in onbeforeunload and save + update(cm); + return; + } + if (delay > 0) { + setTimeout(cm => { cm.performLint(); update(cm); }, delay, cm); + return; + } + // 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 ? '' : '' + + 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 '' + + '' + + info.severity + '' + + '' + (info.from.line + 1) + '' + + ':' + + '' + (info.from.ch + 1) + '' + + '' + message + ''; + }).join('') + ''; + 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 = {'&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/'}; + 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 newBlock = newContent.appendChild(document.createElement('table')); + const html = '' + label + ' ' + (index + 1) + '' + cm.state.lint.html; + newBlock.innerHTML = html; + 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; + } + function doBeautify() { + const tabs = prefs.get('editor.indentWithTabs'); + const options = prefs.get('editor.beautify'); + options.indent_size = tabs ? 1 : prefs.get('editor.tabSize'); + options.indent_char = tabs ? '\t' : ' '; + + const section = getSectionForChild(event.target); + const scope = section ? [section.CodeMirror] : editors; + + showHelp(t('styleBeautify'), '
' + + optionHtml('.selector1,', 'selector_separator_newline') + + optionHtml('.selector2,', 'newline_before_open_brace') + + optionHtml('{', 'newline_after_open_brace') + + optionHtml('border: none;', 'newline_between_properties', true) + + optionHtml('display: block;', 'newline_before_close_brace', true) + + optionHtml('}', 'newline_between_rules') + + `' + + '
' + + '
'); + + const undoButton = document.querySelector('#help-popup button[role="undo"]'); + undoButton.textContent = t(scope.length === 1 ? 'undo' : 'undoGlobal'); + undoButton.addEventListener('click', () => { + let undoable = false; + scope.forEach(cm => { + if (cm.beautifyChange && cm.beautifyChange[cm.changeGeneration()]) { + delete cm.beautifyChange[cm.changeGeneration()]; + cm.undo(); + cm.scrollIntoView(cm.getCursor()); + undoable |= cm.beautifyChange[cm.changeGeneration()]; + } + }); + undoButton.disabled = !undoable; + }); + + scope.forEach(cm => { + setTimeout(() => { + const pos = options.translate_positions = + [].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); + if (newText !== text) { + if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) { + // clear the list if last change wasn't a css-beautify + cm.beautifyChange = {}; + } + cm.setValue(newText); + const selections = []; + for (let i = 0; i < pos.length; i += 2) { + selections.push({anchor: pos[i], head: pos[i + 1]}); + } + cm.setSelections(selections); + cm.beautifyChange[cm.changeGeneration()] = true; + undoButton.disabled = false; + } + }, 0); + }); + + document.querySelector('.beautify-options').onchange = ({target}) => { + const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0; + prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value})); + if (target.parentNode.hasAttribute('newline')) { + target.parentNode.setAttribute('newline', value.toString()); + } + doBeautify(); + }; + + function optionHtml(label, optionName, indent) { + const value = options[optionName]; + return '
' + + '' + label + '' + + '
'; + } + } +} + +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 + // This is an add + tE('heading', 'addStyleTitle'); + const section = {code: ''}; + for (const i in CssToProperty) { + if (params[i]) { + section[CssToProperty[i]] = [params[i]]; + } + } + window.onload = () => { + window.onload = null; + addSection(null, section); + editors[0].setOption('lint', CodeMirror.defaults.lint); + // default to enabled + document.getElementById('enabled').checked = true; + initHooks(); + }; + return; + } + // This is an edit + tE('heading', 'editStyleHeading', null, false); + getStylesSafe({id: params.id}).then(styles => { + let style = styles[0]; + if (!style) { + style = {id: null, sections: []}; + history.replaceState({}, document.title, location.pathname); + } + styleId = style.id; + setStyleMeta(style); + window.onload = () => { + window.onload = null; + initWithStyle({style}); + }; + if (document.readyState !== 'loading') { + window.onload(); + } + }); +} + +function setStyleMeta(style) { + document.getElementById('name').value = style.name; + document.getElementById('enabled').checked = style.enabled; + document.getElementById('url').href = style.url; +} + +function initWithStyle({style, codeIsUpdated}) { + setStyleMeta(style); + + if (codeIsUpdated === false) { + setCleanGlobal(); + updateTitle(); + return; + } + + // if this was done in response to an update, we need to clear existing sections + getSections().forEach(div => { div.remove(); }); + const queue = style.sections.length ? style.sections.slice() : [{code: ''}]; + const queueStart = new Date().getTime(); + // after 100ms the sections will be added asynchronously + while (new Date().getTime() - queueStart <= 100 && queue.length) { + add(); + } + (function processQueue() { + if (queue.length) { + add(); + setTimeout(processQueue, 0); + } + })(); + initHooks(); + + function add() { + 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')); + } +} + +function initHooks() { + document.querySelectorAll('#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); + } + + document.querySelectorAll( + 'input:not([type]), input[type="text"], input[type="search"], input[type="number"]') + .forEach(e => e.addEventListener('mousedown', toggleContextMenuDelete)); + + setupGlobalSearch(); + setCleanGlobal(); + updateTitle(); +} + + +function toggleContextMenuDelete(event) { + if (event.button === 2 && prefs.get('editor.contextDelete')) { + chrome.contextMenus.update('editor.contextDelete', { + enabled: Boolean( + this.selectionStart !== this.selectionEnd || + this.somethingSelected && this.somethingSelected() + ), + }, ignoreChromeError); + } +} + + +function maximizeCodeHeight(sectionDiv, isLast) { + const cm = sectionDiv.CodeMirror; + const stats = maximizeCodeHeight.stats = maximizeCodeHeight.stats || {totalHeight: 0, deltas: []}; + if (!stats.cmActualHeight) { + stats.cmActualHeight = getComputedHeight(cm.display.wrapper); + } + if (!stats.sectionMarginTop) { + stats.sectionMarginTop = parseFloat(getComputedStyle(sectionDiv).marginTop); + } + const sectionTop = sectionDiv.getBoundingClientRect().top - stats.sectionMarginTop; + if (!stats.firstSectionTop) { + stats.firstSectionTop = sectionTop; + } + const extrasHeight = getComputedHeight(sectionDiv) - stats.cmActualHeight; + const cmMaxHeight = window.innerHeight - extrasHeight - sectionTop - stats.sectionMarginTop; + const cmDesiredHeight = cm.display.sizer.clientHeight + 2 * cm.defaultTextHeight(); + const cmGrantableHeight = Math.max(stats.cmActualHeight, Math.min(cmMaxHeight, cmDesiredHeight)); + stats.deltas.push(cmGrantableHeight - stats.cmActualHeight); + stats.totalHeight += cmGrantableHeight + extrasHeight; + if (!isLast) { + return; + } + stats.totalHeight += stats.firstSectionTop; + if (stats.totalHeight <= window.innerHeight) { + editors.forEach((cm, index) => { + cm.setSize(null, stats.deltas[index] + stats.cmActualHeight); + }); + return; + } + // scale heights to fill the gap between last section and bottom edge of the window + const sections = document.getElementById('sections'); + const available = window.innerHeight - sections.getBoundingClientRect().bottom - + parseFloat(getComputedStyle(sections).marginBottom); + if (available <= 0) { + return; + } + const totalDelta = stats.deltas.reduce((sum, d) => sum + d, 0); + const q = available / totalDelta; + const baseHeight = stats.cmActualHeight - stats.sectionMarginTop; + stats.deltas.forEach((delta, index) => { + editors[index].setSize(null, baseHeight + Math.floor(q * delta)); + }); +} + +function updateTitle() { + const DIRTY_TITLE = '* $'; + + const name = document.getElementById('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; + if (name === '') { + return t('styleMissingName'); + } + // validate the regexps + if (document.querySelectorAll('.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 value = valueElement.value; + if (type && value) { + if (type === 'regexp') { + try { + new RegExp(value); + } catch (ex) { + valueElement.focus(); + return true; + } + } + } + return false; + }); + })) { + return t('styleBadRegexp'); + } + return null; +} + +function save() { + updateLintReport(null, 0); + + // save the contents of the CodeMirror editors back into the textareas + for (let i = 0; i < editors.length; i++) { + editors[i].save(); + } + + const error = validate(); + if (error) { + alert(error); + return; + } + const name = document.getElementById('name').value; + const enabled = document.getElementById('enabled').checked; + saveStyleSafe({ + id: styleId, + name: name, + enabled: enabled, + reason: 'editSave', + sections: getSectionsHashes() + }) + .then(saveComplete); +} + +function getSectionsHashes() { + const sections = []; + getSections().forEach(div => { + const meta = getMeta(div); + const code = div.CodeMirror.getValue(); + if (/^\s*$/.test(code) && Object.keys(meta).length === 0) { + return; + } + meta.code = code; + sections.push(meta); + }); + return sections; +} + +function getMeta(e) { + const meta = {urls: [], urlPrefixes: [], domains: [], regexps: []}; + e.querySelector('.applies-to-list').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; + if (type && value) { + const property = CssToProperty[type]; + meta[property].push(value); + } + }); + return meta; +} + +function saveComplete(style) { + styleId = style.id; + setCleanGlobal(); + + // Go from new style URL to edit style URL + if (location.href.indexOf('id=') === -1) { + history.replaceState({}, document.title, 'edit.html?id=' + style.id); + tE('heading', 'editStyleHeading', null, false); + } + updateTitle(); +} + +function showMozillaFormat() { + const popup = showCodeMirrorPopup(t('styleToMozillaFormatTitle'), '', {readOnly: true}); + popup.codebox.setValue(toMozillaFormat()); + popup.codebox.execCommand('selectAll'); +} + +function toMozillaFormat() { + return getSectionsHashes().map(section => { + let cssMds = []; + for (const i in propertyToCss) { + if (section[i]) { + cssMds = cssMds.concat(section[i].map(v => + propertyToCss[i] + '("' + v.replace(/\\/g, '\\\\') + '")' + )); + } + } + return cssMds.length ? '@-moz-document ' + cssMds.join(', ') + ' {\n' + section.code + '\n}' : section.code; + }).join('\n\n'); +} + +function fromMozillaFormat() { + const popup = showCodeMirrorPopup(t('styleFromMozillaFormatPrompt'), tHTML(`
+ + +
` + ).innerHTML); + + const contents = popup.querySelector('.contents'); + contents.insertBefore(popup.codebox.display.wrapper, contents.firstElementChild); + popup.codebox.focus(); + + popup.querySelector('[name="import-append"]').addEventListener('click', doImport); + popup.querySelector('[name="import-replace"]').addEventListener('click', doImport); + + popup.codebox.on('change', () => { + clearTimeout(popup.mozillaTimeout); + popup.mozillaTimeout = setTimeout(() => { + popup.classList.toggle('ready', trimNewLines(popup.codebox.getValue())); + }, 100); + }); + + function doImport() { + const replaceOldStyle = this.name === 'import-replace'; + popup.querySelector('.dismiss').onclick(); + const mozStyle = trimNewLines(popup.codebox.getValue()); + const parser = new parserlib.css.Parser(); + const lines = mozStyle.split('\n'); + const sectionStack = [{code: '', start: {line: 1, col: 1}}]; + const errors = []; + // let oldSectionCount = editors.length; + let firstAddedCM; + + parser.addListener('startdocument', function (e) { + let outerText = getRange(sectionStack.last.start, (--e.col, e)); + const gapComment = outerText.match(/(\/\*[\s\S]*?\*\/)[\s\n]*$/); + const section = {code: '', start: backtrackTo(this, parserlib.css.Tokens.LBRACE, 'end')}; + // move last comment before @-moz-document inside the section + if (gapComment && !gapComment[1].match(/\/\*\s*AGENT_SHEET\s*\*\//)) { + section.code = gapComment[1] + '\n'; + outerText = trimNewLines(outerText.substring(0, gapComment.index)); + } + if (outerText.trim()) { + sectionStack.last.code = outerText; + doAddSection(sectionStack.last); + sectionStack.last.code = ''; + } + for (const f of e.functions) { + const m = f && f.match(/^([\w-]*)\((['"]?)(.+?)\2?\)$/); + if (!m || !/^(url|url-prefix|domain|regexp)$/.test(m[1])) { + errors.push(`${e.line}:${e.col + 1} invalid function "${m ? m[1] : f || ''}"`); + continue; + } + const aType = CssToProperty[m[1]]; + const aValue = aType !== 'regexps' ? m[3] : m[3].replace(/\\\\/g, '\\'); + (section[aType] = section[aType] || []).push(aValue); + } + sectionStack.push(section); + }); + + parser.addListener('enddocument', function () { + const end = backtrackTo(this, parserlib.css.Tokens.RBRACE, 'start'); + const section = sectionStack.pop(); + section.code += getRange(section.start, end); + sectionStack.last.start = (++end.col, end); + doAddSection(section); + }); + + parser.addListener('endstylesheet', () => { + // add nonclosed outer sections (either broken or the last global one) + const endOfText = {line: lines.length, col: lines.last.length + 1}; + sectionStack.last.code += getRange(sectionStack.last.start, endOfText); + sectionStack.forEach(doAddSection); + + delete maximizeCodeHeight.stats; + editors.forEach(cm => { + maximizeCodeHeight(cm.getSection(), cm === editors.last); + }); + + makeSectionVisible(firstAddedCM); + firstAddedCM.focus(); + + if (errors) { + showHelp(t('issues'), $element({ + tag: 'pre', + textContent: errors.join('\n'), + })); + } + }); + + parser.addListener('error', e => { + errors.push(e.line + ':' + e.col + ' ' + + e.message.replace(/ at line \d.+$/, '')); + }); + + parser.parse(mozStyle); + + function getRange(start, end) { + const L1 = start.line - 1; + const C1 = start.col - 1; + const L2 = end.line - 1; + const C2 = end.col - 1; + if (L1 === L2) { + return lines[L1].substr(C1, C2 - C1 + 1); + } else { + const middle = lines.slice(L1 + 1, L2).join('\n'); + return lines[L1].substr(C1) + '\n' + middle + + (L2 >= lines.length ? '' : ((middle ? '\n' : '') + lines[L2].substring(0, C2))); + } + } + function doAddSection(section) { + section.code = section.code.trim(); + // don't add empty sections + if ( + !section.code && + !section.urls && + !section.urlPrefixes && + !section.domains && + !section.regexps + ) { + return; + } + if (!firstAddedCM) { + if (!initFirstSection(section)) { + return; + } + } + setCleanItem(addSection(null, section), false); + firstAddedCM = firstAddedCM || editors.last; + } + // do onetime housekeeping as the imported text is confirmed to be a valid style + function initFirstSection(section) { + // skip adding the first global section when there's no code/comments + if (!section.code.replace('@namespace url(http://www.w3.org/1999/xhtml);', '') /* ignore boilerplate NS */ + .replace(/[\s\n]/g, '')) { /* ignore all whitespace including new lines */ + return false; + } + if (replaceOldStyle) { + editors.slice(0).reverse().forEach(cm => { + removeSection({target: cm.getSection().firstElementChild}); + }); + } else if (!editors.last.getValue()) { + // nuke the last blank section + if (editors.last.getSection().querySelector('.applies-to-everything')) { + removeSection({target: editors.last.getSection()}); + } + } + return true; + } + } + function backtrackTo(parser, tokenType, startEnd) { + const tokens = parser._tokenStream._lt; + for (let i = parser._tokenStream._ltIndex - 1; i >= 0; --i) { + if (tokens[i].type === tokenType) { + return {line: tokens[i][startEnd + 'Line'], col: tokens[i][startEnd + 'Col']}; + } + } + } + function trimNewLines(s) { + return s.replace(/^[\s\n]+/, '').replace(/[\s\n]+$/, ''); + } +} + +function showSectionHelp() { + showHelp(t('styleSectionsTitle'), t('sectionHelp')); +} + +function showAppliesToHelp() { + showHelp(t('appliesLabel'), t('appliesHelp')); +} + +function showToMozillaHelp() { + showHelp(t('styleMozillaFormatHeading'), t('styleToMozillaFormatHelp')); +} + +function showToggleStyleHelp() { + showHelp(t('helpAlt'), t('styleEnabledToggleHint')); +} + +function showKeyMapHelp() { + const keyMap = mergeKeyMaps({}, prefs.get('editor.keyMap'), CodeMirror.defaults.extraKeys); + const keyMapSorted = Object.keys(keyMap) + .map(key => ({key: key, cmd: keyMap[key]})) + .concat([{key: 'Shift-Ctrl-Wheel', cmd: 'scrollWindow'}]) + .sort((a, b) => (a.cmd < b.cmd || (a.cmd === b.cmd && a.key < b.key) ? -1 : 1)); + showHelp(t('cm_keyMap') + ': ' + prefs.get('editor.keyMap'), + '' + + '' + + '' + + '' + keyMapSorted.map(value => + '' + ).join('') + + '' + + '
' + value.key + '' + value.cmd + '
'); + + const table = document.querySelector('#help-popup table'); + table.addEventListener('input', filterTable); + + const inputs = table.querySelectorAll('input'); + inputs[0].addEventListener('keydown', hotkeyHandler); + inputs[1].focus(); + + function hotkeyHandler(event) { + const keyName = CodeMirror.keyName(event); + if (keyName === 'Esc' || keyName === 'Tab' || keyName === 'Shift-Tab') { + return; + } + event.preventDefault(); + event.stopPropagation(); + // normalize order of modifiers, + // for modifier-only keys ('Ctrl-Shift') a dummy main key has to be temporarily added + const keyMap = {}; + keyMap[keyName.replace(/(Shift|Ctrl|Alt|Cmd)$/, '$&-dummy')] = ''; + const normalizedKey = Object.keys(CodeMirror.normalizeKeyMap(keyMap))[0]; + this.value = normalizedKey.replace('-dummy', ''); + filterTable(event); + } + + function filterTable(event) { + const input = event.target; + const query = stringAsRegExp(input.value, 'gi'); + const col = input.parentNode.cellIndex; + inputs[1 - col].value = ''; + table.tBodies[0].childNodes.forEach(row => { + let cell = row.children[col]; + cell.innerHTML = cell.textContent.replace(query, '$&'); + row.style.display = query.test(cell.textContent) ? '' : 'none'; + // clear highlight from the other column + cell = row.children[1 - col]; + cell.innerHTML = cell.textContent; + }); + } + function mergeKeyMaps(merged, ...more) { + more.forEach(keyMap => { + if (typeof keyMap === 'string') { + keyMap = CodeMirror.keyMap[keyMap]; + } + Object.keys(keyMap).forEach(key => { + let cmd = keyMap[key]; + // filter out '...', 'attach', etc. (hotkeys start with an uppercase letter) + if (!merged[key] && !key.match(/^[a-z]/) && cmd !== '...') { + if (typeof cmd === 'function') { + // for 'emacs' keymap: provide at least something meaningful (hotkeys and the function body) + // for 'vim*' keymaps: almost nothing as it doesn't rely on CM keymap mechanism + cmd = cmd.toString().replace(/^function.*?\{[\s\r\n]*([\s\S]+?)[\s\r\n]*\}$/, '$1'); + merged[key] = cmd.length <= 200 ? cmd : cmd.substr(0, 200) + '...'; + } else { + merged[key] = cmd; + } + } + }); + if (keyMap.fallthrough) { + merged = mergeKeyMaps(merged, keyMap.fallthrough); + } + }); + return merged; + } +} + +function showLintHelp() { + showHelp(t('issues'), t('issuesHelp') + '' + ); +} + +function showRegExpTester(event, section = getSectionForChild(this)) { + const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain='; + const OWN_ICON = chrome.runtime.getManifest().icons['16']; + const cachedRegexps = showRegExpTester.cachedRegexps = + showRegExpTester.cachedRegexps || new Map(); + const regexps = [...section.querySelector('.applies-to-list').children] + .map(item => + !item.matches('.applies-to-everything') && + item.querySelector('.applies-type').value === 'regexp' && + item.querySelector('.applies-value').value.trim()) + .filter(item => item) + .map(text => { + const rxData = Object.assign({text}, cachedRegexps.get(text)); + if (!rxData.urls) { + cachedRegexps.set(text, Object.assign(rxData, { + rx: tryRegExp(text), + urls: new Map(), + })); + } + return rxData; + }); + chrome.tabs.onUpdated.addListener(function _(tabId, info) { + if (document.querySelector('.regexp-report')) { + if (info.url) { + showRegExpTester(event, section); + } + } else { + chrome.tabs.onUpdated.removeListener(_); + } + }); + queryTabs().then(tabs => { + const supported = tabs.map(tab => tab.url) + .filter(url => URLS.supported.test(url)); + const unique = [...new Set(supported).values()]; + for (const rxData of regexps) { + const {rx, urls} = rxData; + if (rx) { + const urlsNow = new Map(); + for (const url of unique) { + const match = urls.get(url) || (url.match(rx) || [])[0]; + if (match) { + urlsNow.set(url, match); + } + } + rxData.urls = urlsNow; + } + } + const stats = { + full: {data: [], label: t('styleRegexpTestFull')}, + partial: {data: [], label: [ + t('styleRegexpTestPartial'), + template.regexpTestPartial.cloneNode(true), + ]}, + none: {data: [], label: t('styleRegexpTestNone')}, + invalid: {data: [], label: t('styleRegexpTestInvalid')}, + }; + // collect stats + for (const {text, rx, urls} of regexps) { + if (!rx) { + stats.invalid.data.push({text}); + continue; + } + if (!urls.size) { + stats.none.data.push({text}); + continue; + } + const full = []; + const partial = []; + for (const [url, match] of urls.entries()) { + const faviconUrl = url.startsWith(URLS.ownOrigin) + ? OWN_ICON + : GET_FAVICON_URL + new URL(url).hostname; + const icon = $element({tag: 'img', src: faviconUrl}); + if (match.length === url.length) { + full.push($element({appendChild: [ + icon, + url, + ]})); + } else { + partial.push($element({appendChild: [ + icon, + $element({tag: 'mark', textContent: match}), + url.substr(match.length), + ]})); + } + } + if (full.length) { + stats.full.data.push({text, urls: full}); + } + if (partial.length) { + stats.partial.data.push({text, urls: partial}); + } + } + // render stats + const report = $element({className: 'regexp-report'}); + const br = $element({tag: 'br'}); + for (const type in stats) { + // top level groups: full, partial, none, invalid + const {label, data} = stats[type]; + if (!data.length) { + continue; + } + // 2nd level: regexp text + const summary = $element({tag: 'summary', appendChild: label}); + const block = [summary]; + for (const {text, urls} of data) { + if (!urls) { + block.push(text, br.cloneNode()); + continue; + } + block.push($element({ + tag: 'details', + open: true, + appendChild: [ + $element({tag: 'summary', textContent: text}), + // 3rd level: tab urls + ...urls, + ], + })); + } + report.appendChild($element({ + tag: 'details', + open: true, + dataset: {type}, + appendChild: block, + })); + } + showHelp(t('styleRegexpTestTitle'), report); + + document.querySelector('.regexp-report').onclick = event => { + const target = event.target.closest('a, .regexp-report div'); + if (target) { + openURL({url: target.href || target.textContent}); + event.preventDefault(); + } + }; + }); +} + +function showHelp(title, text) { + const div = $('#help-popup'); + div.classList.remove('big'); + + const contents = $('.contents', div); + if (text instanceof HTMLElement) { + contents.textContent = ''; + contents.appendChild(text); + } else { + contents.innerHTML = text; + } + $('.title', div).textContent = title; + + if (getComputedStyle(div).display === 'none') { + document.addEventListener('keydown', closeHelp); + div.querySelector('.dismiss').onclick = closeHelp; // avoid chaining on multiple showHelp() calls + } + + div.style.display = 'block'; + return div; + + function closeHelp(e) { + if ( + !e || + e.type === 'click' || + ((e.keyCode || e.which) === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) + ) { + div.style.display = ''; + document.querySelector('.contents').innerHTML = ''; + document.removeEventListener('keydown', closeHelp); + } + } +} + +function showCodeMirrorPopup(title, html, options) { + const popup = showHelp(title, html); + popup.classList.add('big'); + + popup.codebox = CodeMirror(popup.querySelector('.contents'), Object.assign({ + mode: 'css', + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], + matchBrackets: true, + lint: {getAnnotations: CodeMirror.lint.css, delay: 0}, + styleActiveLine: true, + theme: prefs.get('editor.theme'), + keyMap: prefs.get('editor.keyMap') + }, options)); + popup.codebox.focus(); + popup.codebox.on('focus', () => { hotkeyRerouter.setState(false); }); + popup.codebox.on('blur', () => { hotkeyRerouter.setState(true); }); + return popup; +} + +function getParams() { + const params = {}; + const urlParts = location.href.split('?', 2); + if (urlParts.length === 1) { + return params; + } + urlParts[1].split('&').forEach(keyValue => { + const splitKeyValue = keyValue.split('=', 2); + params[decodeURIComponent(splitKeyValue[0])] = decodeURIComponent(splitKeyValue[1]); + }); + return params; +} + +chrome.runtime.onMessage.addListener(onRuntimeMessage); + +function onRuntimeMessage(request) { + switch (request.method) { + case 'styleUpdated': + if (styleId && styleId === request.style.id && request.reason !== 'editSave') { + if ((request.style.sections[0] || {}).code === null) { + // the code-less style came from notifyAllTabs + onBackgroundReady().then(() => { + request.style = BG.cachedStyles.byId.get(request.style.id); + initWithStyle(request); + }); + } else { + initWithStyle(request); + } + } + break; + case 'styleDeleted': + if (styleId && styleId === request.id) { + window.onbeforeunload = () => {}; + window.close(); + break; + } + break; + case 'prefChanged': + if ('editor.smartIndent' in request.prefs) { + CodeMirror.setOption('smartIndent', request.prefs['editor.smartIndent']); + } + break; + case 'editDeleteText': + document.execCommand('delete'); + break; + } +} + +function getComputedHeight(el) { + const compStyle = getComputedStyle(el); + return el.getBoundingClientRect().height + + parseFloat(compStyle.marginTop) + parseFloat(compStyle.marginBottom); +} + + +function getCodeMirrorThemes() { + if (!chrome.runtime.getPackageDirectoryEntry) { + const themes = [ + chrome.i18n.getMessage('defaultTheme'), + '3024-day', + '3024-night', + 'abcdef', + 'ambiance', + 'ambiance-mobile', + 'base16-dark', + 'base16-light', + 'bespin', + 'blackboard', + 'cobalt', + 'colorforth', + 'dracula', + 'duotone-dark', + 'duotone-light', + 'eclipse', + 'elegant', + 'erlang-dark', + 'hopscotch', + 'icecoder', + 'isotope', + 'lesser-dark', + 'liquibyte', + 'material', + 'mbo', + 'mdn-like', + 'midnight', + 'monokai', + 'neat', + 'neo', + 'night', + 'panda-syntax', + 'paraiso-dark', + 'paraiso-light', + 'pastel-on-dark', + 'railscasts', + 'rubyblue', + 'seti', + 'solarized', + 'the-matrix', + 'tomorrow-night-bright', + 'tomorrow-night-eighties', + 'ttcn', + 'twilight', + 'vibrant-ink', + 'xq-dark', + 'xq-light', + 'yeti', + 'zenburn', + ]; + localStorage.codeMirrorThemes = themes.join(' '); + return Promise.resolve(themes); + } + return new Promise(resolve => { + chrome.runtime.getPackageDirectoryEntry(rootDir => { + rootDir.getDirectory('vendor/codemirror/theme', {create: false}, themeDir => { + themeDir.createReader().readEntries(entries => { + const themes = [ + chrome.i18n.getMessage('defaultTheme') + ].concat( + entries.filter(entry => entry.isFile) + .sort((a, b) => (a.name < b.name ? -1 : 1)) + .map(entry => entry.name.replace(/\.css$/, '')) + ); + localStorage.codeMirrorThemes = themes.join(' '); + resolve(themes); + }); + }); + }); + }); +} diff --git a/fill_locale_placeholders.py b/fill_locale_placeholders.py deleted file mode 100644 index 37027738..00000000 --- a/fill_locale_placeholders.py +++ /dev/null @@ -1,30 +0,0 @@ -#! python2 -import io, os, json, re -from collections import OrderedDict - -with io.open('_locales/en/messages.json', 'r', encoding='utf-8') as f: - english_placeholders = [(k, v['placeholders']) for k,v in json.load(f).items() - if 'placeholders' in v] - -for locale_name in os.listdir('_locales'): - if locale_name == 'en': - continue - loc_path = '_locales/' + locale_name + '/messages.json' - with io.open(loc_path, 'r+', encoding='utf-8') as f: - loc = json.load(f, object_pairs_hook=OrderedDict) - - changed = 0 - for msgId, placeholder in english_placeholders: - if msgId in loc and cmp(placeholder, loc[msgId].get('placeholders', None))!=0: - loc[msgId]['placeholders'] = placeholder - changed += 1 - - if changed > 0: - f.seek(0) - json_str = json.dumps(loc, indent=1, ensure_ascii=False, - separators=(',', ': '), encoding='utf-8') - json_tabs = re.sub(r'^\s+', lambda s: s.group(0).replace(' ', '\t'), - json_str, flags=re.MULTILINE) - f.write(json_tabs) - f.truncate() - print 'Placeholders added to %s: %d' % (locale_name, changed) diff --git a/images/icon/16.png b/images/icon/16.png index fa6c32b3..48252ae4 100644 Binary files a/images/icon/16.png and b/images/icon/16.png differ diff --git a/images/icon/16w.png b/images/icon/16w.png index 8ea89b0a..5dd2cffb 100644 Binary files a/images/icon/16w.png and b/images/icon/16w.png differ diff --git a/images/icon/16x.png b/images/icon/16x.png index 9292d0a9..09bea46d 100644 Binary files a/images/icon/16x.png and b/images/icon/16x.png differ diff --git a/images/icon/32.png b/images/icon/32.png index 4da26c37..8fd51f80 100644 Binary files a/images/icon/32.png and b/images/icon/32.png differ diff --git a/images/icon/32w.png b/images/icon/32w.png index 42aec600..56421263 100644 Binary files a/images/icon/32w.png and b/images/icon/32w.png differ diff --git a/images/icon/32x.png b/images/icon/32x.png index 99793fc6..51116fdb 100644 Binary files a/images/icon/32x.png and b/images/icon/32x.png differ diff --git a/images/icon/38.png b/images/icon/38.png index 37362a0f..ffa99c2b 100644 Binary files a/images/icon/38.png and b/images/icon/38.png differ diff --git a/images/icon/38w.png b/images/icon/38w.png index 555eef9a..73342164 100644 Binary files a/images/icon/38w.png and b/images/icon/38w.png differ diff --git a/images/icon/38x.png b/images/icon/38x.png index 265b7c03..a0cbf099 100644 Binary files a/images/icon/38x.png and b/images/icon/38x.png differ diff --git a/images/icon/light/16.png b/images/icon/light/16.png new file mode 100644 index 00000000..3ec57b4e Binary files /dev/null and b/images/icon/light/16.png differ diff --git a/images/icon/light/16w.png b/images/icon/light/16w.png new file mode 100644 index 00000000..6287ce91 Binary files /dev/null and b/images/icon/light/16w.png differ diff --git a/images/icon/light/16x.png b/images/icon/light/16x.png new file mode 100644 index 00000000..4ccc3e50 Binary files /dev/null and b/images/icon/light/16x.png differ diff --git a/images/icon/light/19.png b/images/icon/light/19.png new file mode 100644 index 00000000..3ec57b4e Binary files /dev/null and b/images/icon/light/19.png differ diff --git a/images/icon/light/19w.png b/images/icon/light/19w.png new file mode 100644 index 00000000..6287ce91 Binary files /dev/null and b/images/icon/light/19w.png differ diff --git a/images/icon/light/19x.png b/images/icon/light/19x.png new file mode 100644 index 00000000..4ccc3e50 Binary files /dev/null and b/images/icon/light/19x.png differ diff --git a/images/icon/light/32.png b/images/icon/light/32.png new file mode 100644 index 00000000..82519c1f Binary files /dev/null and b/images/icon/light/32.png differ diff --git a/images/icon/light/32w.png b/images/icon/light/32w.png new file mode 100644 index 00000000..28104ad1 Binary files /dev/null and b/images/icon/light/32w.png differ diff --git a/images/icon/light/32x.png b/images/icon/light/32x.png new file mode 100644 index 00000000..0cc96ca0 Binary files /dev/null and b/images/icon/light/32x.png differ diff --git a/images/icon/light/38.png b/images/icon/light/38.png new file mode 100644 index 00000000..9d06f23e Binary files /dev/null and b/images/icon/light/38.png differ diff --git a/images/icon/light/38w.png b/images/icon/light/38w.png new file mode 100644 index 00000000..fcc351fa Binary files /dev/null and b/images/icon/light/38w.png differ diff --git a/images/icon/light/38x.png b/images/icon/light/38x.png new file mode 100644 index 00000000..ac3d7b3e Binary files /dev/null and b/images/icon/light/38x.png differ diff --git a/install.js b/install.js deleted file mode 100644 index 560546e7..00000000 --- a/install.js +++ /dev/null @@ -1,249 +0,0 @@ -'use strict'; - -const FIREFOX = /Firefox/.test(navigator.userAgent); -const VIVALDI = /Vivaldi/.test(navigator.userAgent); -const OPERA = /OPR/.test(navigator.userAgent); - -document.addEventListener("stylishUpdate", onUpdateClicked); -document.addEventListener("stylishUpdateChrome", onUpdateClicked); -document.addEventListener("stylishUpdateOpera", onUpdateClicked); - -document.addEventListener("stylishInstall", onInstallClicked); -document.addEventListener("stylishInstallChrome", onInstallClicked); -document.addEventListener("stylishInstallOpera", onInstallClicked); - -chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { - // orphaned content script check - if (msg.method == 'ping') { - sendResponse(true); - } -}); - -new MutationObserver((mutations, observer) => { - if (document.body) { - observer.disconnect(); - chrome.runtime.sendMessage({ - method: 'getStyles', - url: getMeta('stylish-id-url') || location.href - }, checkUpdatability); - } -}).observe(document.documentElement, {childList: true}); - -/* since we are using "stylish-code-chrome" meta key on all browsers and - US.o does not provide "advanced settings" on this url if browser is not Chrome, - we need to fix this URL using "stylish-update-url" meta key -*/ -function getStyleURL () { - const url = getMeta('stylish-code-chrome'); - - if (FIREFOX || OPERA || VIVALDI) { - /* get custom settings from the update url */ - return Object.assign(new URL(url), { - search: (new URL(getMeta('stylish-update-url'))).search - }).href; - } - return url; -} - -function checkUpdatability([installedStyle]) { - if (!installedStyle) { - sendEvent('styleCanBeInstalledChrome'); - return; - } - const md5Url = getMeta('stylish-md5-url'); - if (md5Url && installedStyle.md5Url && installedStyle.originalMd5) { - getResource(md5Url).then(md5 => { - reportUpdatable(md5 != installedStyle.originalMd5); - }); - } else { - getResource(getStyleURL()).then(code => { - reportUpdatable(code === null || - !styleSectionsEqual(JSON.parse(code), installedStyle)); - }); - } - - function reportUpdatable(isUpdatable) { - sendEvent( - isUpdatable - ? 'styleCanBeUpdatedChrome' - : 'styleAlreadyInstalledChrome', - { - updateUrl: installedStyle.updateUrl - } - ); - } -} - - -function sendEvent(type, detail = null) { - if (FIREFOX) { - type = type.replace('Chrome', ''); - } - else if (OPERA || VIVALDI) { - type = type.replace('Chrome', 'Opera'); - } - detail = {detail}; - if (typeof cloneInto != 'undefined') { - // Firefox requires explicit cloning, however USO can't process our messages anyway - // because USO tries to use a global "event" variable deprecated in Firefox - detail = cloneInto(detail, document); // eslint-disable-line no-undef - } - onDOMready().then(() => { - document.dispatchEvent(new CustomEvent(type, detail)); - }); -} - - -function onInstallClicked() { - if (!orphanCheck || !orphanCheck()) { - return; - } - getResource(getMeta('stylish-description')) - .then(name => saveStyleCode('styleInstall', name)) - .then(() => getResource(getMeta('stylish-install-ping-url-chrome'))); -} - - -function onUpdateClicked() { - if (!orphanCheck || !orphanCheck()) { - return; - } - chrome.runtime.sendMessage({ - method: 'getStyles', - url: getMeta('stylish-id-url') || location.href, - }, ([style]) => { - saveStyleCode('styleUpdate', style.name, {id: style.id}); - }); -} - - -function saveStyleCode(message, name, addProps) { - return new Promise(resolve => { - if (!confirm(chrome.i18n.getMessage(message, [name]))) { - return; - } - - getResource(getStyleURL()).then(code => { - chrome.runtime.sendMessage( - Object.assign(JSON.parse(code), addProps, { - method: 'saveStyle', - reason: 'update', - }), - () => sendEvent('styleInstalledChrome') - ); - resolve(); - }); - }); -} - - -function getMeta(name) { - const e = document.querySelector(`link[rel="${name}"]`); - return e ? e.getAttribute('href') : null; -} - - -function getResource(url) { - return new Promise(resolve => { - if (url.startsWith('#')) { - resolve(document.getElementById(url.slice(1)).textContent); - } else { - chrome.runtime.sendMessage({method: 'download', url}, resolve); - } - }); -} - - -function styleSectionsEqual({sections: a}, {sections: b}) { - if (!a || !b) { - return undefined; - } - if (a.length != b.length) { - return false; - } - const checkedInB = []; - return a.every(sectionA => b.some(sectionB => { - if (!checkedInB.includes(sectionB) && propertiesEqual(sectionA, sectionB)) { - checkedInB.push(sectionB); - return true; - } - })); - - function propertiesEqual(secA, secB) { - for (const name of ['urlPrefixes', 'urls', 'domains', 'regexps']) { - if (!equalOrEmpty(secA[name], secB[name], 'every', arrayMirrors)) { - return false; - } - } - return equalOrEmpty(secA.code, secB.code, 'substr', (a, b) => a == b); - } - - function equalOrEmpty(a, b, telltale, comparator) { - const typeA = a && typeof a[telltale] == 'function'; - const typeB = b && typeof b[telltale] == 'function'; - return ( - (a === null || a === undefined || (typeA && !a.length)) && - (b === null || b === undefined || (typeB && !b.length)) - ) || typeA && typeB && a.length == b.length && comparator(a, b); - } - - function arrayMirrors(array1, array2) { - for (const el of array1) { - if (array2.indexOf(el) < 0) { - return false; - } - } - for (const el of array2) { - if (array1.indexOf(el) < 0) { - return false; - } - } - return true; - } -} - - -function onDOMready() { - if (document.readyState != 'loading') { - return Promise.resolve(); - } - return new Promise(resolve => { - document.addEventListener('DOMContentLoaded', function _() { - document.removeEventListener('DOMContentLoaded', _); - resolve(); - }); - }); -} - - -function orphanCheck() { - const port = chrome.runtime.connect(); - if (port) { - port.disconnect(); - return true; - } - // we're orphaned due to an extension update - // we can detach event listeners - document.removeEventListener("stylishUpdate", onUpdateClicked); - document.removeEventListener("stylishUpdateChrome", onUpdateClicked); - document.removeEventListener("stylishUpdateOpera", onUpdateClicked); - - document.removeEventListener("stylishInstall", onInstallClicked); - document.removeEventListener("stylishInstallChrome", onInstallClicked); - document.removeEventListener("stylishInstallOpera", onInstallClicked); - - // we can't detach chrome.runtime.onMessage because it's no longer connected internally - // we can destroy global functions in this context to free up memory - [ - 'checkUpdatability', - 'getMeta', - 'getResource', - 'onDOMready', - 'onInstallClicked', - 'onUpdateClicked', - 'orphanCheck', - 'saveStyleCode', - 'sendEvent', - 'styleSectionsEqual', - ].forEach(fn => (window[fn] = null)); -} diff --git a/dom.js b/js/dom.js similarity index 65% rename from dom.js rename to js/dom.js index 30d21779..ced73414 100644 --- a/dom.js +++ b/js/dom.js @@ -12,20 +12,22 @@ for (const type of [NodeList, NamedNodeMap, HTMLCollection, HTMLAllCollection]) } // add favicon in Firefox -if (/Firefox/.test(navigator.userAgent)) { - for (const size of [128, 38, 32, 19, 16]) { +// eslint-disable-next-line no-unused-expressions +navigator.userAgent.includes('Firefox') && setTimeout(() => { + const iconset = ['', 'light/'][prefs.get('iconset')] || ''; + for (const size of [38, 32, 19, 16]) { document.head.appendChild($element({ tag: 'link', rel: 'icon', - href: `/images/icon/${size}.png`, + href: `/images/icon/${iconset}${size}.png`, sizes: size + 'x' + size, })); } -} +}); function onDOMready() { - if (document.readyState != 'loading') { + if (document.readyState !== 'loading') { return Promise.resolve(); } return new Promise(resolve => { @@ -46,13 +48,20 @@ function scrollElementIntoView(element) { } -function animateElement(element, {className, removeExtraClasses = [], remove = false}) { - return new Promise(resolve => { +function animateElement( + element, { + className = 'highlight', + removeExtraClasses = [], + remove = false, + } = {}) { + return element && new Promise(resolve => { element.addEventListener('animationend', function _() { element.removeEventListener('animationend', _); element.classList.remove( className, - ...removeExtraClasses // In Firefox, `resolve()` might be called one frame later. This is helpful to clean-up on the same frame + // In Firefox, `resolve()` might be called one frame later. + // This is helpful to clean-up on the same frame + ...removeExtraClasses ); // TODO: investigate why animation restarts if the elements is removed in .then() if (remove) { @@ -70,9 +79,9 @@ function enforceInputRange(element) { const max = Number(element.max); const doNotify = () => element.dispatchEvent(new Event('change', {bubbles: true})); const onChange = ({type}) => { - if (type == 'input' && element.checkValidity()) { + if (type === 'input' && element.checkValidity()) { doNotify(); - } else if (type == 'change' && !element.checkValidity()) { + } else if (type === 'change' && !element.checkValidity()) { element.value = Math.max(min, Math.min(max, Number(element.value))); doNotify(); } @@ -97,17 +106,21 @@ function $$(selector, base = document) { function $element(opt) { // tag: string, default 'div', may include namespace like 'ns#tag' - // appendChild: element or an array of elements + // appendChild: element/string or an array of elements/strings // dataset: object // any DOM property: assigned as is const [ns, tag] = opt.tag && opt.tag.includes('#') ? opt.tag.split('#') : [null, opt.tag]; const element = ns - ? document.createElementNS(ns == 'SVG' || ns == 'svg' ? 'http://www.w3.org/2000/svg' : ns, tag) + ? document.createElementNS(ns === 'SVG' || ns === 'svg' ? 'http://www.w3.org/2000/svg' : ns, tag) : document.createElement(tag || 'div'); - (opt.appendChild instanceof Array ? opt.appendChild : [opt.appendChild]) - .forEach(child => child && element.appendChild(child)); + const children = opt.appendChild instanceof Array ? opt.appendChild : [opt.appendChild]; + for (const child of children) { + if (child) { + element.appendChild(child instanceof Node ? child : document.createTextNode(child)); + } + } delete opt.appendChild; delete opt.tag; if (opt.dataset) { @@ -123,3 +136,15 @@ function $element(opt) { } return element; } + + +function retranslateCSS(selectorToMessageMap) { + // TODO: remove when this bug is fixed in FF + // Note: selectors must be spec-normalized e.g. ::before, not :before + for (const rule of document.styleSheets[0].cssRules) { + const msg = selectorToMessageMap[rule.selectorText]; + if (msg) { + rule.style.content = '"' + msg.replace(/__MSG_(\w+)__/g, (_, id) => t(id)) + '"'; + } + } +} diff --git a/localization.js b/js/localization.js similarity index 87% rename from localization.js rename to js/localization.js index 2a6ac4c4..1db7577b 100644 --- a/localization.js +++ b/js/localization.js @@ -7,7 +7,7 @@ tDocLoader(); function t(key, params) { const cache = !params && t.cache[key]; const s = cache || chrome.i18n.getMessage(key, params); - if (s == '') { + if (s === '') { throw `Missing string "${key}"`; } if (!params && !cache) { @@ -20,7 +20,7 @@ function t(key, params) { function tE(id, key, attr, esc) { if (attr) { document.getElementById(id).setAttribute(attr, t(key)); - } else if (typeof esc == 'undefined' || esc) { + } else if (typeof esc === 'undefined' || esc) { document.getElementById(id).appendChild(document.createTextNode(t(key))); } else { document.getElementById(id).innerHTML = t(key); @@ -43,10 +43,10 @@ function tNodeList(nodes) { for (let n = nodes.length; --n >= 0;) { const node = nodes[n]; // skip non-ELEMENT_NODE - if (node.nodeType != 1) { + if (node.nodeType !== 1) { continue; } - if (node.localName == 'template') { + if (node.localName === 'template') { const elements = node.content.querySelectorAll('*'); tNodeList(elements); template[node.dataset.id] = elements[0]; @@ -91,7 +91,16 @@ function tNodeList(nodes) { function tDocLoader() { t.cache = tryJSONparse(localStorage.L10N) || {}; + + // reset L10N cache on UI language change + const UIlang = chrome.i18n.getUILanguage(); + if (t.cache.browserUIlanguage !== UIlang) { + t.cache = {browserUIlanguage: UIlang}; + localStorage.L10N = JSON.stringify(t.cache); + } + const cacheLength = Object.keys(t.cache).length; + // localize HEAD tNodeList(document.getElementsByTagName('*')); @@ -105,7 +114,7 @@ function tDocLoader() { const onLoad = () => { tDocLoader.stop(); process(observer.takeRecords()); - if (cacheLength != Object.keys(t.cache).length) { + if (cacheLength !== Object.keys(t.cache).length) { localStorage.L10N = JSON.stringify(t.cache); } }; diff --git a/messaging.js b/js/messaging.js similarity index 72% rename from messaging.js rename to js/messaging.js index 063f4a2a..386f7409 100644 --- a/messaging.js +++ b/js/messaging.js @@ -1,332 +1,371 @@ -/* global BG: true, onRuntimeMessage, applyOnMessage, handleUpdate, handleDelete */ -'use strict'; - -// keep message channel open for sendResponse in chrome.runtime.onMessage listener -const KEEP_CHANNEL_OPEN = true; - -const FIREFOX = /Firefox/.test(navigator.userAgent); -const OPERA = /OPR/.test(navigator.userAgent); - -const URLS = { - ownOrigin: chrome.runtime.getURL(''), - - optionsUI: [ - chrome.runtime.getURL('options/index.html'), - 'chrome://extensions/?options=' + chrome.runtime.id, - ], - - configureCommands: - OPERA ? 'opera://settings/configureCommands' - : 'chrome://extensions/configureCommands', - - // CWS cannot be scripted in chromium, see ChromeExtensionsClient::IsScriptableURL - // https://cs.chromium.org/chromium/src/chrome/common/extensions/chrome_extensions_client.cc - chromeWebStore: FIREFOX ? 'https://addons.mozilla.org/' : ( - OPERA ? 'https://addons.opera.com/' : 'https://chrome.google.com/webstore/' - ), - - supported: new RegExp( - '^(file|ftps?|http)://|' + - `^https://${FIREFOX ? '(?!addons\\.mozilla\\.org)' : ( - OPERA ? '(?!addons\\.opera\\.com)' : '(?!chrome\\.google\\.com/webstore)' - )}|` + - '^' + chrome.runtime.getURL('')), -}; - -let BG = chrome.extension.getBackgroundPage(); - -if (!BG || BG != window) { - document.documentElement.classList.toggle('firefox', FIREFOX); - document.documentElement.classList.toggle('opera', OPERA); - // TODO: remove once our manifest's minimum_chrome_version is 50+ - // Chrome 49 doesn't report own extension pages in webNavigation apparently - if (navigator.userAgent.includes('Chrome/49.')) { - getActiveTab().then(BG.updateIcon); - } -} - -function notifyAllTabs(msg) { - const originalMessage = msg; - if (msg.method == 'styleUpdated' || msg.method == 'styleAdded') { - // apply/popup/manage use only meta for these two methods, - // editor may need the full code but can fetch it directly, - // so we send just the meta to avoid spamming lots of tabs with huge styles - msg = Object.assign({}, msg, { - style: getStyleWithNoCode(msg.style) - }); - } - const affectsAll = !msg.affects || msg.affects.all; - const affectsOwnOriginOnly = !affectsAll && (msg.affects.editor || msg.affects.manager); - const affectsTabs = affectsAll || affectsOwnOriginOnly; - const affectsIcon = affectsAll || msg.affects.icon; - const affectsPopup = affectsAll || msg.affects.popup; - const affectsSelf = affectsPopup || msg.prefs; - if (affectsTabs || affectsIcon) { - // list all tabs including chrome-extension:// which can be ours - chrome.tabs.query(affectsOwnOriginOnly ? {url: URLS.ownOrigin + '*'} : {}, tabs => { - for (const tab of tabs) { - // own pages will be notified via runtime.sendMessage later - if ((affectsTabs || URLS.optionsUI.includes(tab.url)) - && !(affectsSelf && tab.url.startsWith(URLS.ownOrigin))) { - chrome.tabs.sendMessage(tab.id, msg); - } - if (affectsIcon && BG) { - BG.updateIcon(tab); - } - } - }); - } - // notify self: the message no longer is sent to the origin in new Chrome - if (typeof onRuntimeMessage != 'undefined') { - onRuntimeMessage(originalMessage); - } - // notify apply.js on own pages - if (typeof applyOnMessage != 'undefined') { - applyOnMessage(originalMessage); - } - // notify background page and all open popups - if (affectsSelf) { - chrome.runtime.sendMessage(msg); - } -} - - -function getTab(id) { - return new Promise(resolve => - chrome.tabs.get(id, tab => - !chrome.runtime.lastError && resolve(tab))); -} - - -function getActiveTab() { - return new Promise(resolve => - chrome.tabs.query({currentWindow: true, active: true}, tabs => - resolve(tabs[0]))); -} - - -function getActiveTabRealURL() { - return getActiveTab() - .then(getTabRealURL); -} - - -function getTabRealURL(tab) { - return new Promise(resolve => { - if (tab.url != 'chrome://newtab/') { - resolve(tab.url); - } else { - chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, processId: -1}, frame => { - resolve(frame && frame.url || ''); - }); - } - }); -} - - -// opens a tab or activates the already opened one, -// reuses the New Tab page if it's focused now -function openURL({url, currentWindow = true}) { - if (!url.includes('://')) { - url = chrome.runtime.getURL(url); - } - return new Promise(resolve => { - // [some] chromium forks don't handle their fake branded protocols - url = url.replace(/^(opera|vivaldi)/, 'chrome'); - // API doesn't handle the hash-fragment part - chrome.tabs.query({url: url.replace(/#.*/, ''), currentWindow}, tabs => { - for (const tab of tabs) { - if (tab.url == url) { - activateTab(tab).then(resolve); - return; - } - } - getActiveTab().then(tab => { - if (tab && tab.url == 'chrome://newtab/') { - chrome.tabs.update({url}, resolve); - } else { - chrome.tabs.create(tab && !FIREFOX ? {url, openerTabId: tab.id} : {url}, resolve); - } - }); - }); - }); -} - - -function activateTab(tab) { - return Promise.all([ - new Promise(resolve => { - chrome.tabs.update(tab.id, {active: true}, resolve); - }), - new Promise(resolve => { - chrome.windows.update(tab.windowId, {focused: true}, resolve); - }), - ]); -} - - -function stringAsRegExp(s, flags) { - return new RegExp(s.replace(/[{}()[\]/\\.+?^$:=*!|]/g, '\\$&'), flags); -} - - -function ignoreChromeError() { - chrome.runtime.lastError; // eslint-disable-line no-unused-expressions -} - - -function getStyleWithNoCode(style) { - const stripped = Object.assign({}, style, {sections: []}); - for (const section of style.sections) { - stripped.sections.push(Object.assign({}, section, {code: null})); - } - return stripped; -} - - -// js engine can't optimize the entire function if it contains try-catch -// so we should keep it isolated from normal code in a minimal wrapper -// Update: might get fixed in V8 TurboFan in the future -function tryCatch(func, ...args) { - try { - return func(...args); - } catch (e) {} -} - - -function tryRegExp(regexp) { - try { - return new RegExp(regexp); - } catch (e) {} -} - - -function tryJSONparse(jsonString) { - try { - return JSON.parse(jsonString); - } catch (e) {} -} - - -const debounce = Object.assign((fn, delay, ...args) => { - clearTimeout(debounce.timers.get(fn)); - debounce.timers.set(fn, setTimeout(debounce.run, delay, fn, ...args)); -}, { - timers: new Map(), - run(fn, ...args) { - debounce.timers.delete(fn); - fn(...args); - }, - unregister(fn) { - clearTimeout(debounce.timers.get(fn)); - debounce.timers.delete(fn); - }, -}); - - -function deepCopy(obj) { - return obj !== null && obj !== undefined && typeof obj == 'object' - ? deepMerge(typeof obj.slice == 'function' ? [] : {}, obj) - : obj; -} - - -function deepMerge(target, ...args) { - const isArray = typeof target.slice == 'function'; - for (const obj of args) { - if (isArray && obj !== null && obj !== undefined) { - for (const element of obj) { - target.push(deepCopy(element)); - } - continue; - } - for (const k in obj) { - const value = obj[k]; - if (k in target && typeof value == 'object' && value !== null) { - deepMerge(target[k], value); - } else { - target[k] = deepCopy(value); - } - } - } - return target; -} - - -function sessionStorageHash(name) { - return { - name, - value: tryCatch(JSON.parse, sessionStorage[name]) || {}, - set(k, v) { - this.value[k] = v; - this.updateStorage(); - }, - unset(k) { - delete this.value[k]; - this.updateStorage(); - }, - updateStorage() { - sessionStorage[this.name] = JSON.stringify(this.value); - } - }; -} - - -function onBackgroundReady(...dataPassthru) { - return BG ? Promise.resolve(...dataPassthru) : new Promise(ping); - function ping(resolve) { - chrome.runtime.sendMessage({method: 'healthCheck'}, health => { - if (health !== undefined) { - BG = chrome.extension.getBackgroundPage(); - resolve(...dataPassthru); - } else { - ping(resolve); - } - }); - } -} - - -// in case Chrome haven't yet loaded the bg page and displays our page like edit/manage -function getStylesSafe(options) { - return onBackgroundReady(options).then(BG.getStyles); -} - - -function saveStyleSafe(style) { - return onBackgroundReady(BG.deepCopy(style)) - .then(BG.saveStyle) - .then(savedStyle => { - if (style.notify === false) { - handleUpdate(savedStyle, style); - } - return savedStyle; - }); -} - - -function deleteStyleSafe({id, notify = true} = {}) { - return onBackgroundReady({id, notify}) - .then(BG.deleteStyle) - .then(() => { - if (!notify) { - handleDelete(id); - } - return id; - }); -} - - -function download(url) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.timeout = 10e3; - xhr.onloadend = () => (xhr.status == 200 - ? resolve(xhr.responseText) - : reject(xhr.status)); - const [mainUrl, query] = url.split('?'); - xhr.open(query ? 'POST' : 'GET', mainUrl, true); - xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhr.send(query); - }); -} +/* global BG: true, onRuntimeMessage, applyOnMessage, handleUpdate, handleDelete */ +'use strict'; + +// keep message channel open for sendResponse in chrome.runtime.onMessage listener +const KEEP_CHANNEL_OPEN = true; + +const FIREFOX = /Firefox/.test(navigator.userAgent); +const OPERA = /OPR/.test(navigator.userAgent); + +const URLS = { + ownOrigin: chrome.runtime.getURL(''), + + optionsUI: [ + chrome.runtime.getURL('options.html'), + 'chrome://extensions/?options=' + chrome.runtime.id, + ], + + configureCommands: + OPERA ? 'opera://settings/configureCommands' + : 'chrome://extensions/configureCommands', + + // CWS cannot be scripted in chromium, see ChromeExtensionsClient::IsScriptableURL + // https://cs.chromium.org/chromium/src/chrome/common/extensions/chrome_extensions_client.cc + chromeWebStore: FIREFOX ? 'https://addons.mozilla.org/' : ( + OPERA ? 'https://addons.opera.com/' : 'https://chrome.google.com/webstore/' + ), + + supported: new RegExp( + '^(file|ftps?|http)://|' + + `^https://${FIREFOX ? '(?!addons\\.mozilla\\.org)' : ( + OPERA ? '(?!addons\\.opera\\.com)' : '(?!chrome\\.google\\.com/webstore)' + )}|` + + '^' + chrome.runtime.getURL('')), +}; + +let BG = chrome.extension.getBackgroundPage(); + +if (!BG || BG !== window) { + document.documentElement.classList.toggle('firefox', FIREFOX); + document.documentElement.classList.toggle('opera', OPERA); + // TODO: remove once our manifest's minimum_chrome_version is 50+ + // Chrome 49 doesn't report own extension pages in webNavigation apparently + if (navigator.userAgent.includes('Chrome/49.')) { + getActiveTab().then(BG.updateIcon); + } +} + +function notifyAllTabs(msg) { + const originalMessage = msg; + if (msg.method === 'styleUpdated' || msg.method === 'styleAdded') { + // apply/popup/manage use only meta for these two methods, + // editor may need the full code but can fetch it directly, + // so we send just the meta to avoid spamming lots of tabs with huge styles + msg = Object.assign({}, msg, { + style: getStyleWithNoCode(msg.style) + }); + } + const affectsAll = !msg.affects || msg.affects.all; + const affectsOwnOriginOnly = !affectsAll && (msg.affects.editor || msg.affects.manager); + const affectsTabs = affectsAll || affectsOwnOriginOnly; + const affectsIcon = affectsAll || msg.affects.icon; + const affectsPopup = affectsAll || msg.affects.popup; + const affectsSelf = affectsPopup || msg.prefs; + if (affectsTabs || affectsIcon) { + const notifyTab = tab => { + // own pages will be notified via runtime.sendMessage later + if ((affectsTabs || URLS.optionsUI.includes(tab.url)) + && !(affectsSelf && tab.url.startsWith(URLS.ownOrigin)) + // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF + && (!FIREFOX || tab.width)) { + chrome.tabs.sendMessage(tab.id, msg); + } + if (affectsIcon && BG) { + BG.updateIcon(tab); + } + }; + // list all tabs including chrome-extension:// which can be ours + Promise.all([ + queryTabs(affectsOwnOriginOnly ? {url: URLS.ownOrigin + '*'} : {}), + getActiveTab(), + ]).then(([tabs, activeTab]) => { + const activeTabId = activeTab && activeTab.id; + for (const tab of tabs) { + invokeOrPostpone(tab.id === activeTabId, notifyTab, tab); + } + }); + } + // notify self: the message no longer is sent to the origin in new Chrome + if (typeof onRuntimeMessage !== 'undefined') { + onRuntimeMessage(originalMessage); + } + // notify apply.js on own pages + if (typeof applyOnMessage !== 'undefined') { + applyOnMessage(originalMessage); + } + // notify background page and all open popups + if (affectsSelf) { + chrome.runtime.sendMessage(msg); + } +} + + +function queryTabs(options = {}) { + return new Promise(resolve => + chrome.tabs.query(options, tabs => + resolve(tabs))); +} + + +function getTab(id) { + return new Promise(resolve => + chrome.tabs.get(id, tab => + !chrome.runtime.lastError && resolve(tab))); +} + + +function getOwnTab() { + return new Promise(resolve => + chrome.tabs.getCurrent(tab => resolve(tab))); +} + + +function getActiveTab() { + return queryTabs({currentWindow: true, active: true}) + .then(tabs => tabs[0]); +} + + +function getActiveTabRealURL() { + return getActiveTab() + .then(getTabRealURL); +} + + +function getTabRealURL(tab) { + return new Promise(resolve => { + if (tab.url !== 'chrome://newtab/') { + resolve(tab.url); + } else { + chrome.webNavigation.getFrame({tabId: tab.id, frameId: 0, processId: -1}, frame => { + resolve(frame && frame.url || ''); + }); + } + }); +} + + +// opens a tab or activates the already opened one, +// reuses the New Tab page if it's focused now +function openURL({url, currentWindow = true}) { + if (!url.includes('://')) { + url = chrome.runtime.getURL(url); + } + return new Promise(resolve => { + // [some] chromium forks don't handle their fake branded protocols + url = url.replace(/^(opera|vivaldi)/, 'chrome'); + // FF doesn't handle moz-extension:// URLs (bug) + // API doesn't handle the hash-fragment part + const urlQuery = url.startsWith('moz-extension') ? undefined : url.replace(/#.*/, ''); + queryTabs({url: urlQuery, currentWindow}).then(tabs => { + for (const tab of tabs) { + if (tab.url === url) { + activateTab(tab).then(resolve); + return; + } + } + getActiveTab().then(tab => { + if (tab && tab.url === 'chrome://newtab/' + // prevent redirecting incognito NTP to a chrome URL as it crashes Chrome + && (!url.startsWith('chrome') || !tab.incognito)) { + chrome.tabs.update({url}, resolve); + } else { + chrome.tabs.create(tab && !FIREFOX ? {url, openerTabId: tab.id} : {url}, resolve); + } + }); + }); + }); +} + + +function activateTab(tab) { + return Promise.all([ + new Promise(resolve => { + chrome.tabs.update(tab.id, {active: true}, resolve); + }), + new Promise(resolve => { + chrome.windows.update(tab.windowId, {focused: true}, resolve); + }), + ]); +} + + +function stringAsRegExp(s, flags) { + return new RegExp(s.replace(/[{}()[\]/\\.+?^$:=*!|]/g, '\\$&'), flags); +} + + +function ignoreChromeError() { + chrome.runtime.lastError; // eslint-disable-line no-unused-expressions +} + + +function getStyleWithNoCode(style) { + const stripped = Object.assign({}, style, {sections: []}); + for (const section of style.sections) { + stripped.sections.push(Object.assign({}, section, {code: null})); + } + return stripped; +} + + +// js engine can't optimize the entire function if it contains try-catch +// so we should keep it isolated from normal code in a minimal wrapper +// Update: might get fixed in V8 TurboFan in the future +function tryCatch(func, ...args) { + try { + return func(...args); + } catch (e) {} +} + + +function tryRegExp(regexp) { + try { + return new RegExp(regexp); + } catch (e) {} +} + + +function tryJSONparse(jsonString) { + try { + return JSON.parse(jsonString); + } catch (e) {} +} + + +const debounce = Object.assign((fn, delay, ...args) => { + clearTimeout(debounce.timers.get(fn)); + debounce.timers.set(fn, setTimeout(debounce.run, delay, fn, ...args)); +}, { + timers: new Map(), + run(fn, ...args) { + debounce.timers.delete(fn); + fn(...args); + }, + unregister(fn) { + clearTimeout(debounce.timers.get(fn)); + debounce.timers.delete(fn); + }, +}); + + +function deepCopy(obj) { + return obj !== null && obj !== undefined && typeof obj === 'object' + ? deepMerge(typeof obj.slice === 'function' ? [] : {}, obj) + : obj; +} + + +function deepMerge(target, ...args) { + const isArray = typeof target.slice === 'function'; + for (const obj of args) { + if (isArray && obj !== null && obj !== undefined) { + for (const element of obj) { + target.push(deepCopy(element)); + } + continue; + } + for (const k in obj) { + const value = obj[k]; + if (k in target && typeof value === 'object' && value !== null) { + deepMerge(target[k], value); + } else { + target[k] = deepCopy(value); + } + } + } + return target; +} + + +function sessionStorageHash(name) { + return { + name, + value: tryCatch(JSON.parse, sessionStorage[name]) || {}, + set(k, v) { + this.value[k] = v; + this.updateStorage(); + }, + unset(k) { + delete this.value[k]; + this.updateStorage(); + }, + updateStorage() { + sessionStorage[this.name] = JSON.stringify(this.value); + } + }; +} + + +function onBackgroundReady() { + return BG && BG.getStyles ? Promise.resolve() : new Promise(function ping(resolve) { + chrome.runtime.sendMessage({method: 'healthCheck'}, health => { + if (health !== undefined) { + BG = chrome.extension.getBackgroundPage(); + resolve(); + } else { + setTimeout(ping, 0, resolve); + } + }); + }); +} + + +// in case Chrome haven't yet loaded the bg page and displays our page like edit/manage +function getStylesSafe(options) { + return onBackgroundReady() + .then(() => BG.getStyles(options)); +} + + +function saveStyleSafe(style) { + return onBackgroundReady() + .then(() => BG.saveStyle(BG.deepCopy(style))) + .then(savedStyle => { + if (style.notify === false) { + handleUpdate(savedStyle, style); + } + return savedStyle; + }); +} + + +function deleteStyleSafe({id, notify = true} = {}) { + return onBackgroundReady() + .then(() => BG.deleteStyle({id, notify})) + .then(() => { + if (!notify) { + handleDelete(id); + } + return id; + }); +} + + +function download(url) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.timeout = 10e3; + xhr.onloadend = () => (xhr.status === 200 + ? resolve(xhr.responseText) + : reject(xhr.status)); + const [mainUrl, query] = url.split('?'); + xhr.open(query ? 'POST' : 'GET', mainUrl, true); + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhr.send(query); + }); +} + + +function doTimeout(ms = 0, ...args) { + return ms > 0 + ? () => new Promise(resolve => setTimeout(resolve, ms, ...args)) + : new Promise(resolve => setTimeout(resolve, 0, ...args)); +} + + +function invokeOrPostpone(isInvoke, fn, ...args) { + return isInvoke + ? fn(...args) + : setTimeout(invokeOrPostpone, 0, true, fn, ...args); +} diff --git a/prefs.js b/js/prefs.js similarity index 91% rename from prefs.js rename to js/prefs.js index c632d1e9..2326036b 100644 --- a/prefs.js +++ b/js/prefs.js @@ -37,15 +37,19 @@ var prefs = new function Prefs() { newline_before_close_brace: true, newline_between_rules: false, end_with_newline: false, - space_around_selector_separator: true, + indent_conditional: true, }, 'editor.lintDelay': 500, // lint gutter marker update delay, ms '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 // '' (empty string) = disabled + 'editor.autocompleteOnTyping': false, // show autocomplete dropdown on typing a word token 'editor.contextDelete': contextDeleteMissing(), // "Delete" item in context menu + 'iconset': 0, // 0 = dark-themed icon + // 1 = light-themed icon + 'badgeDisabled': '#8B0000', // badge background color when disabled 'badgeNormal': '#006666', // badge background color @@ -60,6 +64,7 @@ var prefs = new function Prefs() { 'disableAll', 'badgeDisabled', 'badgeNormal', + 'iconset', ]; const onChange = { @@ -110,10 +115,10 @@ var prefs = new function Prefs() { defineReadonlyProperty(this.readOnlyValues, key, value); const hasChanged = !equal(value, oldValue); if (!fromBroadcast) { - if (BG && BG != window) { + if (BG && BG !== window) { BG.prefs.set(key, BG.deepCopy(value), {broadcast, sync}); } else { - localStorage[key] = typeof defaults[key] == 'object' + localStorage[key] = typeof defaults[key] === 'object' ? JSON.stringify(value) : value; if (broadcast && hasChanged) { @@ -161,7 +166,7 @@ var prefs = new function Prefs() { for (const key in defaults) { const defaultValue = defaults[key]; let value = localStorage[key]; - if (typeof value == 'string') { + if (typeof value === 'string') { switch (typeof defaultValue) { case 'boolean': value = value.toLowerCase() === 'true'; @@ -176,7 +181,7 @@ var prefs = new function Prefs() { } else { value = defaultValue; } - if (BG == window) { + if (BG === window) { // when in bg page, .set() will write to localStorage this.set(key, value, {broadcast: false, sync: false}); } else { @@ -185,13 +190,13 @@ var prefs = new function Prefs() { } } - if (!BG || BG == window) { + if (!BG || BG === window) { affectsIcon.forEach(key => this.broadcast(key, values[key], {sync: false})); getSync().get('settings', ({settings: synced} = {}) => { if (synced) { for (const key in defaults) { - if (key == 'popupWidth' && synced[key] != values.popupWidth) { + if (key === 'popupWidth' && synced[key] !== values.popupWidth) { // this is a fix for the period when popupWidth wasn't synced // TODO: remove it in a couple of months continue; @@ -204,7 +209,7 @@ var prefs = new function Prefs() { }); chrome.storage.onChanged.addListener((changes, area) => { - if (area == 'sync' && 'settings' in changes) { + if (area === 'sync' && 'settings' in changes) { const synced = changes.settings.newValue; if (synced) { for (const key in defaults) { @@ -278,21 +283,21 @@ var prefs = new function Prefs() { function defineReadonlyProperty(obj, key, value) { const copy = deepCopy(value); - if (typeof copy == 'object') { + if (typeof copy === 'object') { Object.freeze(copy); } Object.defineProperty(obj, key, {value: copy, configurable: true}); } function equal(a, b) { - if (!a || !b || typeof a != 'object' || typeof b != 'object') { + if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { return a === b; } - if (Object.keys(a).length != Object.keys(b).length) { + if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (const k in a) { - if (typeof a[k] == 'object') { + if (typeof a[k] === 'object') { if (!equal(a[k], b[k])) { return false; } @@ -310,7 +315,7 @@ var prefs = new function Prefs() { // Chrome and co. /Safari\/[\d.]+$/.test(navigator.userAgent) && // skip forks with Flash as those are likely to have the menu e.g. CentBrowser - !Array.from(navigator.plugins).some(p => p.name == 'Shockwave Flash') + !Array.from(navigator.plugins).some(p => p.name === 'Shockwave Flash') ); } }(); @@ -325,7 +330,7 @@ function setupLivePrefs( const checkedProps = {}; for (const id of IDs) { const element = document.getElementById(id); - checkedProps[id] = element.type == 'checkbox' ? 'checked' : 'value'; + checkedProps[id] = element.type === 'checkbox' ? 'checked' : 'value'; updateElement({id, element, force: true}); element.addEventListener('change', onChange); } @@ -333,7 +338,7 @@ function setupLivePrefs( function onChange() { const value = this[checkedProps[this.id]]; - if (prefs.get(this.id) != value) { + if (prefs.get(this.id) !== value) { prefs.set(this.id, value); } } @@ -344,7 +349,7 @@ function setupLivePrefs( force, }) { const prop = checkedProps[id]; - if (force || element[prop] != value) { + if (force || element[prop] !== value) { element[prop] = value; element.dispatchEvent(new Event('change', {bubbles: true, cancelable: true})); } diff --git a/manage.html b/manage.html index eb405e0a..b3bb0545 100644 --- a/manage.html +++ b/manage.html @@ -3,7 +3,7 @@ - + @@ -17,7 +17,7 @@ - - - - - - + + + + + + @@ -152,7 +152,7 @@ data-filter-hide=":not(.updatable):not(.update-done), .no-update:not(.update-problem)"> - @@ -216,7 +216,7 @@
- + diff --git a/backup/fileSaveLoad.js b/manage/fileSaveLoad.js similarity index 82% rename from backup/fileSaveLoad.js rename to manage/fileSaveLoad.js index 9ebd7f62..4a8397ec 100644 --- a/backup/fileSaveLoad.js +++ b/manage/fileSaveLoad.js @@ -1,364 +1,395 @@ -/* global messageBox, handleUpdate, applyOnMessage */ -'use strict'; - -const STYLISH_DUMP_FILE_EXT = '.txt'; -const STYLUS_BACKUP_FILE_EXT = '.json'; - - -function importFromFile({fileTypeFilter, file} = {}) { - return new Promise(resolve => { - const fileInput = document.createElement('input'); - if (file) { - readFile(); - return; - } - fileInput.style.display = 'none'; - fileInput.type = 'file'; - fileInput.accept = fileTypeFilter || STYLISH_DUMP_FILE_EXT; - fileInput.acceptCharset = 'utf-8'; - - document.body.appendChild(fileInput); - fileInput.initialValue = fileInput.value; - fileInput.onchange = readFile; - fileInput.click(); - - function readFile() { - if (file || fileInput.value !== fileInput.initialValue) { - file = file || fileInput.files[0]; - if (file.size > 100e6) { - console.warn("100MB backup? I don't believe you."); - importFromString('').then(resolve); - return; - } - document.body.style.cursor = 'wait'; - const fReader = new FileReader(); - fReader.onloadend = event => { - fileInput.remove(); - importFromString(event.target.result).then(numStyles => { - document.body.style.cursor = ''; - resolve(numStyles); - }); - }; - fReader.readAsText(file, 'utf-8'); - } - } - }); -} - - -function importFromString(jsonString) { - if (!BG) { - onBackgroundReady().then(() => importFromString(jsonString)); - return; - } - // create objects in background context - const json = BG.tryJSONparse(jsonString) || []; - if (typeof json.slice != 'function') { - json.length = 0; - } - const oldStyles = json.length && BG.deepCopy(BG.cachedStyles.list || []); - const oldStylesByName = json.length && new Map( - oldStyles.map(style => [style.name.trim(), style])); - - const stats = { - added: {names: [], ids: [], legend: 'importReportLegendAdded'}, - unchanged: {names: [], ids: [], legend: 'importReportLegendIdentical'}, - metaAndCode: {names: [], ids: [], legend: 'importReportLegendUpdatedBoth'}, - metaOnly: {names: [], ids: [], legend: 'importReportLegendUpdatedMeta'}, - codeOnly: {names: [], ids: [], legend: 'importReportLegendUpdatedCode'}, - invalid: {names: [], legend: 'importReportLegendInvalid'}, - }; - - let index = 0; - let lastRenderTime = performance.now(); - const renderQueue = []; - const RENDER_NAP_TIME_MAX = 1000; // ms - const RENDER_QUEUE_MAX = 50; // number of styles - const SAVE_OPTIONS = {reason: 'import', notify: false}; - - return new Promise(proceed); - - function proceed(resolve) { - while (index < json.length) { - const item = json[index++]; - const info = analyze(item); - if (info) { - // using saveStyle directly since json was parsed in background page context - return BG.saveStyle(Object.assign(item, SAVE_OPTIONS)) - .then(style => account({style, info, resolve})); - } - } - renderQueue.forEach(style => handleUpdate(style, {reason: 'import'})); - renderQueue.length = 0; - done(resolve); - } - - function analyze(item) { - if (!item || !item.name || !item.name.trim() || typeof item != 'object' - || (item.sections && typeof item.sections.slice != 'function')) { - stats.invalid.names.push(`#${index}: ${limitString(item && item.name || '')}`); - return; - } - item.name = item.name.trim(); - const byId = BG.cachedStyles.byId.get(item.id); - const byName = oldStylesByName.get(item.name); - oldStylesByName.delete(item.name); - let oldStyle; - if (byId) { - if (sameStyle(byId, item)) { - oldStyle = byId; - } else { - item.id = null; - } - } - if (!oldStyle && byName) { - item.id = byName.id; - oldStyle = byName; - } - const oldStyleKeys = oldStyle && Object.keys(oldStyle); - const metaEqual = oldStyleKeys && - oldStyleKeys.length == Object.keys(item).length && - oldStyleKeys.every(k => k == 'sections' || oldStyle[k] === item[k]); - const codeEqual = oldStyle && BG.styleSectionsEqual(oldStyle, item); - if (metaEqual && codeEqual) { - stats.unchanged.names.push(oldStyle.name); - stats.unchanged.ids.push(oldStyle.id); - return; - } - return {oldStyle, metaEqual, codeEqual}; - } - - function sameStyle(oldStyle, newStyle) { - return oldStyle.name.trim() === newStyle.name.trim() || - ['updateUrl', 'originalMd5', 'originalDigest'] - .some(field => oldStyle[field] && oldStyle[field] == newStyle[field]); - } - - function account({style, info, resolve}) { - renderQueue.push(style); - if (performance.now() - lastRenderTime > RENDER_NAP_TIME_MAX - || renderQueue.length > RENDER_QUEUE_MAX) { - renderQueue.forEach(style => handleUpdate(style, {reason: 'import'})); - setTimeout(scrollElementIntoView, 0, $('#style-' + renderQueue.pop().id)); - renderQueue.length = 0; - lastRenderTime = performance.now(); - } - setTimeout(proceed, 0, resolve); - const {oldStyle, metaEqual, codeEqual} = info; - if (!oldStyle) { - stats.added.names.push(style.name); - stats.added.ids.push(style.id); - return; - } - if (!metaEqual && !codeEqual) { - stats.metaAndCode.names.push(reportNameChange(oldStyle, style)); - stats.metaAndCode.ids.push(style.id); - return; - } - if (!codeEqual) { - stats.codeOnly.names.push(style.name); - stats.codeOnly.ids.push(style.id); - return; - } - stats.metaOnly.names.push(reportNameChange(oldStyle, style)); - stats.metaOnly.ids.push(style.id); - } - - function done(resolve) { - const numChanged = stats.metaAndCode.names.length + - stats.metaOnly.names.length + - stats.codeOnly.names.length + - stats.added.names.length; - Promise.resolve(numChanged && refreshAllTabs()).then(() => { - const report = Object.keys(stats) - .filter(kind => stats[kind].names.length) - .map(kind => { - const {ids, names, legend} = stats[kind]; - const listItemsWithId = (name, i) => - $element({dataset: {id: ids[i]}, textContent: name}); - const listItems = name => - $element({textContent: name}); - const block = - $element({tag: 'details', dataset: {id: kind}, appendChild: [ - $element({tag: 'summary', appendChild: - $element({tag: 'b', textContent: names.length + ' ' + t(legend)}) - }), - $element({tag: 'small', appendChild: - names.map(ids ? listItemsWithId : listItems) - }), - ]}); - return block; - }); - scrollTo(0, 0); - messageBox({ - title: t('importReportTitle'), - contents: report.length ? report : t('importReportUnchanged'), - buttons: [t('confirmOK'), numChanged && t('undo')], - onshow: bindClick, - }).then(({button, enter, esc}) => { - if (button == 1) { - undo(); - } - }); - resolve(numChanged); - }); - } - - function undo() { - const oldStylesById = new Map(oldStyles.map(style => [style.id, style])); - const newIds = [ - ...stats.metaAndCode.ids, - ...stats.metaOnly.ids, - ...stats.codeOnly.ids, - ...stats.added.ids, - ]; - let resolve; - index = 0; - return new Promise(resolve_ => { - resolve = resolve_; - undoNextId(); - }).then(BG.refreshAllTabs) - .then(() => messageBox({ - title: t('importReportUndoneTitle'), - contents: newIds.length + ' ' + t('importReportUndone'), - buttons: [t('confirmOK')], - })); - function undoNextId() { - if (index == newIds.length) { - resolve(); - return; - } - const id = newIds[index++]; - deleteStyleSafe({id, notify: false}).then(id => { - const oldStyle = oldStylesById.get(id); - if (oldStyle) { - saveStyleSafe(Object.assign(oldStyle, SAVE_OPTIONS)) - .then(undoNextId); - } else { - undoNextId(); - } - }); - } - } - - function bindClick(box) { - const highlightElement = event => { - const styleElement = $('#style-' + event.target.dataset.id); - if (styleElement) { - scrollElementIntoView(styleElement); - animateElement(styleElement, {className: 'highlight'}); - } - }; - for (const block of $$('details')) { - if (block.dataset.id != 'invalid') { - block.style.cursor = 'pointer'; - block.onclick = highlightElement; - } - } - } - - function limitString(s, limit = 100) { - return s.length <= limit ? s : s.substr(0, limit) + '...'; - } - - function reportNameChange(oldStyle, newStyle) { - return newStyle.name != oldStyle.name - ? oldStyle.name + ' —> ' + newStyle.name - : oldStyle.name; - } - - function refreshAllTabs() { - return getActiveTab().then(activeTab => new Promise(resolve => { - // list all tabs including chrome-extension:// which can be ours - chrome.tabs.query({}, tabs => { - const lastTab = tabs[tabs.length - 1]; - for (const tab of tabs) { - getStylesSafe({matchUrl: tab.url, enabled: true, asHash: true}).then(styles => { - const message = {method: 'styleReplaceAll', styles}; - if (tab.id == activeTab.id) { - applyOnMessage(message); - } else { - chrome.tabs.sendMessage(tab.id, message); - } - BG.updateIcon(tab, styles); - if (tab == lastTab) { - resolve(); - } - }); - } - }); - })); - } -} - - -$('#file-all-styles').onclick = () => { - getStylesSafe().then(styles => { - const text = JSON.stringify(styles, null, '\t'); - const url = 'data:text/plain;charset=utf-8,' + encodeURIComponent(text); - return url; - // for long URLs; https://github.com/schomery/stylish-chrome/issues/13#issuecomment-284582600 - }).then(fetch) - .then(res => res.blob()) - .then(blob => { - const objectURL = URL.createObjectURL(blob); - Object.assign(document.createElement('a'), { - download: generateFileName(), - href: objectURL, - type: 'application/json', - }).dispatchEvent(new MouseEvent('click')); - setTimeout(() => URL.revokeObjectURL(objectURL)); - }); - - function generateFileName() { - const today = new Date(); - const dd = ('0' + today.getDate()).substr(-2); - const mm = ('0' + (today.getMonth() + 1)).substr(-2); - const yyyy = today.getFullYear(); - return `stylus-${mm}-${dd}-${yyyy}${STYLUS_BACKUP_FILE_EXT}`; - } -}; - - -$('#unfile-all-styles').onclick = () => { - importFromFile({fileTypeFilter: STYLUS_BACKUP_FILE_EXT}); -}; - -Object.assign(document.body, { - ondragover(event) { - const hasFiles = event.dataTransfer.types.includes('Files'); - event.dataTransfer.dropEffect = hasFiles || event.target.type == 'search' ? 'copy' : 'none'; - this.classList.toggle('dropzone', hasFiles); - if (hasFiles) { - event.preventDefault(); - clearTimeout(this.fadeoutTimer); - this.classList.remove('fadeout'); - } - }, - ondragend(event) { - animateElement(this, {className: 'fadeout', removeExtraClasses: ['dropzone']}).then(() => { - this.style.animationDuration = ''; - }); - }, - ondragleave(event) { - try { - // in Firefox event.target could be XUL browser and hence there is no permission to access it - if (event.target === this) { - this.ondragend(); - } - } - catch (e) { - this.ondragend(); - } - }, - ondrop(event) { - this.ondragend(); - if (event.dataTransfer.files.length) { - event.preventDefault(); - if ($('#onlyUpdates input').checked) { - $('#onlyUpdates input').click(); - } - importFromFile({file: event.dataTransfer.files[0]}); - } - }, -}); +/* global messageBox, handleUpdate, applyOnMessage */ +'use strict'; + +const STYLISH_DUMP_FILE_EXT = '.txt'; +const STYLUS_BACKUP_FILE_EXT = '.json'; + + +function importFromFile({fileTypeFilter, file} = {}) { + return new Promise(resolve => { + const fileInput = document.createElement('input'); + if (file) { + readFile(); + return; + } + fileInput.style.display = 'none'; + fileInput.type = 'file'; + fileInput.accept = fileTypeFilter || STYLISH_DUMP_FILE_EXT; + fileInput.acceptCharset = 'utf-8'; + + document.body.appendChild(fileInput); + fileInput.initialValue = fileInput.value; + fileInput.onchange = readFile; + fileInput.click(); + + function readFile() { + if (file || fileInput.value !== fileInput.initialValue) { + file = file || fileInput.files[0]; + if (file.size > 100e6) { + console.warn("100MB backup? I don't believe you."); + importFromString('').then(resolve); + return; + } + document.body.style.cursor = 'wait'; + const fReader = new FileReader(); + fReader.onloadend = event => { + fileInput.remove(); + importFromString(event.target.result).then(numStyles => { + document.body.style.cursor = ''; + resolve(numStyles); + }); + }; + fReader.readAsText(file, 'utf-8'); + } + } + }); +} + + +function importFromString(jsonString) { + if (!BG) { + onBackgroundReady().then(() => importFromString(jsonString)); + return; + } + // create objects in background context + const json = BG.tryJSONparse(jsonString) || []; + if (typeof json.slice !== 'function') { + json.length = 0; + } + const oldStyles = json.length && BG.deepCopy(BG.cachedStyles.list || []); + const oldStylesByName = json.length && new Map( + oldStyles.map(style => [style.name.trim(), style])); + + const stats = { + added: {names: [], ids: [], legend: 'importReportLegendAdded'}, + unchanged: {names: [], ids: [], legend: 'importReportLegendIdentical'}, + metaAndCode: {names: [], ids: [], legend: 'importReportLegendUpdatedBoth'}, + metaOnly: {names: [], ids: [], legend: 'importReportLegendUpdatedMeta'}, + codeOnly: {names: [], ids: [], legend: 'importReportLegendUpdatedCode'}, + invalid: {names: [], legend: 'importReportLegendInvalid'}, + }; + + let index = 0; + let lastRenderTime = performance.now(); + const renderQueue = []; + const RENDER_NAP_TIME_MAX = 1000; // ms + const RENDER_QUEUE_MAX = 50; // number of styles + const SAVE_OPTIONS = {reason: 'import', notify: false}; + + return new Promise(proceed); + + function proceed(resolve) { + while (index < json.length) { + const item = json[index++]; + const info = analyze(item); + if (info) { + // using saveStyle directly since json was parsed in background page context + return BG.saveStyle(Object.assign(item, SAVE_OPTIONS)) + .then(style => account({style, info, resolve})); + } + } + renderQueue.forEach(style => handleUpdate(style, {reason: 'import'})); + renderQueue.length = 0; + done(resolve); + } + + function analyze(item) { + if (!item || !item.name || !item.name.trim() || typeof item !== 'object' + || (item.sections && typeof item.sections.slice !== 'function')) { + stats.invalid.names.push(`#${index}: ${limitString(item && item.name || '')}`); + return; + } + item.name = item.name.trim(); + const byId = BG.cachedStyles.byId.get(item.id); + const byName = oldStylesByName.get(item.name); + oldStylesByName.delete(item.name); + let oldStyle; + if (byId) { + if (sameStyle(byId, item)) { + oldStyle = byId; + } else { + item.id = null; + } + } + if (!oldStyle && byName) { + item.id = byName.id; + oldStyle = byName; + } + const oldStyleKeys = oldStyle && Object.keys(oldStyle); + const metaEqual = oldStyleKeys && + oldStyleKeys.length === Object.keys(item).length && + oldStyleKeys.every(k => k === 'sections' || oldStyle[k] === item[k]); + const codeEqual = oldStyle && BG.styleSectionsEqual(oldStyle, item); + if (metaEqual && codeEqual) { + stats.unchanged.names.push(oldStyle.name); + stats.unchanged.ids.push(oldStyle.id); + return; + } + return {oldStyle, metaEqual, codeEqual}; + } + + function sameStyle(oldStyle, newStyle) { + return oldStyle.name.trim() === newStyle.name.trim() || + ['updateUrl', 'originalMd5', 'originalDigest'] + .some(field => oldStyle[field] && oldStyle[field] === newStyle[field]); + } + + function account({style, info, resolve}) { + renderQueue.push(style); + if (performance.now() - lastRenderTime > RENDER_NAP_TIME_MAX + || renderQueue.length > RENDER_QUEUE_MAX) { + renderQueue.forEach(style => handleUpdate(style, {reason: 'import'})); + setTimeout(scrollElementIntoView, 0, $('#style-' + renderQueue.pop().id)); + renderQueue.length = 0; + lastRenderTime = performance.now(); + } + setTimeout(proceed, 0, resolve); + const {oldStyle, metaEqual, codeEqual} = info; + if (!oldStyle) { + stats.added.names.push(style.name); + stats.added.ids.push(style.id); + return; + } + if (!metaEqual && !codeEqual) { + stats.metaAndCode.names.push(reportNameChange(oldStyle, style)); + stats.metaAndCode.ids.push(style.id); + return; + } + if (!codeEqual) { + stats.codeOnly.names.push(style.name); + stats.codeOnly.ids.push(style.id); + return; + } + stats.metaOnly.names.push(reportNameChange(oldStyle, style)); + stats.metaOnly.ids.push(style.id); + } + + function done(resolve) { + const numChanged = stats.metaAndCode.names.length + + stats.metaOnly.names.length + + stats.codeOnly.names.length + + stats.added.names.length; + Promise.resolve(numChanged && refreshAllTabs()).then(() => { + const report = Object.keys(stats) + .filter(kind => stats[kind].names.length) + .map(kind => { + const {ids, names, legend} = stats[kind]; + const listItemsWithId = (name, i) => + $element({dataset: {id: ids[i]}, textContent: name}); + const listItems = name => + $element({textContent: name}); + const block = + $element({tag: 'details', dataset: {id: kind}, appendChild: [ + $element({tag: 'summary', appendChild: + $element({tag: 'b', textContent: names.length + ' ' + t(legend)}) + }), + $element({tag: 'small', appendChild: + names.map(ids ? listItemsWithId : listItems) + }), + ]}); + return block; + }); + scrollTo(0, 0); + messageBox({ + title: t('importReportTitle'), + contents: report.length ? report : t('importReportUnchanged'), + buttons: [t('confirmOK'), numChanged && t('undo')], + onshow: bindClick, + }).then(({button}) => { + if (button === 1) { + undo(); + } + }); + resolve(numChanged); + }); + } + + function undo() { + const oldStylesById = new Map(oldStyles.map(style => [style.id, style])); + const newIds = [ + ...stats.metaAndCode.ids, + ...stats.metaOnly.ids, + ...stats.codeOnly.ids, + ...stats.added.ids, + ]; + let resolve; + index = 0; + return new Promise(resolve_ => { + resolve = resolve_; + undoNextId(); + }).then(refreshAllTabs) + .then(() => messageBox({ + title: t('importReportUndoneTitle'), + contents: newIds.length + ' ' + t('importReportUndone'), + buttons: [t('confirmOK')], + })); + function undoNextId() { + if (index === newIds.length) { + resolve(); + return; + } + const id = newIds[index++]; + deleteStyleSafe({id, notify: false}).then(id => { + const oldStyle = oldStylesById.get(id); + if (oldStyle) { + saveStyleSafe(Object.assign(oldStyle, SAVE_OPTIONS)) + .then(undoNextId); + } else { + undoNextId(); + } + }); + } + } + + function bindClick() { + const highlightElement = event => { + const styleElement = $('#style-' + event.target.dataset.id); + if (styleElement) { + scrollElementIntoView(styleElement); + animateElement(styleElement); + } + }; + for (const block of $$('details')) { + if (block.dataset.id !== 'invalid') { + block.style.cursor = 'pointer'; + block.onclick = highlightElement; + } + } + } + + function limitString(s, limit = 100) { + return s.length <= limit ? s : s.substr(0, limit) + '...'; + } + + function reportNameChange(oldStyle, newStyle) { + return newStyle.name !== oldStyle.name + ? oldStyle.name + ' —> ' + newStyle.name + : oldStyle.name; + } + + function refreshAllTabs() { + return Promise.all([ + getActiveTab(), + getOwnTab(), + ]).then(([activeTab, ownTab]) => new Promise(resolve => { + // list all tabs including chrome-extension:// which can be ours + queryTabs().then(tabs => { + const lastTab = tabs[tabs.length - 1]; + for (const tab of tabs) { + // skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF + if (FIREFOX && !tab.width) { + if (tab === lastTab) { + resolve(); + } + continue; + } + getStylesSafe({matchUrl: tab.url, enabled: true, asHash: true}).then(styles => { + const message = {method: 'styleReplaceAll', styles}; + if (tab.id === ownTab.id) { + applyOnMessage(message); + } else { + invokeOrPostpone(tab.id === activeTab.id, + chrome.tabs.sendMessage, tab.id, message, ignoreChromeError); + } + setTimeout(BG.updateIcon, 0, tab, styles); + if (tab === lastTab) { + resolve(); + } + }); + } + }); + })); + } +} + + +$('#file-all-styles').onclick = () => { + getStylesSafe().then(styles => { + const text = JSON.stringify(styles, null, '\t'); + const url = 'data:text/plain;charset=utf-8,' + encodeURIComponent(text); + return url; + // for long URLs; https://github.com/schomery/stylus/issues/13#issuecomment-284582600 + }).then(fetch) + .then(res => res.blob()) + .then(blob => { + const objectURL = URL.createObjectURL(blob); + let link = $element({ + tag:'a', + href: objectURL, + type: 'application/json', + download: generateFileName(), + }); + // TODO: remove the fallback when FF multi-process bug is fixed + if (!FIREFOX) { + link.dispatchEvent(new MouseEvent('click')); + setTimeout(() => URL.revokeObjectURL(objectURL)); + } else { + const iframe = document.body.appendChild($element({ + tag: 'iframe', + style: 'width: 0; height: 0; position: fixed; opacity: 0;'.replace(/;/g, '!important;'), + })); + doTimeout().then(() => { + link = iframe.contentDocument.importNode(link, true); + iframe.contentDocument.body.appendChild(link); + }) + .then(doTimeout) + .then(() => link.dispatchEvent(new MouseEvent('click'))) + .then(doTimeout(1000)) + .then(() => { + URL.revokeObjectURL(objectURL); + iframe.remove(); + }); + } + }); + + function generateFileName() { + const today = new Date(); + const dd = ('0' + today.getDate()).substr(-2); + const mm = ('0' + (today.getMonth() + 1)).substr(-2); + const yyyy = today.getFullYear(); + return `stylus-${yyyy}-${mm}-${dd}${STYLUS_BACKUP_FILE_EXT}`; + } +}; + + +$('#unfile-all-styles').onclick = () => { + importFromFile({fileTypeFilter: STYLUS_BACKUP_FILE_EXT}); +}; + +Object.assign(document.body, { + ondragover(event) { + const hasFiles = event.dataTransfer.types.includes('Files'); + event.dataTransfer.dropEffect = hasFiles || event.target.type === 'search' ? 'copy' : 'none'; + this.classList.toggle('dropzone', hasFiles); + if (hasFiles) { + event.preventDefault(); + clearTimeout(this.fadeoutTimer); + this.classList.remove('fadeout'); + } + }, + ondragend() { + animateElement(this, {className: 'fadeout', removeExtraClasses: ['dropzone']}).then(() => { + this.style.animationDuration = ''; + }); + }, + ondragleave(event) { + try { + // in Firefox event.target could be XUL browser and hence there is no permission to access it + if (event.target === this) { + this.ondragend(); + } + } catch (e) { + this.ondragend(); + } + }, + ondrop(event) { + this.ondragend(); + if (event.dataTransfer.files.length) { + event.preventDefault(); + if ($('#onlyUpdates input').checked) { + $('#onlyUpdates input').click(); + } + importFromFile({file: event.dataTransfer.files[0]}); + } + }, +}); diff --git a/manage.css b/manage/manage.css similarity index 100% rename from manage.css rename to manage/manage.css diff --git a/manage.js b/manage/manage.js similarity index 86% rename from manage.js rename to manage/manage.js index d1f3149e..3d11308a 100644 --- a/manage.js +++ b/manage/manage.js @@ -1,4 +1,4 @@ -/* global messageBox, getStyleWithNoCode */ +/* global messageBox, getStyleWithNoCode, retranslateCSS */ 'use strict'; let installed; @@ -7,6 +7,9 @@ const filtersSelector = { unhide: '', }; +const ENTRY_ID_PREFIX_RAW = 'style-'; +const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW; + const newUI = { enabled: prefs.get('manage.newUI'), favicons: prefs.get('manage.newUI.favicons'), @@ -31,6 +34,15 @@ Promise.all([ showStyles(styles); }); +if (FIREFOX) { + // TODO: remove when this bug is fixed in FF + retranslateCSS({ + '.disabled h2::after': + '__MSG_genericDisabledLabel__', + '#update-all-no-updates[data-skipped-edited="true"]::after': + ' __MSG_updateAllCheckSucceededSomeEdited__', + }); +} chrome.runtime.onMessage.addListener(onRuntimeMessage); @@ -61,7 +73,7 @@ function initGlobalEvents() { // focus search field on / key document.onkeypress = event => { - if ((event.keyCode || event.which) == 47 + if ((event.keyCode || event.which) === 47 && !event.altKey && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.target.matches('[type="text"], [type="search"]')) { event.preventDefault(); @@ -102,10 +114,10 @@ function initGlobalEvents() { function showStyles(styles = []) { const sorted = styles .map(style => ({name: style.name.toLocaleLowerCase(), style})) - .sort((a, b) => (a.name < b.name ? -1 : a.name == b.name ? 0 : 1)); + .sort((a, b) => (a.name < b.name ? -1 : a.name === b.name ? 0 : 1)); let index = 0; const scrollY = (history.state || {}).scrollY; - const shouldRenderAll = scrollY > window.innerHeight; + const shouldRenderAll = scrollY > window.innerHeight || sessionStorage.justEditedStyleId; const renderBin = document.createDocumentFragment(); if (scrollY) { renderStyles(); @@ -116,8 +128,11 @@ function showStyles(styles = []) { function renderStyles() { const t0 = performance.now(); let rendered = 0; - while (index < sorted.length - && (shouldRenderAll || ++rendered < 10 || performance.now() - t0 < 10)) { + while ( + index < sorted.length && + // eslint-disable-next-line no-unmodified-loop-condition + (shouldRenderAll || ++rendered < 10 || performance.now() - t0 < 10) + ) { renderBin.appendChild(createStyleElement(sorted[index++])); } filterAndAppend({container: renderBin}); @@ -131,6 +146,14 @@ function showStyles(styles = []) { if (newUI.enabled && newUI.favicons) { debounce(handleEvent.loadFavicons, 16); } + if (sessionStorage.justEditedStyleId) { + const entry = $(ENTRY_ID_PREFIX + sessionStorage.justEditedStyleId); + delete sessionStorage.justEditedStyleId; + if (entry) { + animateElement(entry); + scrollElementIntoView(entry); + } + } } } @@ -146,7 +169,7 @@ function createStyleElement({style, name}) { checker: $('.checker', entry) || {}, nameLink: $('.style-name-link', entry), editLink: $('.style-edit-link', entry) || {}, - editHrefBase: $('.style-name-link', entry).getAttribute('href'), + editHrefBase: 'edit.html?id=', homepage: $('.homepage', entry), homepageIcon: template[`homepageIcon${newUI.enabled ? 'Small' : 'Big'}`], appliesTo: $('.applies-to', entry), @@ -166,7 +189,7 @@ function createStyleElement({style, name}) { parts.homepage.href = parts.homepage.title = style.url || ''; const entry = parts.entry.cloneNode(true); - entry.id = 'style-' + style.id; + entry.id = ENTRY_ID_PREFIX_RAW + style.id; entry.styleId = style.id; entry.styleNameLowerCase = name || style.name.toLocaleLowerCase(); entry.styleMeta = getStyleWithNoCode(style); @@ -205,18 +228,18 @@ function createStyleTargetsElement({entry, style, postponeFavicons}) { displayed.add(targetValue); const element = template.appliesToTarget.cloneNode(true); if (!newUI.enabled) { - if (numTargets == 10) { + if (numTargets === 10) { container = container.appendChild(template.extraAppliesTo.cloneNode(true)); } else if (numTargets > 1) { container.appendChild(template.appliesToSeparator.cloneNode(true)); } } else if (newUI.favicons) { let favicon = ''; - if (type == 'domains') { + if (type === 'domains') { favicon = GET_FAVICON_URL + targetValue; } else if (targetValue.startsWith('chrome-extension:')) { favicon = OWN_ICON; - } else if (type != 'regexps') { + } else if (type !== 'regexps') { favicon = targetValue.includes('://') && targetValue.match(/^.*?:\/\/([^/]+)/); favicon = favicon ? GET_FAVICON_URL + favicon[1] : ''; } @@ -269,7 +292,7 @@ Object.assign(handleEvent, { const target = event.target; const entry = target.closest('.entry'); for (const selector in handleEvent.ENTRY_ROUTES) { - for (let el = target; el && el != entry; el = el.parentElement) { + for (let el = target; el && el !== entry; el = el.parentElement) { if (el.matches(selector)) { const handler = handleEvent.ENTRY_ROUTES[selector]; return handleEvent[handler].call(el, event, entry); @@ -284,8 +307,8 @@ Object.assign(handleEvent, { } event.preventDefault(); event.stopPropagation(); - const left = event.button == 0; - const middle = event.button == 1; + const left = event.button === 0; + const middle = event.button === 1; const shift = event.shiftKey; const ctrl = event.ctrlKey; const openWindow = left && shift && !ctrl; @@ -330,15 +353,15 @@ Object.assign(handleEvent, { delete(event, entry) { const id = entry.styleId; const {name} = BG.cachedStyles.byId.get(id) || {}; - animateElement(entry, {className: 'highlight'}); + animateElement(entry); messageBox({ title: t('deleteStyleConfirm'), contents: name, className: 'danger center', buttons: [t('confirmDelete'), t('confirmCancel')], }) - .then(({button, enter, esc}) => { - if (button == 0 || enter) { + .then(({button, enter}) => { + if (button === 0 || enter) { deleteStyleSafe({id}); } }); @@ -363,10 +386,10 @@ Object.assign(handleEvent, { }, filterOnChange({target: el, forceRefilter}) { - const getValue = el => (el.type == 'checkbox' ? el.checked : el.value.trim()); + const getValue = el => (el.type === 'checkbox' ? el.checked : el.value.trim()); if (!forceRefilter) { const value = getValue(el); - if (value == el.lastValue) { + if (value === el.lastValue) { return; } el.lastValue = value; @@ -391,36 +414,36 @@ Object.assign(handleEvent, { function handleUpdate(style, {reason, method} = {}) { let entry; - let oldEntry = $('#style-' + style.id); - if (oldEntry && method == 'styleUpdated') { + let oldEntry = $(ENTRY_ID_PREFIX + style.id); + if (oldEntry && method === 'styleUpdated') { handleToggledOrCodeOnly(); } entry = entry || createStyleElement({style}); if (oldEntry) { - if (oldEntry.styleNameLowerCase == entry.styleNameLowerCase) { + if (oldEntry.styleNameLowerCase === entry.styleNameLowerCase) { installed.replaceChild(entry, oldEntry); } else { oldEntry.remove(); } } - if (reason == 'update' && entry.matches('.updatable')) { + if (reason === 'update' && entry.matches('.updatable')) { handleUpdateInstalled(); } filterAndAppend({entry}); - if (!entry.matches('.hidden') && reason != 'import') { - animateElement(entry, {className: 'highlight'}); + if (!entry.matches('.hidden') && reason !== 'import') { + animateElement(entry); scrollElementIntoView(entry); } function handleToggledOrCodeOnly() { const newStyleMeta = getStyleWithNoCode(style); const diff = objectDiff(oldEntry.styleMeta, newStyleMeta); - if (diff.length == 0) { + if (diff.length === 0) { // only code was modified entry = oldEntry; oldEntry = null; } - if (diff.length == 1 && diff[0].key == 'enabled') { + if (diff.length === 1 && diff[0].key === 'enabled') { oldEntry.classList.toggle('enabled', style.enabled); oldEntry.classList.toggle('disabled', !style.enabled); $$('.checker', oldEntry).forEach(el => (el.checked = style.enabled)); @@ -440,7 +463,7 @@ function handleUpdate(style, {reason, method} = {}) { function handleDelete(id) { - const node = $('#style-' + id); + const node = $(ENTRY_ID_PREFIX + id); if (node) { node.remove(); if (node.matches('.can-update')) { @@ -458,8 +481,8 @@ function switchUI({styleOnly} = {}) { // ensure the global option is processed first for (const el of [$('#manage.newUI'), ...$$('[id^="manage.newUI."]')]) { const id = el.id.replace(/^manage\.newUI\.?/, '') || 'enabled'; - const value = el.type == 'checkbox' ? el.checked : Number(el.value); - const valueChanged = value !== newUI[id] && (id == 'enabled' || current.enabled); + const value = el.type === 'checkbox' ? el.checked : Number(el.value); + const valueChanged = value !== newUI[id] && (id === 'enabled' || current.enabled); current[id] = value; changed[id] = valueChanged; someChanged |= valueChanged; @@ -510,7 +533,7 @@ function switchUI({styleOnly} = {}) { if (missingFavicons) { getStylesSafe().then(styles => { for (const style of styles) { - const entry = $('#style-' + style.id); + const entry = $(ENTRY_ID_PREFIX + style.id); if (entry) { createStyleTargetsElement({entry, style, postponeFavicons: true}); } @@ -545,7 +568,7 @@ function checkUpdateAll() { $('#apply-all-updates').classList.add('hidden'); $('#update-all-no-updates').classList.add('hidden'); - const ignoreDigest = this && this.id == 'check-all-updates-force'; + const ignoreDigest = this && this.id === 'check-all-updates-force'; $$('.updatable:not(.can-update)' + (ignoreDigest ? '' : ':not(.update-problem)')) .map(el => checkUpdate(el, {single: false})); @@ -562,7 +585,7 @@ function checkUpdateAll() { total = value; break; case BG.updater.UPDATED: - if (++updated == 1) { + if (++updated === 1) { $('#apply-all-updates').disabled = true; $('#apply-all-updates').classList.remove('hidden'); } @@ -570,7 +593,7 @@ function checkUpdateAll() { // fallthrough case BG.updater.SKIPPED: checked++; - if (details == BG.updater.EDITED || details == BG.updater.MAYBE_EDITED) { + if (details === BG.updater.EDITED || details === BG.updater.MAYBE_EDITED) { skippedEdited++; } reportUpdateState(state, value, details); @@ -583,13 +606,13 @@ function checkUpdateAll() { function done() { document.body.classList.remove('update-in-progress'); - $('#check-all-updates').disabled = total == 0; + $('#check-all-updates').disabled = total === 0; $('#apply-all-updates').disabled = false; renderUpdatesOnlyFilter({check: updated + skippedEdited > 0}); if (!updated) { $('#update-all-no-updates').dataset.skippedEdited = skippedEdited > 0; $('#update-all-no-updates').classList.remove('hidden'); - $('#check-all-updates-force').classList.toggle('hidden', skippedEdited == 0); + $('#check-all-updates-force').classList.toggle('hidden', skippedEdited === 0); } } } @@ -612,7 +635,7 @@ function checkUpdate(entry, {single = true} = {}) { function reportUpdateState(state, style, details) { - const entry = $('#style-' + style.id); + const entry = $(ENTRY_ID_PREFIX + style.id); entry.classList.remove('checking-update'); switch (state) { case BG.updater.UPDATED: @@ -625,16 +648,16 @@ function reportUpdateState(state, style, details) { if (entry.classList.contains('can-update')) { break; } - const same = details == BG.updater.SAME_MD5 || details == BG.updater.SAME_CODE; - const edited = details == BG.updater.EDITED || details == BG.updater.MAYBE_EDITED; + const same = details === BG.updater.SAME_MD5 || details === BG.updater.SAME_CODE; + const edited = details === BG.updater.EDITED || details === BG.updater.MAYBE_EDITED; entry.dataset.details = details; if (!details) { details = t('updateCheckFailServerUnreachable'); - } else if (typeof details == 'number') { + } else if (typeof details === 'number') { details = t('updateCheckFailBadResponseCode', [details]); - } else if (details == BG.updater.EDITED) { + } else if (details === BG.updater.EDITED) { details = t('updateCheckSkippedLocallyEdited') + '\n' + t('updateCheckManualUpdateHint'); - } else if (details == BG.updater.MAYBE_EDITED) { + } else if (details === BG.updater.MAYBE_EDITED) { details = t('updateCheckSkippedMaybeLocallyEdited') + '\n' + t('updateCheckManualUpdateHint'); } const message = same ? t('updateCheckSucceededNoUpdate') : details; @@ -696,12 +719,11 @@ function searchStyles({immediately, container}) { const searchElement = $('#search'); const query = searchElement.value.toLocaleLowerCase(); const queryPrev = searchElement.lastValue || ''; - if (query == queryPrev && !immediately && !container) { + if (query === queryPrev && !immediately && !container) { return; } if (!immediately) { - clearTimeout(searchStyles.timeout); - searchStyles.timeout = setTimeout(searchStyles, 150, {immediately: true}); + debounce(searchStyles, 150, {immediately: true}); return; } searchElement.lastValue = query; @@ -719,7 +741,7 @@ function searchStyles({immediately, container}) { style.url && isMatchingText(style.url) || isMatchingStyle(style))); } - if (entry.classList.contains('not-matching') != !isMatching) { + if (entry.classList.contains('not-matching') !== !isMatching) { entry.classList.toggle('not-matching', !isMatching); needsRefilter = true; } @@ -764,8 +786,7 @@ function filterAndAppend({entry, container}) { if (!filtersSelector.hide || !entry.matches(filtersSelector.hide)) { entry.classList.add('hidden'); } - } - if ($('#search').value.trim()) { + } else if ($('#search').value.trim()) { searchStyles({immediately: true, container}); } reapplyFilter(container); @@ -774,14 +795,20 @@ function filterAndAppend({entry, container}) { function reapplyFilter(container = installed) { // A: show - let toUnhide = filtersSelector.hide ? filterContainer({hide: false}) : container; + let toHide = []; + let toUnhide = []; + if (filtersSelector.hide) { + filterContainer({hide: false}); + } else { + toUnhide = container; + } // showStyles() is building the page and no filters are active if (toUnhide instanceof DocumentFragment) { installed.appendChild(toUnhide); return; } else if (toUnhide.length && $('#search').value.trim()) { searchStyles({immediately: true, container: toUnhide}); - toUnhide = filterContainer({hide: false}); + filterContainer({hide: false}); } // filtering needed or a single-element job from handleUpdate() const entries = installed.children; @@ -798,7 +825,9 @@ function reapplyFilter(container = installed) { } } // B: hide - const toHide = filtersSelector.hide ? filterContainer({hide: true}) : []; + if (filtersSelector.hide) { + filterContainer({hide: true}); + } if (!toHide.length) { return; } @@ -821,7 +850,7 @@ function reapplyFilter(container = installed) { shuffle(false); setTimeout(shuffle, 0, true); // single-element job from handleEvent(): add the last wraith - if (toHide.length == 1 && toHide[0].parentElement != installed) { + if (toHide.length === 1 && toHide[0].parentElement !== installed) { installed.appendChild(toHide[0]); } return; @@ -831,16 +860,16 @@ function reapplyFilter(container = installed) { if (container.filter) { if (hide) { // already filtered in previous invocation - return container; + return; } - const toHide = [], toUnhide = []; for (const el of container) { (el.matches(selector) ? toUnhide : toHide).push(el); } - container = toHide; - return toUnhide; + return; + } else if (hide) { + toHide = $$(selector, container); } else { - return $$(selector, container); + toUnhide = $$(selector, container); } } @@ -856,7 +885,7 @@ function reapplyFilter(container = installed) { const skipGroup = state => { const start = i; const first = entry; - while (entry && entry.classList.contains('hidden') == state) { + while (entry && entry.classList.contains('hidden') === state) { entry = entry.nextElementSibling; i++; } @@ -874,7 +903,7 @@ function reapplyFilter(container = installed) { // 3. move the shortest group; repeat 2-3 if (hidden.len < visible.len && (fullPass || hidden.len % 2)) { // 3a. move hidden under the horizon - for (let j = 0; j < (fullPass ? hidden.len : 1); j++) { + for (let j = 0; j < (fullPass ? hidden.len : 1); j++) { const entry = entries[hidden.start]; installed.insertBefore(entry, horizon); horizon = entry; @@ -949,14 +978,19 @@ function objectDiff(first, second, path = '') { diff.push({path, key, values: [a], type: 'removed'}); continue; } - if (a && typeof a.filter == 'function' && b && typeof b.filter == 'function') { - if (a.length != b.length - || a.some((el, i) => !el || typeof el != 'object' ? el != b[i] - : objectDiff(el, b[i], path + key + '[' + i + '].').length) + if (a && typeof a.filter === 'function' && b && typeof b.filter === 'function') { + if ( + a.length !== b.length || + a.some((el, i) => { + const result = !el || typeof el !== 'object' + ? el !== b[i] + : objectDiff(el, b[i], path + key + '[' + i + '].').length; + return result; + }) ) { diff.push({path, key, values: [a, b], type: 'changed'}); } - } else if (typeof a == 'object' && typeof b == 'object') { + } else if (typeof a === 'object' && typeof b === 'object') { diff.push(...objectDiff(a, b, path + key + '.')); } else { diff.push({path, key, values: [a, b], type: 'changed'}); diff --git a/manifest.json b/manifest.json index 9612f5c4..958f5a5e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name": "Stylus", - "version": "1.0.8", + "version": "1.1.1", "minimum_chrome_version": "49", "description": "__MSG_description__", "homepage_url": "http://add0n.com/stylus.html", @@ -19,7 +19,13 @@ "" ], "background": { - "scripts": ["messaging.js", "storage.js", "prefs.js", "background.js", "update.js"] + "scripts": [ + "js/messaging.js", + "background/storage.js", + "js/prefs.js", + "background/background.js", + "background/update.js" + ] }, "commands": { "openManage": { @@ -35,13 +41,13 @@ "run_at": "document_start", "all_frames": true, "match_about_blank": true, - "js": ["apply.js"] + "js": ["content/apply.js"] }, { "matches": ["http://userstyles.org/*", "https://userstyles.org/*"], "run_at": "document_start", "all_frames": false, - "js": ["install.js"] + "js": ["content/install.js"] } ], "browser_action": { @@ -56,7 +62,7 @@ }, "default_locale": "en", "options_ui": { - "page": "options/index.html", + "page": "options.html", "chrome_style": true } } diff --git a/msgbox/msgbox.js b/msgbox/msgbox.js index a7ebbe2a..8faf8d60 100644 --- a/msgbox/msgbox.js +++ b/msgbox/msgbox.js @@ -30,9 +30,9 @@ function messageBox({ key(event) { const keyCode = event.keyCode || event.which; if (!event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey - && (keyCode == 13 || keyCode == 27)) { + && (keyCode === 13 || keyCode === 27)) { event.preventDefault(); - resolveWith(keyCode == 13 ? {enter: true} : {esc: true}); + resolveWith(keyCode === 13 ? {enter: true} : {esc: true}); } }, scroll() { @@ -52,7 +52,7 @@ function messageBox({ unbindAndRemoveSelf(); } const id = 'message-box'; - const putAs = typeof contents == 'string' ? 'innerHTML' : 'appendChild'; + const putAs = typeof contents === 'string' ? 'innerHTML' : 'appendChild'; messageBox.element = $element({id, className, appendChild: [ $element({appendChild: [ $element({id: `${id}-title`, innerHTML: title}), diff --git a/options/index.html b/options.html similarity index 73% rename from options/index.html rename to options.html index 5812ddc3..c673ac44 100644 --- a/options/index.html +++ b/options.html @@ -2,17 +2,41 @@ Stylus - - - - - - + + + + + +
+
+

+
+ + +
+
+

@@ -104,6 +128,6 @@
- + diff --git a/options/index.css b/options/index.css index b1501ed7..1b9508f7 100644 --- a/options/index.css +++ b/options/index.css @@ -1,10 +1,12 @@ html.opera { - text-align: center; + display: flex; + align-items: center; + justify-content: center; + height: 100%; } html.opera body { - display: inline-block; - text-align: initial; + width: auto; } html.firefox .block { @@ -19,7 +21,15 @@ body { margin: 0; font-family: "Helvetica Neue", Helvetica, sans-serif; font-size: 12px; - width: calc(16px + 100px + 8px + 260px + 8px + 60px + 4px + 16px); + width: 480px; + min-width: 400px; + max-width: 800px; +} + +@media (min-width: 401px) { + .firefox body { + width: calc(100% - 6px); /* TODO: rework to avoid compensating padding of 'html.firefox .block' */ + } } .firefox .chromium-only { @@ -31,7 +41,7 @@ body { align-items: center; margin: 1em 0; border-bottom: 1px dotted #ccc; - padding: 0 0 1em 16px; + padding: 0 16px 1em; position: relative; } @@ -41,29 +51,28 @@ body { } h1 { - width: 100px; + width: 30%; margin: 0; font-size: 120%; font-weight: bold; padding-right: 8px; + box-sizing: border-box; word-wrap: break-word; } -label { - display: block; - white-space: nowrap; - margin: .25ex 0; +.items { + width: 70%; } -label > * { - display: inline-block; - vertical-align: middle; +label { + display: flex; + margin: .25ex 0; + align-items: center; } label > :first-child { - width: 260px; - white-space: normal; margin-right: 8px; + flex-grow: 1; } label:not([disabled]) > :first-child { @@ -72,6 +81,7 @@ label:not([disabled]) > :first-child { label:not([disabled]):hover > :first-child { text-shadow: 0 0 0.01px rgba(0, 0, 0, .25); + cursor: pointer; } button, @@ -80,6 +90,7 @@ input[type="color"], .onoffswitch { width: 60px; box-sizing: border-box; + flex-shrink: 0; } a { @@ -104,6 +115,14 @@ input[type="color"] { height: 2em; } +.iconset { + display: flex; +} + +.iconset input { + display: block; +} + #actions { justify-content: space-around; align-items: stretch; @@ -140,7 +159,7 @@ input[type="color"] { margin-top: 1px; } -#updates-installed:after { +#updates-installed::after { content: attr(data-value); margin-left: .5ex; font-weight: bold; @@ -221,10 +240,11 @@ sup { box-shadow: inset 2px 2px 4px rgba(0,0,0,0.1); } -.onoffswitch span:before { +.onoffswitch span::before { content: ""; display: block; width: 18px; + height: 18px; margin: -3px; background: #efefef; position: absolute; @@ -239,7 +259,7 @@ sup { background-color: #CAEBE3; } -.onoffswitch input:checked + span, .onoffswitch input:checked + span:before { +.onoffswitch input:checked + span, .onoffswitch input:checked + span::before { border-color: #CAEBE3; } @@ -247,7 +267,7 @@ sup { margin-left: 0; } -.onoffswitch input:checked + span:before { +.onoffswitch input:checked + span::before { right: 0; background-color: #04BA9F; box-shadow: 3px 6px 18px 0 rgba(0, 0, 0, 0.2); diff --git a/options/index.js b/options/index.js index 3b4dce31..10404e5b 100644 --- a/options/index.js +++ b/options/index.js @@ -1,6 +1,7 @@ 'use strict'; setupLivePrefs(); +setupRadioButtons(); enforceInputRange($('#popupWidth')); // actions @@ -14,7 +15,7 @@ document.onclick = e => { switch (target.dataset.cmd) { case 'open-manage': - openURL({url: '/manage.html'}); + openURL({url: 'manage.html'}); break; case 'check-updates': @@ -61,3 +62,26 @@ function checkUpdates() { $('#updates-installed').dataset.value = updated || ''; } } + +function setupRadioButtons() { + const sets = {}; + const onChange = function () { + const newValue = sets[this.name].indexOf(this); + if (newValue >= 0 && prefs.get(this.name) !== newValue) { + prefs.set(this.name, newValue); + } + }; + // group all radio-inputs by name="prefName" attribute + for (const el of $$('input[type="radio"][name]')) { + (sets[el.name] = sets[el.name] || []).push(el); + el.addEventListener('change', onChange); + } + // select the input corresponding to the actual pref value + for (const name in sets) { + sets[name][prefs.get(name)].checked = true; + } + // listen to pref changes and update the values + prefs.subscribe((key, value) => { + sets[key][value].checked = true; + }, Object.keys(sets)); +} diff --git a/popup.html b/popup.html index 7684eb05..933df669 100644 --- a/popup.html +++ b/popup.html @@ -2,7 +2,7 @@ - +