Merge branch 'master' into dev-color-scheme

This commit is contained in:
eight04 2020-08-15 04:47:27 +08:00
commit 72488cf099
83 changed files with 4512 additions and 2382 deletions

20
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: ci
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: ['10', '12', '14']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: npm install
- run: npm test

View File

@ -1277,6 +1277,10 @@
"message": "Code formatieren", "message": "Code formatieren",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "Hinweis: Mache einen Rechtsklick auf \"Code formatieren\" oder nutze das weiter unten definierte Tastenkürzel, um den Code zu formatieren ohne dieses Fenster anzuzeigen.",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "Rücke @media / @supports ein", "message": "Rücke @media / @supports ein",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

View File

@ -1297,6 +1297,10 @@
"message": "Beautify", "message": "Beautify",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "Hint: right-click the “Beautify” button or use the keyboard shortcut defined below to beautify without showing this panel",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "Indent @media, @supports", "message": "Indent @media, @supports",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

View File

@ -93,7 +93,7 @@
"description": "" "description": ""
}, },
"checkAllUpdates": { "checkAllUpdates": {
"message": "Rechercher des mises à jour pour tous les styles", "message": "Rechercher des mises à jour",
"description": "Label for the button to check all styles for updates" "description": "Label for the button to check all styles for updates"
}, },
"checkAllUpdatesForce": { "checkAllUpdatesForce": {
@ -101,7 +101,7 @@
"description": "Label for the button to apply all detected updates" "description": "Label for the button to apply all detected updates"
}, },
"checkForUpdate": { "checkForUpdate": {
"message": "Rechercher des mises à jour", "message": "Rechercher une mise à jour",
"description": "Label for the button to check a single style for an update" "description": "Label for the button to check a single style for an update"
}, },
"checkingForUpdate": { "checkingForUpdate": {
@ -292,6 +292,10 @@
"message": "Glisser votre fichier de sauvegarde nimporte où sur cette page pour limporter.", "message": "Glisser votre fichier de sauvegarde nimporte où sur cette page pour limporter.",
"description": "Drag'n'drop message" "description": "Drag'n'drop message"
}, },
"dragDropUsercssTabstrip": {
"message": "Pour installer le fichier, glisser le fichier sur la barre des onglets (la zone où les titres des onglets sont affichés).",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": { "editDeleteText": {
"message": "Supprimer", "message": "Supprimer",
"description": "Label for the context menu item in the editor to delete selected text" "description": "Label for the context menu item in the editor to delete selected text"
@ -617,6 +621,14 @@
"message": "Une erreur est survenue durant la surveillance du fichier", "message": "Une erreur est survenue durant la surveillance du fichier",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Gardez cet onglet ouvert pour mettre à jour le style automatiquement basé sur les modifications extérieures.",
"description": "The label of live-reload feature"
},
"liveReloadInstallHintFF": {
"message": "Gardez cet onglet ouvert ainsi que l´onglet original pour mettre à jour le style automatiquement basé sur les modifications extérieures.",
"description": "The extra hint of live-reload feature shown only for file:// URLs in Firefox"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Rechargement immédiat", "message": "Rechargement immédiat",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
@ -674,15 +686,15 @@
"description": "Tooltip for the checkbox to show only locally created styles i.e. non-updatable" "description": "Tooltip for the checkbox to show only locally created styles i.e. non-updatable"
}, },
"manageOnlyNonUsercss": { "manageOnlyNonUsercss": {
"message": "Uniquement les styles non Usercss", "message": "Styles non Usercss uniquement",
"description": "Checkbox to show only non-Usercss (standard) styles" "description": "Checkbox to show only non-Usercss (standard) styles"
}, },
"manageOnlyUpdates": { "manageOnlyUpdates": {
"message": "Uniquement avec mises à jour ou problèmes", "message": "Avec mises à jour ou problèmes uniquement",
"description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed" "description": "Checkbox to show only styles that have updates after check-all-styles-for-updates was performed"
}, },
"manageOnlyUsercss": { "manageOnlyUsercss": {
"message": "Uniquement les styles Usercss", "message": "Styles Usercss uniquement",
"description": "Checkbox to show only Usercss styles" "description": "Checkbox to show only Usercss styles"
}, },
"menuShowBadge": { "menuShowBadge": {
@ -1464,7 +1476,7 @@
"description": "Note in the toolbar popup for some file types that cannot be accessed" "description": "Note in the toolbar popup for some file types that cannot be accessed"
}, },
"updateAllCheckSucceededNoUpdate": { "updateAllCheckSucceededNoUpdate": {
"message": "All styles are up to date.", "message": "Aucune mise à jour trouvée.",
"description": "Text that displays when an update all check completed and no updates are available" "description": "Text that displays when an update all check completed and no updates are available"
}, },
"updateAllCheckSucceededSomeEdited": { "updateAllCheckSucceededSomeEdited": {

View File

@ -1162,7 +1162,7 @@
"description": "Label for the checkbox controlling 'edit' action behavior in the popup." "description": "Label for the checkbox controlling 'edit' action behavior in the popup."
}, },
"popupStylesFirst": { "popupStylesFirst": {
"message": "スタイルをコマンドボタンより上に表示", "message": "スタイル一覧をコマンドボタンより上に表示",
"description": "Label for the checkbox controlling section order in the popup." "description": "Label for the checkbox controlling section order in the popup."
}, },
"prefShowBadge": { "prefShowBadge": {
@ -1305,6 +1305,10 @@
"message": "書式整形", "message": "書式整形",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "ヒント: 書式整形ボタンを右クリックするか、下で設定したキーボードショートカットを使用すると、このパネルを表示せずに書式設定が行えます",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "@media, @supports をインデント", "message": "@media, @supports をインデント",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

File diff suppressed because it is too large Load Diff

View File

@ -1285,6 +1285,10 @@
"message": "Mooier maken", "message": "Mooier maken",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "Hint: klik met de rechtermuisknop op de knop Mooier maken of gebruik de hieronder gedefinieerde sneltoets om iets mooier te maken zonder dit paneel te tonen",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "@media, @supports inspringen", "message": "@media, @supports inspringen",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

View File

@ -1309,6 +1309,10 @@
"message": "Upiększ", "message": "Upiększ",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "Wskazówka: kliknij prawym przyciskiem myszy przycisk “Upiększ” lub użyj zdefiniowanego poniżej skrótu klawiaturowego, aby upiększyć bez pokazywania tego panelu",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "Wcięcie @media, @supports", "message": "Wcięcie @media, @supports",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

View File

@ -244,6 +244,14 @@
"message": "Да", "message": "Да",
"description": "'Yes' button in a confirm dialog" "description": "'Yes' button in a confirm dialog"
}, },
"copied": {
"message": "Скопировано в буфер обмена",
"description": "Message shown when content has been copied to the clipboard"
},
"copy": {
"message": "Скопировать в буфер обмена",
"description": "Tooltip for elements which can be copied"
},
"dateInstalled": { "dateInstalled": {
"message": "Дата установки", "message": "Дата установки",
"description": "Option text for the user to sort the style by install date" "description": "Option text for the user to sort the style by install date"
@ -284,6 +292,10 @@
"message": "Перетащите файл с резервной копией стилей в любое место этой страницы, чтобы импортировать его.", "message": "Перетащите файл с резервной копией стилей в любое место этой страницы, чтобы импортировать его.",
"description": "Drag'n'drop message" "description": "Drag'n'drop message"
}, },
"dragDropUsercssTabstrip": {
"message": "Чтобы установить файл, поместите его на вкладку (область, где отображаются заголовки вкладок).",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": { "editDeleteText": {
"message": "Удалить", "message": "Удалить",
"description": "Label for the context menu item in the editor to delete selected text" "description": "Label for the context menu item in the editor to delete selected text"
@ -441,6 +453,10 @@
"message": "Нажмите клавишу", "message": "Нажмите клавишу",
"description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short"
}, },
"hostDisabled": {
"message": "Этот хост был отключен из-за ошибки в текущей версии используемого браузера",
"description": "Tooltip for cloud host disabled"
},
"importAppendLabel": { "importAppendLabel": {
"message": "Добавить к стилю", "message": "Добавить к стилю",
"description": "Label for the button to import a style and append to the existing sections" "description": "Label for the button to import a style and append to the existing sections"
@ -613,6 +629,14 @@
"message": "Ошибка слежения за файлом", "message": "Ошибка слежения за файлом",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Держите эту вкладку открытой, чтобы автоматически обновлять стиль при внешних изменениях.",
"description": "The label of live-reload feature"
},
"liveReloadInstallHintFF": {
"message": "Оставьте эту вкладку и исходную вкладку открытой, чтобы автоматически обновлять стиль при внешних изменениях.",
"description": "The extra hint of live-reload feature shown only for file:// URLs in Firefox"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Автозагрузка изменений", "message": "Автозагрузка изменений",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
@ -909,6 +933,10 @@
"message": "Менеджер", "message": "Менеджер",
"description": "Link to open the manage page." "description": "Link to open the manage page."
}, },
"openOptions": {
"message": "Опции",
"description": "Go to Options UI"
},
"openStylesManager": { "openStylesManager": {
"message": "Менеджер стилей", "message": "Менеджер стилей",
"description": "Label for the style maanger opener in the browser action context menu." "description": "Label for the style maanger opener in the browser action context menu."
@ -969,6 +997,10 @@
"message": "Обновления", "message": "Обновления",
"description": "" "description": ""
}, },
"optionsCustomizeSync": {
"message": "Синхронизация с облаком",
"description": ""
},
"optionsHeading": { "optionsHeading": {
"message": "Настройки", "message": "Настройки",
"description": "Heading for options section on manage page." "description": "Heading for options section on manage page."
@ -1013,6 +1045,70 @@
"message": "Интервал обновления стилей, в часах (укажите 0 для выключения)", "message": "Интервал обновления стилей, в часах (укажите 0 для выключения)",
"description": "" "description": ""
}, },
"optionsSyncNone": {
"message": "Ничего",
"description": ""
},
"optionsSyncConnect": {
"message": "Подключить",
"description": ""
},
"optionsSyncDisconnect": {
"message": "Отсоединить",
"description": ""
},
"optionsSyncSyncNow": {
"message": "Синхронизировать",
"description": ""
},
"optionsSyncLogin": {
"message": "Логин",
"description": ""
},
"optionsSyncStatusPull": {
"message": "Загружено стилей $loaded$ из $total$",
"description": "",
"placeholders": {
"loaded": {
"content": "$1"
},
"total": {
"content": "$2"
}
}
},
"optionsSyncStatusPush": {
"message": "Загружено стилей $loaded$ из $total$",
"description": "",
"placeholders": {
"loaded": {
"content": "$1"
},
"total": {
"content": "$2"
}
}
},
"optionsSyncStatusSyncing": {
"message": "Синхронизация",
"description": ""
},
"optionsSyncStatusConnecting": {
"message": "Подключение…",
"description": ""
},
"optionsSyncStatusConnected": {
"message": "Подключено",
"description": ""
},
"optionsSyncStatusDisconnecting": {
"message": "Отсоединение…",
"description": ""
},
"optionsSyncStatusDisconnected": {
"message": "Отсоединено",
"description": ""
},
"paginationCurrent": { "paginationCurrent": {
"message": "Текущая страница", "message": "Текущая страница",
"description": "Tooltip for the current page index in search results" "description": "Tooltip for the current page index in search results"
@ -1089,6 +1185,10 @@
"message": "Временно применяет стиль пока вы его редактируете.\nСохраните стиль, чтобы сделать изменения постоянными.", "message": "Временно применяет стиль пока вы его редактируете.\nСохраните стиль, чтобы сделать изменения постоянными.",
"description": "Tooltip for the checkbox in style editor to enable live preview while editing." "description": "Tooltip for the checkbox in style editor to enable live preview while editing."
}, },
"reload": {
"message": "Перезагрузить расширение Stylus",
"description": "Context menu reload"
},
"replace": { "replace": {
"message": "Заменить", "message": "Заменить",
"description": "Label before the replace input field in the editor shown on Ctrl-H" "description": "Label before the replace input field in the editor shown on Ctrl-H"
@ -1496,6 +1596,10 @@
"message": "Экспорт Dropbox", "message": "Экспорт Dropbox",
"description": "" "description": ""
}, },
"syncDropboxDeprecated": {
"message": "Импорт/экспорт Dropbox заменен более продвинутой синхронизацией стилей на странице параметров.",
"description": ""
},
"retrieveDropboxSync": { "retrieveDropboxSync": {
"message": "Импорт Dropbox", "message": "Импорт Dropbox",
"description": "" "description": ""

View File

@ -40,6 +40,10 @@
"message": "Şuraya uygulanır", "message": "Şuraya uygulanır",
"description": "Label for 'applies to' fields on the edit/add screen" "description": "Label for 'applies to' fields on the edit/add screen"
}, },
"appliesLineWidgetWarning": {
"message": "Küçültülmüş CSS ile çalışmaz",
"description": "A warning that applies-to information won't show properly with minified CSS"
},
"appliesRegexpOption": { "appliesRegexpOption": {
"message": "regexp ile eşleşen URL'ler", "message": "regexp ile eşleşen URL'ler",
"description": "Option to make the style apply to the entered string as a regular expression" "description": "Option to make the style apply to the entered string as a regular expression"
@ -68,6 +72,14 @@
"message": "Yazar", "message": "Yazar",
"description": "Label for the style author" "description": "Label for the style author"
}, },
"backupButtons": {
"message": "Yedek",
"description": "Heading for backup"
},
"backupMessage": {
"message": "Bir dosya seçin veya bu sayfaya sürükleyip bırakın.",
"description": "Message for backup"
},
"bckpInstStyles": { "bckpInstStyles": {
"message": "Dışa aktar", "message": "Dışa aktar",
"description": "" "description": ""
@ -76,6 +88,10 @@
"message": "Tüm stiller için güncellemeleri denetle", "message": "Tüm stiller için güncellemeleri denetle",
"description": "Label for the button to check all styles for updates" "description": "Label for the button to check all styles for updates"
}, },
"checkAllUpdatesForce": {
"message": "Tekrar kontrol et, herhangi bir stil düzenlemedim!",
"description": "Label for the button to apply all detected updates"
},
"checkForUpdate": { "checkForUpdate": {
"message": "Güncellemeleri denetle", "message": "Güncellemeleri denetle",
"description": "Label for the button to check a single style for an update" "description": "Label for the button to check a single style for an update"
@ -92,6 +108,10 @@
"message": "Parantezleri ve tırnak işaretlerini otomatik olarak kapat", "message": "Parantezleri ve tırnak işaretlerini otomatik olarak kapat",
"description": "Label for the checkbox in the style editor." "description": "Label for the checkbox in the style editor."
}, },
"cm_autoCloseBracketsTooltip": {
"message": "()[]{}''\"\" Açılışlarından birini yazarken otomatik olarak bir kapanış çifti ekleyin",
"description": "Label for the checkbox in the style editor."
},
"cm_autocompleteOnTyping": { "cm_autocompleteOnTyping": {
"message": "Yazarken tamamla", "message": "Yazarken tamamla",
"description": "Label for the checkbox in the style editor." "description": "Label for the checkbox in the style editor."
@ -104,6 +124,14 @@
"message": "Akıllı girintili tab kullan", "message": "Akıllı girintili tab kullan",
"description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor."
}, },
"cm_keyMap": {
"message": "Tuşeşlem",
"description": "Label for the drop-down list controlling the keymap for the style editor."
},
"cm_lineWrapping": {
"message": "Kelime kaydırma",
"description": "Label for the checkbox controlling word wrap option for the style editor."
},
"cm_matchHighlight": { "cm_matchHighlight": {
"message": "Vurgulama", "message": "Vurgulama",
"description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor." "description": "Label for the drop-down list controlling the automatic highlighting of current word/selection occurrences in the style editor."
@ -112,6 +140,26 @@
"message": "Yalnızca seçim", "message": "Yalnızca seçim",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text"
}, },
"cm_matchHighlightToken": {
"message": "İmleç altındaki token",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of the word/token under cursor even if nothing is selected"
},
"cm_resizeGripHint": {
"message": "Yüksekliği en üst düzeye çıkarmak/geri yüklemek için çift tıklayın",
"description": "Tooltip for the resize grip in style editor"
},
"cm_selectByTokens": {
"message": "Çift tıklamak tokenleri seçer",
"description": "Label for the checkbox in the editor."
},
"cm_selectByTokensTooltip": {
"message": "Token örnekleri: .foo-bar-2 #aabbcc 0.32 !important\nDevre dışı bırakıldığında: noktalama işareti ile ayrılmış kelimeler seçilir.",
"description": ""
},
"cm_smartIndent": {
"message": "Akıllı girinti kullanma",
"description": "Label for the checkbox controlling smart indentation option for the style editor."
},
"cm_tabSize": { "cm_tabSize": {
"message": "Tab büyüklüğü", "message": "Tab büyüklüğü",
"description": "Label for the text box controlling tab size option for the style editor." "description": "Label for the text box controlling tab size option for the style editor."
@ -120,10 +168,30 @@
"message": "Tema", "message": "Tema",
"description": "Label for the style editor's CSS theme." "description": "Label for the style editor's CSS theme."
}, },
"colorpickerSwitchFormatTooltip": {
"message": "Format değişimleri: HEX -> RGB -> HSL.\nYönü ters çevirmek için Shift tuşunu basılı tutup tıklatın.\nAyrıca PgUp (PageUp), PgDn (PageDown) tuşları ile.",
"description": "Tooltip for the switch button in the color picker popup in the style editor."
},
"colorpickerTooltip": {
"message": "Renk seçiciyi aç",
"description": "Tooltip for the colored squares shown before CSS colors in the style editor."
},
"configOnChange": { "configOnChange": {
"message": "değişiklikte", "message": "değişiklikte",
"description": "VERY SHORT label for the checkbox in style config dialog after the save button - when enabled the changes in the dialog are saved and applied automatically without the need to press the Save button" "description": "VERY SHORT label for the checkbox in style config dialog after the save button - when enabled the changes in the dialog are saved and applied automatically without the need to press the Save button"
}, },
"configOnChangeTooltip": {
"message": "Otomatik kaydetme ve değişiklikleri otomatik olarak uygulama",
"description": ""
},
"configureStyle": {
"message": "Yapılandır",
"description": "Label for the button to configure usercss userstyle"
},
"configureStyleOnHomepage": {
"message": "Ana sayfada yapılandır",
"description": "Label for the button to configure userstyles.org userstyle"
},
"confirmCancel": { "confirmCancel": {
"message": "İptal", "message": "İptal",
"description": "" "description": ""
@ -180,6 +248,10 @@
"message": "Güncelleme tarihi", "message": "Güncelleme tarihi",
"description": "Option text for the user to sort the style by last update date" "description": "Option text for the user to sort the style by last update date"
}, },
"dbError": {
"message": "Stylus veritabanı kullanılırken bir hata oluştu. Olası çözümler içeren bir web sayfasını ziyaret etmek ister misiniz?",
"description": "Prompt when a DB error is encountered"
},
"defaultTheme": { "defaultTheme": {
"message": "öntanımlı", "message": "öntanımlı",
"description": "Default CodeMirror CSS theme option on the edit style page" "description": "Default CodeMirror CSS theme option on the edit style page"
@ -204,6 +276,14 @@
"message": "Devre dışı bırak", "message": "Devre dışı bırak",
"description": "Label for the button to disable a style" "description": "Label for the button to disable a style"
}, },
"dragDropMessage": {
"message": "İçe aktarmak için yedek dosyanızı bu sayfada herhangi bir yere bırakın.",
"description": "Drag'n'drop message"
},
"dragDropUsercssTabstrip": {
"message": "Dosyayı yüklemek için sekme şeridine (sekme başlıklarının gösterildiği alan) bırakın.",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": { "editDeleteText": {
"message": "Sil", "message": "Sil",
"description": "Label for the context menu item in the editor to delete selected text" "description": "Label for the context menu item in the editor to delete selected text"
@ -237,6 +317,14 @@
"message": "Etkinleştir", "message": "Etkinleştir",
"description": "Label for the button to enable a style" "description": "Label for the button to enable a style"
}, },
"excludeStyleByDomainLabel": {
"message": "Mevcut alan adını hariç tut",
"description": ""
},
"excludeStyleByUrlLabel": {
"message": "Mevcut URL'yi hariç tut",
"description": ""
},
"exportLabel": { "exportLabel": {
"message": "Dışa aktar", "message": "Dışa aktar",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
@ -249,10 +337,34 @@
"message": "Anasayfa", "message": "Anasayfa",
"description": "Label for the external link to style's homepage" "description": "Label for the external link to style's homepage"
}, },
"externalLink": {
"message": "Harici bağlantı",
"description": "Label for external links"
},
"externalSupport": { "externalSupport": {
"message": "Destek", "message": "Destek",
"description": "Label for the external link to style's support site" "description": "Label for the external link to style's support site"
}, },
"externalUsercssDocument": {
"message": "Usercss belgeleri",
"description": "Label for the external link to usercss documentation"
},
"filteredStyles": {
"message": "Toplam $numTotal$ gösterilen $numShown$",
"description": "TL note - make this message short",
"placeholders": {
"numTotal": {
"content": "$2"
},
"numShown": {
"content": "$1"
}
}
},
"filteredStylesAllHidden": {
"message": "Şu anda uygulanan filtreler hiçbir stille eşleşmiyor",
"description": "Text shown when no styles match currently applied filter in the style manager"
},
"findStyles": { "findStyles": {
"message": "Stil bul", "message": "Stil bul",
"description": "Text for a link that gets a list of styles for the current site" "description": "Text for a link that gets a list of styles for the current site"
@ -261,10 +373,22 @@
"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" "description": "Text for a link that gets a list of styles for the current site"
}, },
"findStylesInline": {
"message": "Hizada",
"description": "Text for a checkbox that opens search results 'inline' (within the Stylus popup window)"
},
"findStylesInlineTooltip": {
"message": "Arama sonuçlarını bu pencerede görüntüleyin.",
"description": "Text for a checkbox that displays search results within the Stylus popup."
},
"genericAdd": { "genericAdd": {
"message": "Ekle", "message": "Ekle",
"description": "Used in various places for an action that adds something" "description": "Used in various places for an action that adds something"
}, },
"genericClone": {
"message": "Klon",
"description": "Used in various places for an action that clones something"
},
"genericDisabledLabel": { "genericDisabledLabel": {
"message": "Devre dışı", "message": "Devre dışı",
"description": "Used in various lists/options to indicate that something is disabled" "description": "Used in various lists/options to indicate that something is disabled"
@ -289,6 +413,10 @@
"message": "Önceki", "message": "Önceki",
"description": "Used in various places to select/perform the previous step/action" "description": "Used in various places to select/perform the previous step/action"
}, },
"genericResetLabel": {
"message": "Yenile",
"description": "Used in various parts of UI to indicate that something may be reset to its original state"
},
"genericSavedMessage": { "genericSavedMessage": {
"message": "Kaydedildi", "message": "Kaydedildi",
"description": "Used in various parts of the UI to indicate that something was saved" "description": "Used in various parts of the UI to indicate that something was saved"
@ -305,10 +433,26 @@
"message": "Yardım", "message": "Yardım",
"description": "Alternate text for help buttons" "description": "Alternate text for help buttons"
}, },
"helpKeyMapCommand": {
"message": "Bir komut adı yazın",
"description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short"
},
"helpKeyMapHotkey": {
"message": "Bir kısayol tuşuna basın",
"description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short"
},
"hostDisabled": {
"message": "Bu ana bilgisayar, kullanılan tarayıcının mevcut sürümündeki bir hata nedeniyle devre dışı bırakıldı",
"description": "Tooltip for cloud host disabled"
},
"importAppendLabel": { "importAppendLabel": {
"message": "Stile ekle", "message": "Stile ekle",
"description": "Label for the button to import a style and append to the existing sections" "description": "Label for the button to import a style and append to the existing sections"
}, },
"importAppendTooltip": {
"message": "İçe aktarılan stili geçerli stile ekleme",
"description": "Tooltip for the button to import a style and append to the existing sections"
},
"importLabel": { "importLabel": {
"message": "İçe aktar", "message": "İçe aktar",
"description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)"
@ -317,10 +461,18 @@
"message": "Stilin üzerine yaz", "message": "Stilin üzerine yaz",
"description": "Label for the button to import and overwrite current style" "description": "Label for the button to import and overwrite current style"
}, },
"importReplaceTooltip": {
"message": "Geçerli stilin içeriğini atın ve içe aktarılan stil ile üzerine yazın",
"description": "Label for the button to import and overwrite current style"
},
"importReportLegendAdded": { "importReportLegendAdded": {
"message": "eklendi", "message": "eklendi",
"description": "Text after the number of styles added in the report shown after importing styles" "description": "Text after the number of styles added in the report shown after importing styles"
}, },
"importReportLegendUpdatedBoth": {
"message": "hem meta bilgi hem de kod güncellendi",
"description": "Text after the number of styles updated entirely in the report shown after importing styles"
},
"importReportLegendUpdatedCode": { "importReportLegendUpdatedCode": {
"message": "güncellenmiş kod", "message": "güncellenmiş kod",
"description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles" "description": "Text after the number of styles with updated code (meta info is unchanged) in the report shown after importing styles"
@ -640,6 +792,10 @@
"message": "Güncelleme kontrolü geçmişi", "message": "Güncelleme kontrolü geçmişi",
"description": "" "description": ""
}, },
"updateCheckSkippedMaybeLocallyEdited": {
"message": "Bu stil yerel olarak düzenlenmiş olabilir.",
"description": "Text that displays when an update check skipped updating the style to avoid losing possible local modifications"
},
"updateCheckSucceededNoUpdate": { "updateCheckSucceededNoUpdate": {
"message": "Stil güncel.", "message": "Stil güncel.",
"description": "Text that displays when an update check completed and no update is available" "description": "Text that displays when an update check completed and no update is available"
@ -652,6 +808,38 @@
"message": "Kurulan güncellemeler:", "message": "Kurulan güncellemeler:",
"description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates." "description": "Text that displays when an update is installed on options page. Followed by the number of currently installed updates."
}, },
"usercssAvoidOverwriting": {
"message": "Mevcut bir stilin üzerine yazmaktan kaçınmak için lütfen @name veya @ ad alanının değerini değiştirin.",
"description": "Shown in a message box when attempting to save a new Usercss style that would overwrite an existing one."
},
"usercssConfigIncomplete": {
"message": "Stil, yapılandırma iletişim kutusu gösterildikten sonra güncellendi veya silindi. Bu değişkenler, stilin meta verilerini bozmamak için kaydedilmedi:",
"description": ""
},
"usercssEditorNamePlaceholder": {
"message": "Kodda @name belirtin",
"description": "Placeholder text for the empty name input field when creating a new Usercss style"
},
"usercssReplaceTemplateConfirmation": {
"message": "Yeni Usercss stilleri için varsayılan şablonu geçerli kodla değiştir?",
"description": ""
},
"usercssReplaceTemplateName": {
"message": "Boş @name, varsayılan şablonun yerini alır",
"description": "The text shown after @name when creating a new Usercss style"
},
"usercssReplaceTemplateSectionBody": {
"message": "Kodu buraya ekle...",
"description": "The code placeholder comment in a new style created by clicking 'Write style' in the popup"
},
"versionInvalidOlder": {
"message": "Sürüm, kurulu stillerden daha eski.",
"description": "Displayed when the version of style is older than the installed one"
},
"writeStyleFor": {
"message": "Şunun için stil yaz:",
"description": "Label for toolbar pop-up that precedes the links to write a new style"
},
"writeStyleForURL": { "writeStyleForURL": {
"message": "bu URL", "message": "bu URL",
"description": "Text for link in toolbar pop-up to write a new style for the current URL" "description": "Text for link in toolbar pop-up to write a new style for the current URL"
@ -660,14 +848,34 @@
"message": "Dropbox'tan aktar", "message": "Dropbox'tan aktar",
"description": "" "description": ""
}, },
"syncDropboxDeprecated": {
"message": "Dropbox içe/dışa aktarma, seçenekler sayfasında daha gelişmiş bir stil senkronizasyonu ile değiştirilir.",
"description": ""
},
"retrieveDropboxSync": { "retrieveDropboxSync": {
"message": "Dropbox'a aktar", "message": "Dropbox'a aktar",
"description": "" "description": ""
}, },
"overwriteFileExport": {
"message": "Mevcut bir dosyanın üzerine yazmak istiyor musunuz?",
"description": ""
},
"exportSavedSuccess": { "exportSavedSuccess": {
"message": "Dosya başarıyla kaydedildi", "message": "Dosya başarıyla kaydedildi",
"description": "" "description": ""
}, },
"noFileToImport": {
"message": "Stillerinizi içe aktarmak için önce dışa aktarmanız gerekir.",
"description": ""
},
"connectingDropbox": {
"message": "Dropbox'a bağlanıyor...",
"description": ""
},
"connectingDropboxNotAllowed": {
"message": "Dropbox'a bağlanmak yalnızca doğrudan web mağazasından yüklenen uygulamalarda kullanılabilir",
"description": ""
},
"gettingStyles": { "gettingStyles": {
"message": "Tüm stiller alınıyor...", "message": "Tüm stiller alınıyor...",
"description": "" "description": ""
@ -676,6 +884,10 @@
"message": "Stiller sıkıştırılıyor...", "message": "Stiller sıkıştırılıyor...",
"description": "" "description": ""
}, },
"unzipStyles": {
"message": "Stiller açılıyor...",
"description": ""
},
"readingStyles": { "readingStyles": {
"message": "Stiller okunuyor...", "message": "Stiller okunuyor...",
"description": "" "description": ""

View File

@ -125,7 +125,7 @@
"description": "Label for the checkbox in the style editor." "description": "Label for the checkbox in the style editor."
}, },
"cm_autocompleteOnTyping": { "cm_autocompleteOnTyping": {
"message": "输入时自动完成", "message": "输入时自动补全",
"description": "Label for the checkbox in the style editor." "description": "Label for the checkbox in the style editor."
}, },
"cm_colorpicker": { "cm_colorpicker": {
@ -133,7 +133,7 @@
"description": "Label for the checkbox controlling colorpicker option for the style editor." "description": "Label for the checkbox controlling colorpicker option for the style editor."
}, },
"cm_indentWithTabs": { "cm_indentWithTabs": {
"message": "使用 Tab 智能缩进", "message": "使用Tab智能缩进",
"description": "Label for the checkbox controlling tabs with smart indentation option for the style editor." "description": "Label for the checkbox controlling tabs with smart indentation option for the style editor."
}, },
"cm_keyMap": { "cm_keyMap": {
@ -153,7 +153,7 @@
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text" "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text"
}, },
"cm_matchHighlightToken": { "cm_matchHighlightToken": {
"message": "光标所在 token 字段", "message": "光标所在token字段",
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of the word/token under cursor even if nothing is selected" "description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of the word/token under cursor even if nothing is selected"
}, },
"cm_resizeGripHint": { "cm_resizeGripHint": {
@ -244,6 +244,14 @@
"message": "是", "message": "是",
"description": "'Yes' button in a confirm dialog" "description": "'Yes' button in a confirm dialog"
}, },
"copied": {
"message": "已复制到粘贴板",
"description": "Message shown when content has been copied to the clipboard"
},
"copy": {
"message": "复制到粘贴板",
"description": "Tooltip for elements which can be copied"
},
"dateInstalled": { "dateInstalled": {
"message": "安装日期", "message": "安装日期",
"description": "Option text for the user to sort the style by install date" "description": "Option text for the user to sort the style by install date"
@ -284,6 +292,10 @@
"message": "把 JSON 备份文件拖放到页面的任意位置,即可导入", "message": "把 JSON 备份文件拖放到页面的任意位置,即可导入",
"description": "Drag'n'drop message" "description": "Drag'n'drop message"
}, },
"dragDropUsercssTabstrip": {
"message": "要安装文件,就将其放在选项卡条(显示选项卡标题的区域)上。",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": { "editDeleteText": {
"message": "删除", "message": "删除",
"description": "Label for the context menu item in the editor to delete selected text" "description": "Label for the context menu item in the editor to delete selected text"
@ -441,6 +453,10 @@
"message": "按下热键", "message": "按下热键",
"description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short" "description": "Placeholder text of inputbox in keymap help popup on the edit style page. Must be very short"
}, },
"hostDisabled": {
"message": "由于当前使用的浏览器版本中存在的错误,此主机已被禁用",
"description": "Tooltip for cloud host disabled"
},
"importAppendLabel": { "importAppendLabel": {
"message": "追加到样式", "message": "追加到样式",
"description": "Label for the button to import a style and append to the existing sections" "description": "Label for the button to import a style and append to the existing sections"
@ -609,6 +625,14 @@
"message": "查看文件时发生错误", "message": "查看文件时发生错误",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "保持此选项卡为打开状态,将在外部更改后自动更新样式。",
"description": "The label of live-reload feature"
},
"liveReloadInstallHintFF": {
"message": "保持此选项卡和原始选项卡处于打开状态,将在外部更改时自动重新更新样式。",
"description": "The extra hint of live-reload feature shown only for file:// URLs in Firefox"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "动态刷新", "message": "动态刷新",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
@ -642,7 +666,7 @@
"description": "VERY SHORT label for the checkbox next to the 'Write new style' button in the style manager" "description": "VERY SHORT label for the checkbox next to the 'Write new style' button in the style manager"
}, },
"manageNewUI": { "manageNewUI": {
"message": "新的样式管理界面", "message": "新版管理器",
"description": "Label for the checkbox that toggles the new UI on manage page" "description": "Label for the checkbox that toggles the new UI on manage page"
}, },
"manageOnlyDisabled": { "manageOnlyDisabled": {
@ -905,6 +929,10 @@
"message": "管理样式", "message": "管理样式",
"description": "Link to open the manage page." "description": "Link to open the manage page."
}, },
"openOptions": {
"message": "选项",
"description": "Go to Options UI"
},
"openStylesManager": { "openStylesManager": {
"message": "打开样式管理器", "message": "打开样式管理器",
"description": "Label for the style maanger opener in the browser action context menu." "description": "Label for the style maanger opener in the browser action context menu."
@ -965,6 +993,10 @@
"message": "检测更新", "message": "检测更新",
"description": "" "description": ""
}, },
"optionsCustomizeSync": {
"message": "同步到云端",
"description": ""
},
"optionsHeading": { "optionsHeading": {
"message": "选项", "message": "选项",
"description": "Heading for options section on manage page." "description": "Heading for options section on manage page."
@ -1009,6 +1041,70 @@
"message": "每隔 N 小时检查所有样式更新0 为关闭检查)", "message": "每隔 N 小时检查所有样式更新0 为关闭检查)",
"description": "" "description": ""
}, },
"optionsSyncNone": {
"message": "无",
"description": ""
},
"optionsSyncConnect": {
"message": "连接",
"description": ""
},
"optionsSyncDisconnect": {
"message": "断开连接",
"description": ""
},
"optionsSyncSyncNow": {
"message": "现在同步",
"description": ""
},
"optionsSyncLogin": {
"message": "登录",
"description": ""
},
"optionsSyncStatusPull": {
"message": "正在获取样式 $loaded$ 中的 $total$",
"description": "",
"placeholders": {
"loaded": {
"content": "$1"
},
"total": {
"content": "$2"
}
}
},
"optionsSyncStatusPush": {
"message": "正在上传样式 $loaded$ 中的 $total$",
"description": "",
"placeholders": {
"loaded": {
"content": "$1"
},
"total": {
"content": "$2"
}
}
},
"optionsSyncStatusSyncing": {
"message": "同步中...",
"description": ""
},
"optionsSyncStatusConnecting": {
"message": "连接中...",
"description": ""
},
"optionsSyncStatusConnected": {
"message": "已连接",
"description": ""
},
"optionsSyncStatusDisconnecting": {
"message": "正在断开连接...",
"description": ""
},
"optionsSyncStatusDisconnected": {
"message": "已断开连接",
"description": ""
},
"paginationCurrent": { "paginationCurrent": {
"message": "当前页数", "message": "当前页数",
"description": "Tooltip for the current page index in search results" "description": "Tooltip for the current page index in search results"
@ -1085,6 +1181,10 @@
"message": "无需保存即可临时应用样式预览效果。保存以使更改永久生效。", "message": "无需保存即可临时应用样式预览效果。保存以使更改永久生效。",
"description": "Tooltip for the checkbox in style editor to enable live preview while editing." "description": "Tooltip for the checkbox in style editor to enable live preview while editing."
}, },
"reload": {
"message": "重新加载 Stylus 扩展程序",
"description": "Context menu reload"
},
"replace": { "replace": {
"message": "替换", "message": "替换",
"description": "Label before the replace input field in the editor shown on Ctrl-H" "description": "Label before the replace input field in the editor shown on Ctrl-H"
@ -1209,6 +1309,10 @@
"message": "格式化", "message": "格式化",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "提示: 你可以不显示此面板, 而是用右键“美化”按钮 或用下面定义的快捷键来美化.",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "缩进 @media、@supports", "message": "缩进 @media、@supports",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"
@ -1218,7 +1322,7 @@
"description": "CSS-beautifier option" "description": "CSS-beautifier option"
}, },
"styleCancelEditLabel": { "styleCancelEditLabel": {
"message": "返回管理页面", "message": "返回管理",
"description": "Label for cancel button for style editing" "description": "Label for cancel button for style editing"
}, },
"styleChangesNotSaved": { "styleChangesNotSaved": {
@ -1492,6 +1596,10 @@
"message": "导出至 Dropbox", "message": "导出至 Dropbox",
"description": "" "description": ""
}, },
"syncDropboxDeprecated": {
"message": "在选项页面中, Dropbox 导入/导出 已经替换为更高级的样式同步方式。",
"description": ""
},
"retrieveDropboxSync": { "retrieveDropboxSync": {
"message": "从 Dropbox 导入", "message": "从 Dropbox 导入",
"description": "" "description": ""

View File

@ -1309,6 +1309,10 @@
"message": "美化", "message": "美化",
"description": "Label for the CSS-beautifier button on the edit style page" "description": "Label for the CSS-beautifier button on the edit style page"
}, },
"styleBeautifyHint": {
"message": "提示:右鍵點選「美化」按鈕或使用下面定義的鍵盤快捷鍵來美化而不顯示此面板",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": { "styleBeautifyIndentConditional": {
"message": "縮排 @media, @supports", "message": "縮排 @media, @supports",
"description": "CSS-beautifier option" "description": "CSS-beautifier option"

View File

@ -1,7 +1,7 @@
/* global download prefs openURL FIREFOX CHROME /* global download prefs openURL FIREFOX CHROME
URLS ignoreChromeError usercssHelper URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil workerUtil contentScripts sync styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab findExistingTab activateTab isTabReplaceable getActiveTab
tabManager colorScheme */ tabManager colorScheme */
'use strict'; 'use strict';
@ -10,6 +10,20 @@ var backgroundWorker = workerUtil.createWorker({
url: '/background/background-worker.js' url: '/background/background-worker.js'
}); });
// eslint-disable-next-line no-var
var browserCommands, contextMenus;
// *************************************************************************
// browser commands
browserCommands = {
openManage,
openOptions: () => openManage({options: true}),
styleDisableAll(info) {
prefs.set('disableAll', info ? info.checked : !prefs.get('disableAll'));
},
reload: () => chrome.runtime.reload(),
};
window.API_METHODS = Object.assign(window.API_METHODS || {}, { window.API_METHODS = Object.assign(window.API_METHODS || {}, {
deleteStyle: styleManager.deleteStyle, deleteStyle: styleManager.deleteStyle,
editSave: styleManager.editSave, editSave: styleManager.editSave,
@ -70,8 +84,8 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
}, },
optionsCustomizeHotkeys() { optionsCustomizeHotkeys() {
return browser.runtime.openOptionsPage() return browserCommands.openOptions()
.then(() => new Promise(resolve => setTimeout(resolve, 100))) .then(() => new Promise(resolve => setTimeout(resolve, 500)))
.then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'})); .then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'}));
}, },
@ -86,9 +100,6 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
openManage openManage
}); });
// eslint-disable-next-line no-var
var browserCommands, contextMenus;
// ************************************************************************* // *************************************************************************
// register all listeners // register all listeners
msg.on(onRuntimeMessage); msg.on(onRuntimeMessage);
@ -148,17 +159,6 @@ chrome.runtime.onInstalled.addListener(({reason}) => {
delete localStorage.codeMirrorThemes; delete localStorage.codeMirrorThemes;
}); });
// *************************************************************************
// browser commands
browserCommands = {
openManage,
openOptions: () => openManage({options: true}),
styleDisableAll(info) {
prefs.set('disableAll', info ? info.checked : !prefs.get('disableAll'));
},
reload: () => chrome.runtime.reload(),
};
// ************************************************************************* // *************************************************************************
// context menus // context menus
contextMenus = { contextMenus = {
@ -218,7 +218,7 @@ function createContextMenus(ids) {
if (chrome.contextMenus) { if (chrome.contextMenus) {
// circumvent the bug with disabling check marks in Chrome 62-64 // circumvent the bug with disabling check marks in Chrome 62-64
const toggleCheckmark = CHROME >= 3172 && CHROME <= 3288 ? const toggleCheckmark = CHROME >= 62 && CHROME <= 64 ?
(id => chrome.contextMenus.remove(id, () => createContextMenus([id]) + ignoreChromeError())) : (id => chrome.contextMenus.remove(id, () => createContextMenus([id]) + ignoreChromeError())) :
((id, checked) => chrome.contextMenus.update(id, {checked}, ignoreChromeError)); ((id, checked) => chrome.contextMenus.update(id, {checked}, ignoreChromeError));
@ -337,7 +337,7 @@ function openManage({options = false, search} = {}) {
if (isTabReplaceable(tab, url)) { if (isTabReplaceable(tab, url)) {
return activateTab(tab, {url}); return activateTab(tab, {url});
} }
return createTab({url}); return browser.tabs.create({url});
}); });
}); });
} }

View File

@ -1,4 +1,4 @@
/* global msg queryTabs ignoreChromeError URLS */ /* global msg ignoreChromeError URLS */
/* exported contentScripts */ /* exported contentScripts */
'use strict'; 'use strict';
@ -55,7 +55,7 @@ const contentScripts = (() => {
} }
function injectToAllTabs() { function injectToAllTabs() {
return queryTabs({}).then(tabs => { return browser.tabs.query({}).then(tabs => {
for (const tab of tabs) { for (const tab of tabs) {
// skip unloaded/discarded/chrome tabs // skip unloaded/discarded/chrome tabs
if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue; if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue;

View File

@ -1,46 +1,29 @@
/* global promisify */ /* global chromeLocal */
/* exported createChromeStorageDB */ /* exported createChromeStorageDB */
'use strict'; 'use strict';
function createChromeStorageDB() { function createChromeStorageDB() {
const get = promisify(chrome.storage.local.get.bind(chrome.storage.local));
const set = promisify(chrome.storage.local.set.bind(chrome.storage.local));
const remove = promisify(chrome.storage.local.remove.bind(chrome.storage.local));
let INC; let INC;
const PREFIX = 'style-'; const PREFIX = 'style-';
const METHODS = { const METHODS = {
// FIXME: we don't use this method at all. Should we remove this? // FIXME: we don't use this method at all. Should we remove this?
get: id => get(PREFIX + id) get: id => chromeLocal.getValue(PREFIX + id),
.then(result => result[PREFIX + id]), put: obj =>
put: obj => Promise.resolve() // FIXME: should we clone the object?
.then(() => { Promise.resolve(!obj.id && prepareInc().then(() => Object.assign(obj, {id: INC++})))
if (!obj.id) { .then(() => chromeLocal.setValue(PREFIX + obj.id, obj))
return prepareInc() .then(() => obj.id),
.then(() => {
// FIXME: should we clone the object?
obj.id = INC++;
});
}
})
.then(() => set({[PREFIX + obj.id]: obj}))
.then(() => obj.id),
putMany: items => prepareInc() putMany: items => prepareInc()
.then(() => { .then(() =>
for (const item of items) { chromeLocal.set(items.reduce((data, item) => {
if (!item.id) { if (!item.id) item.id = INC++;
item.id = INC++; data[PREFIX + item.id] = item;
} return data;
} }, {})))
return set(items.reduce((obj, curr) => {
obj[PREFIX + curr.id] = curr;
return obj;
}, {}));
})
.then(() => items.map(i => i.id)), .then(() => items.map(i => i.id)),
delete: id => remove(PREFIX + id), delete: id => chromeLocal.remove(PREFIX + id),
getAll: () => get(null) getAll: () => chromeLocal.get()
.then(result => { .then(result => {
const output = []; const output = [];
for (const key in result) { for (const key in result) {
@ -69,7 +52,7 @@ function createChromeStorageDB() {
function prepareInc() { function prepareInc() {
if (INC) return Promise.resolve(); if (INC) return Promise.resolve();
return get(null).then(result => { return chromeLocal.get().then(result => {
INC = 1; INC = 1;
for (const key in result) { for (const key in result) {
if (key.startsWith(PREFIX)) { if (key.startsWith(PREFIX)) {

View File

@ -3,7 +3,7 @@
'use strict'; 'use strict';
const iconManager = (() => { const iconManager = (() => {
const ICON_SIZES = FIREFOX || CHROME >= 2883 && !VIVALDI ? [16, 32] : [19, 38]; const ICON_SIZES = FIREFOX || CHROME >= 55 && !VIVALDI ? [16, 32] : [19, 38];
const staleBadges = new Set(); const staleBadges = new Set();
prefs.subscribe([ prefs.subscribe([

View File

@ -1,4 +1,4 @@
/* global promisify CHROME URLS */ /* global CHROME URLS */
/* exported navigatorUtil */ /* exported navigatorUtil */
'use strict'; 'use strict';
@ -6,7 +6,6 @@ const navigatorUtil = (() => {
const handler = { const handler = {
urlChange: null urlChange: null
}; };
const tabGet = promisify(chrome.tabs.get.bind(chrome.tabs));
return extendNative({onUrlChange}); return extendNative({onUrlChange});
function onUrlChange(fn) { function onUrlChange(fn) {
@ -48,7 +47,7 @@ const navigatorUtil = (() => {
) { ) {
return Promise.resolve(); return Promise.resolve();
} }
return tabGet(data.tabId) return browser.tabs.get(data.tabId)
.then(tab => { .then(tab => {
if (tab.url === 'chrome://newtab/') { if (tab.url === 'chrome://newtab/') {
data.url = tab.url; data.url = tab.url;

View File

@ -192,6 +192,7 @@ const sync = (() => {
.catch(handle401Error) .catch(handle401Error)
.then(() => syncNow()), .then(() => syncNow()),
err => { err => {
status.errorMessage = err ? err.message : null;
// FIXME: should we move this logic to options.js? // FIXME: should we move this logic to options.js?
if (err && !fromPref) { if (err && !fromPref) {
console.error(err); console.error(err);

View File

@ -1,9 +1,11 @@
/* global chromeLocal promisify FIREFOX */ /* global chromeLocal promisifyChrome FIREFOX */
/* exported tokenManager */ /* exported tokenManager */
'use strict'; 'use strict';
const tokenManager = (() => { const tokenManager = (() => {
const launchWebAuthFlow = promisify(chrome.identity.launchWebAuthFlow.bind(chrome.identity)); promisifyChrome({
identity: ['launchWebAuthFlow'],
});
const AUTH = { const AUTH = {
dropbox: { dropbox: {
flow: 'token', flow: 'token',
@ -158,7 +160,7 @@ const tokenManager = (() => {
Object.assign(query, provider.authQuery); Object.assign(query, provider.authQuery);
} }
const url = `${provider.authURL}?${stringifyQuery(query)}`; const url = `${provider.authURL}?${stringifyQuery(query)}`;
return launchWebAuthFlow({ return browser.identity.launchWebAuthFlow({
url, url,
interactive interactive
}) })

View File

@ -1,8 +1,7 @@
/* global API_METHODS usercss styleManager deepCopy openURL download URLS getTab */ /* global API_METHODS usercss styleManager deepCopy openURL download URLS */
/* exports usercssHelper */ /* exported usercssHelper */
'use strict'; 'use strict';
// eslint-disable-next-line no-unused-vars
const usercssHelper = (() => { const usercssHelper = (() => {
const installCodeCache = {}; const installCodeCache = {};
const clearInstallCode = url => delete installCodeCache[url]; const clearInstallCode = url => delete installCodeCache[url];
@ -46,7 +45,7 @@ const usercssHelper = (() => {
openInstallerPage(tabId, url, {code, inTab} = {}) { openInstallerPage(tabId, url, {code, inTab} = {}) {
const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`; const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
if (inTab) { if (inTab) {
getTab(tabId).then(tab => browser.tabs.get(tabId).then(tab =>
openURL({ openURL({
url: `${newUrl}&tabId=${tabId}`, url: `${newUrl}&tabId=${tabId}`,
active: tab.active, active: tab.active,

View File

@ -19,90 +19,91 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
let isTransitionPatched; let isTransitionPatched;
// will store the original method refs because the page can override them // will store the original method refs because the page can override them
let creationDoc, createElement, createElementNS; let creationDoc, createElement, createElementNS;
return { return {
apply,
clear,
clearOrphans,
remove,
replace,
toggle,
list, list,
apply(styleMap) {
const styles = _styleMapToArray(styleMap);
return (
!styles.length ?
Promise.resolve([]) :
docRootObserver.evade(() => {
if (!isTransitionPatched && isEnabled) {
_applyTransitionPatch(styles);
}
return styles.map(_addUpdate);
})
).then(_emitUpdate);
},
clear() {
_addRemoveElements(false);
list.length = 0;
table.clear();
_emitUpdate();
},
clearOrphans() {
for (const el of document.querySelectorAll(`style[id^="${PREFIX}"].stylus`)) {
const id = el.id.slice(PREFIX.length);
if (/^\d+$/.test(id) || id === PATCH_ID) {
el.remove();
}
}
},
remove(id) {
_remove(id);
_emitUpdate();
},
replace(styleMap) {
const styles = _styleMapToArray(styleMap);
const added = new Set(styles.map(s => s.id));
const removed = [];
for (const style of list) {
if (!added.has(style.id)) {
removed.push(style.id);
}
}
styles.forEach(_addUpdate);
removed.forEach(_remove);
_emitUpdate();
},
toggle(enable) {
if (isEnabled === enable) return;
isEnabled = enable;
if (!enable) _toggleObservers(false);
_addRemoveElements(enable);
if (enable) _toggleObservers(true);
},
}; };
function apply(styleMap) { function _add(style) {
const styles = _styleMapToArray(styleMap); const el = style.el = _createStyle(style.id, style.code);
return !styles.length ? const i = list.findIndex(item => compare(item, style) > 0);
Promise.resolve([]) : table.set(style.id, style);
docRootObserver.evade(() => { if (isEnabled) {
if (!isTransitionPatched) _applyTransitionPatch(styles); document.documentElement.insertBefore(el, i < 0 ? null : list[i].el);
const els = styles.map(_apply);
_emitUpdate();
return els;
});
}
function clear() {
for (const style of list) {
style.el.remove();
} }
list.length = 0; list.splice(i < 0 ? list.length : i, 0, style);
table.clear(); return el;
_emitUpdate();
} }
function clearOrphans() { function _addRemoveElements(add) {
for (const el of document.querySelectorAll(`style[id^="${PREFIX}"].stylus`)) { for (const {el} of list) {
const id = el.id.slice(PREFIX.length); if (add) {
if (/^\d+$/.test(id) || id === PATCH_ID) { document.documentElement.appendChild(el);
} else {
el.remove(); el.remove();
} }
} }
} }
function remove(id) { function _addUpdate(style) {
_remove(id);
_emitUpdate();
}
function replace(styleMap) {
const styles = _styleMapToArray(styleMap);
const added = new Set(styles.map(s => s.id));
const removed = [];
for (const style of list) {
if (!added.has(style.id)) {
removed.push(style.id);
}
}
styles.forEach(_apply);
removed.forEach(_remove);
_emitUpdate();
}
function toggle(_enabled) {
if (isEnabled === _enabled) return;
isEnabled = _enabled;
for (const style of list) {
style.el.disabled = !isEnabled;
}
}
function _add(style) {
const el = style.el = _createStyle(style.id, style.code);
table.set(style.id, style);
const nextIndex = list.findIndex(i => compare(i, style) > 0);
if (nextIndex < 0) {
document.documentElement.appendChild(el);
list.push(style);
} else {
document.documentElement.insertBefore(el, list[nextIndex].el);
list.splice(nextIndex, 0, style);
}
// moving an element resets its 'disabled' state
el.disabled = !isEnabled;
return el;
}
function _apply(style) {
return table.has(style.id) ? _update(style) : _add(style); return table.has(style.id) ? _update(style) : _add(style);
} }
@ -151,15 +152,16 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
return el; return el;
} }
function _emitUpdate() { function _toggleObservers(shouldStart) {
if (!IS_OWN_PAGE && list.length) { const onOff = shouldStart && isEnabled ? 'start' : 'stop';
docRewriteObserver.start(); docRewriteObserver[onOff]();
docRootObserver.start(); docRootObserver[onOff]();
} else { }
docRewriteObserver.stop();
docRootObserver.stop(); function _emitUpdate(value) {
} _toggleObservers(!IS_OWN_PAGE && list.length);
onUpdate(); onUpdate();
return value;
} }
/* /*
@ -193,11 +195,7 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
function _sort() { function _sort() {
docRootObserver.evade(() => { docRootObserver.evade(() => {
list.sort(compare); list.sort(compare);
for (const style of list) { _addRemoveElements(true);
// moving an element resets its 'disabled' state
document.documentElement.appendChild(style.el);
style.el.disabled = !isEnabled;
}
}); });
} }
@ -241,13 +239,13 @@ self.createStyleInjector = self.INJECTED === 1 ? self.createStyleInjector : ({
if (isChromePre65) { if (isChromePre65) {
const oldEl = style.el; const oldEl = style.el;
style.el = _createStyle(id, code); style.el = _createStyle(id, code);
oldEl.parentNode.insertBefore(style.el, oldEl.nextSibling); if (isEnabled) {
oldEl.remove(); oldEl.parentNode.insertBefore(style.el, oldEl.nextSibling);
oldEl.remove();
}
} else { } else {
style.el.textContent = code; style.el.textContent = code;
} }
// https://github.com/openstyles/stylus/issues/693
style.el.disabled = !isEnabled;
} }
function RewriteObserver(onChange) { function RewriteObserver(onChange) {

View File

@ -64,7 +64,6 @@
<script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script> <script src="vendor-overwrites/codemirror-addon/match-highlighter.js"></script>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -1,8 +1,49 @@
/* global loadScript css_beautify showHelp prefs t $ $create */ /* global loadScript css_beautify showHelp prefs t $ $create */
/* exported beautify */ /* global editor createHotkeyInput moveFocus CodeMirror */
/* exported initBeautifyButton */
'use strict'; 'use strict';
function beautify(scope) { const HOTKEY_ID = 'editor.beautify.hotkey';
prefs.initializing.then(() => {
CodeMirror.defaults.extraKeys[prefs.get(HOTKEY_ID) || ''] = 'beautify';
CodeMirror.commands.beautify = cm => {
// using per-section mode when code editor or applies-to block is focused
const isPerSection = cm.display.wrapper.parentElement.contains(document.activeElement);
beautify(isPerSection ? [cm] : editor.getEditors(), false);
};
});
prefs.subscribe([HOTKEY_ID], (key, value) => {
const {extraKeys} = CodeMirror.defaults;
for (const [key, cmd] of Object.entries(extraKeys)) {
if (cmd === 'beautify') {
delete extraKeys[key];
break;
}
}
if (value) {
extraKeys[value] = 'beautify';
}
});
/**
* @param {HTMLElement} btn - the button element shown in the UI
* @param {function():CodeMirror[]} getScope
*/
function initBeautifyButton(btn, getScope) {
btn.addEventListener('click', () => beautify(getScope()));
btn.addEventListener('contextmenu', e => {
e.preventDefault();
beautify(getScope(), false);
});
}
/**
* @param {CodeMirror[]} scope
* @param {?boolean} ui
*/
function beautify(scope, ui = true) {
loadScript('/vendor-overwrites/beautify/beautify-css-mod.js') loadScript('/vendor-overwrites/beautify/beautify-css-mod.js')
.then(() => { .then(() => {
if (!window.css_beautify && window.exports) { if (!window.css_beautify && window.exports) {
@ -19,7 +60,41 @@ function beautify(scope) {
} }
options.indent_size = tabs ? 1 : prefs.get('editor.tabSize'); options.indent_size = tabs ? 1 : prefs.get('editor.tabSize');
options.indent_char = tabs ? '\t' : ' '; options.indent_char = tabs ? '\t' : ' ';
if (ui) {
createBeautifyUI(scope, options);
}
for (const cm of scope) {
setTimeout(doBeautifyEditor, 0, cm, options);
}
}
function doBeautifyEditor(cm, options) {
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 = 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]});
}
const {scrollX, scrollY} = window;
cm.setSelections(selections);
window.scrollTo(scrollX, scrollY);
cm.beautifyChange[cm.changeGeneration()] = true;
if (ui) {
$('#help-popup button[role="close"]').disabled = false;
}
}
}
function createBeautifyUI(scope, options) {
showHelp(t('styleBeautify'), showHelp(t('styleBeautify'),
$create([ $create([
$create('.beautify-options', [ $create('.beautify-options', [
@ -32,6 +107,10 @@ function beautify(scope) {
$createLabeledCheckbox('preserve_newlines', 'styleBeautifyPreserveNewlines'), $createLabeledCheckbox('preserve_newlines', 'styleBeautifyPreserveNewlines'),
$createLabeledCheckbox('indent_conditional', 'styleBeautifyIndentConditional'), $createLabeledCheckbox('indent_conditional', 'styleBeautifyIndentConditional'),
]), ]),
$create('p.beautify-hint', [
$create('span', t('styleBeautifyHint') + '\u00A0'),
createHotkeyInput(HOTKEY_ID, () => moveFocus($('#help-popup'), 1)),
]),
$create('.buttons', [ $create('.buttons', [
$create('button', { $create('button', {
attributes: {role: 'close'}, attributes: {role: 'close'},
@ -60,32 +139,6 @@ function beautify(scope) {
$('#help-popup').className = 'wide'; $('#help-popup').className = 'wide';
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 = 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]});
}
const {scrollX, scrollY} = window;
cm.setSelections(selections);
window.scrollTo(scrollX, scrollY);
cm.beautifyChange[cm.changeGeneration()] = true;
$('#help-popup button[role="close"]').disabled = false;
}
});
});
$('.beautify-options').onchange = ({target}) => { $('.beautify-options').onchange = ({target}) => {
const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0; const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0;
prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value})); prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value}));

View File

@ -18,6 +18,12 @@
outline: -webkit-focus-ring-color auto 5px; outline: -webkit-focus-ring-color auto 5px;
outline-offset: -2px; outline-offset: -2px;
} }
.CodeMirror-bookmark {
background: linear-gradient(to right, currentColor, transparent);
position: absolute;
width: 2em;
opacity: .5;
}
@supports (-moz-appearance:none) { @supports (-moz-appearance:none) {
/* restrict to FF */ /* restrict to FF */
.CodeMirror-focused { .CodeMirror-focused {

View File

@ -8,6 +8,8 @@
prefs.reset('editor.keyMap'); prefs.reset('editor.keyMap');
} }
const CM_BOOKMARK = 'CodeMirror-bookmark';
const CM_BOOKMARK_GUTTER = CM_BOOKMARK + 'gutter';
const defaults = { const defaults = {
autoCloseBrackets: prefs.get('editor.autoCloseBrackets'), autoCloseBrackets: prefs.get('editor.autoCloseBrackets'),
mode: 'css', mode: 'css',
@ -15,6 +17,7 @@
lineWrapping: prefs.get('editor.lineWrapping'), lineWrapping: prefs.get('editor.lineWrapping'),
foldGutter: true, foldGutter: true,
gutters: [ gutters: [
CM_BOOKMARK_GUTTER,
'CodeMirror-linenumbers', 'CodeMirror-linenumbers',
'CodeMirror-foldgutter', 'CodeMirror-foldgutter',
...(prefs.get('editor.linter') ? ['CodeMirror-lint-markers'] : []), ...(prefs.get('editor.linter') ? ['CodeMirror-lint-markers'] : []),
@ -102,92 +105,19 @@
} }
Object.assign(CodeMirror.mimeModes['text/css'].propertyKeywords, { Object.assign(CodeMirror.mimeModes['text/css'].propertyKeywords, {
// CSS Backgrounds and Borders Module L4
'background-position-x': true, 'background-position-x': true,
'background-position-y': true, 'background-position-y': true,
// CSS Logical Properties and Values L1
'block-size': true,
'border-block-color': true,
'border-block-end': true,
'border-block-end-color': true,
'border-block-end-style': true,
'border-block-end-width': true,
'border-block-start': true,
'border-block-start-color': true,
'border-block-start-style': true,
'border-block-start-width': true,
'border-block-style': true,
'border-block-width': true,
'border-inline-color': true,
'border-inline-end': true,
'border-inline-end-color': true,
'border-inline-end-style': true,
'border-inline-end-width': true,
'border-inline-start': true,
'border-inline-start-color': true,
'border-inline-start-style': true,
'border-inline-start-width': true,
'border-inline-style': true,
'border-inline-width': true,
'inline-size': true,
'inset': true,
'inset-block': true,
'inset-block-end': true,
'inset-block-start': true,
'inset-inline': true,
'inset-inline-end': true,
'inset-inline-start': true,
'margin-block': true,
'margin-block-end': true,
'margin-block-start': true,
'margin-inline': true,
'margin-inline-end': true,
'margin-inline-start': true,
'max-block-size': true,
'max-inline-size': true,
'min-block-size': true,
'min-inline-size': true,
'padding-block': true,
'padding-block-end': true,
'padding-block-start': true,
'padding-inline': true,
'padding-inline-end': true,
'padding-inline-start': true,
'text-align-all': true,
'contain': true, 'contain': true,
'mask-image': true, 'mask-image': true,
'mix-blend-mode': true, 'mix-blend-mode': true,
'overscroll-behavior': true,
'rotate': true, 'rotate': true,
'isolation': true, 'isolation': true,
'zoom': true,
// https://www.w3.org/TR/css-round-display-1/
'border-boundary': true,
'shape': true,
'shape-inside': true,
'viewport-fit': true,
// nonstandard https://compat.spec.whatwg.org/
'box-reflect': true,
'text-fill-color': true,
'text-stroke': true,
'text-stroke-color': true,
'text-stroke-width': true,
// end
});
Object.assign(CodeMirror.mimeModes['text/css'].valueKeywords, {
'isolate': true,
'rect': true,
'recto': true,
'verso': true,
}); });
Object.assign(CodeMirror.mimeModes['text/css'].colorKeywords, { Object.assign(CodeMirror.mimeModes['text/css'].colorKeywords, {
'darkgrey': true, 'darkgrey': true,
'darkslategrey': true, 'darkslategrey': true,
'dimgrey': true, 'dimgrey': true,
'grey': true,
'lightgrey': true, 'lightgrey': true,
'lightslategrey': true, 'lightslategrey': true,
'slategrey': true, 'slategrey': true,
@ -242,22 +172,27 @@
CodeMirror.commands[name] = (...args) => editor[name](...args); CodeMirror.commands[name] = (...args) => editor[name](...args);
} }
// speedup: reuse the old folding marks const elBookmark = document.createElement('div');
// TODO: remove when https://github.com/codemirror/CodeMirror/pull/6010 is shipped in /vendor elBookmark.className = CM_BOOKMARK;
const {setGutterMarker} = CodeMirror.prototype; elBookmark.textContent = '\u00A0';
CodeMirror.prototype.setGutterMarker = function (line, gutterID, value) { const clearMarker = function () {
const o = this.state.foldGutter.options; const line = this.lines[0];
if (typeof o.indicatorOpen === 'string' || CodeMirror.TextMarker.prototype.clear.apply(this);
typeof o.indicatorFolded === 'string') { if (!line.markedSpans.some(span => span.marker.sublimeBookmark)) {
const old = line.gutterMarkers && line.gutterMarkers[gutterID]; this.doc.setGutterMarker(line, CM_BOOKMARK_GUTTER, null);
// old className can contain other names set by CodeMirror so we'll use classList
if (old && value && old.classList.contains(value.className) ||
!old && !value) {
return line;
}
} }
return setGutterMarker.apply(this, arguments);
}; };
const {markText} = CodeMirror.prototype;
Object.assign(CodeMirror.prototype, {
markText() {
const marker = markText.apply(this, arguments);
if (marker.sublimeBookmark) {
this.doc.setGutterMarker(marker.lines[0], CM_BOOKMARK_GUTTER, elBookmark.cloneNode(true));
marker.clear = clearMarker;
}
return marker;
},
});
// CodeMirror convenience commands // CodeMirror convenience commands
Object.assign(CodeMirror.commands, { Object.assign(CodeMirror.commands, {
@ -307,6 +242,7 @@ CodeMirror.hint && (() => {
const RX_IMPORTANT = /(i(m(p(o(r(t(a(nt?)?)?)?)?)?)?)?)?(?=\b|\W|$)/iy; const RX_IMPORTANT = /(i(m(p(o(r(t(a(nt?)?)?)?)?)?)?)?)?(?=\b|\W|$)/iy;
const RX_VAR_KEYWORD = /(^|[^-\w\u0080-\uFFFF])var\(/iy; const RX_VAR_KEYWORD = /(^|[^-\w\u0080-\uFFFF])var\(/iy;
const RX_END_OF_VAR = /[\s,)]|$/g; const RX_END_OF_VAR = /[\s,)]|$/g;
const RX_CONSUME_PROP = /[-\w]*\s*:\s?|$/y;
const originalHelper = CodeMirror.hint.css || (() => {}); const originalHelper = CodeMirror.hint.css || (() => {});
const helper = cm => { const helper = cm => {
@ -372,7 +308,15 @@ CodeMirror.hint && (() => {
} }
if (!editor || !style || !style.includes(USO_VAR)) { if (!editor || !style || !style.includes(USO_VAR)) {
return originalHelper(cm); // add ":" after a property name
const res = originalHelper(cm);
const state = res && cm.getTokenAt(pos).state.state;
if (state === 'block' || state === 'maybeprop') {
res.list = res.list.map(str => str + ': ');
RX_CONSUME_PROP.lastIndex = res.to.ch;
res.to.ch += RX_CONSUME_PROP.exec(text)[0].length;
}
return res;
} }
// USO vars in usercss mode editor // USO vars in usercss mode editor

View File

@ -1,4 +1,4 @@
/* global CodeMirror showHelp cmFactory onDOMready $ $create prefs t */ /* global CodeMirror showHelp cmFactory onDOMready $ prefs t createHotkeyInput */
'use strict'; 'use strict';
(() => { (() => {
@ -62,46 +62,8 @@
function configureColorpicker(event) { function configureColorpicker(event) {
event.preventDefault(); event.preventDefault();
const input = $create('input', { const input = createHotkeyInput('editor.colorpicker.hotkey', () => {
type: 'search', $('#help-popup .dismiss').onclick();
spellcheck: false,
value: prefs.get('editor.colorpicker.hotkey'),
onkeydown(event) {
event.preventDefault();
event.stopPropagation();
const key = CodeMirror.keyName(event);
switch (key) {
case 'Enter':
if (this.checkValidity()) {
$('#help-popup .dismiss').onclick();
}
return;
case 'Esc':
$('#help-popup .dismiss').onclick();
return;
default:
// disallow: [Shift?] characters, modifiers-only, [modifiers?] + Esc, Tab, nav keys
if (!key || new RegExp('^(' + [
'(Back)?Space',
'(Shift-)?.', // a single character
'(Shift-?|Ctrl-?|Alt-?|Cmd-?){0,2}(|Esc|Tab|(Page)?(Up|Down)|Left|Right|Home|End|Insert|Delete)',
].join('|') + ')$', 'i').test(key)) {
this.value = key || this.value;
this.setCustomValidity('Not allowed');
return;
}
}
this.value = key;
this.setCustomValidity('');
prefs.set('editor.colorpicker.hotkey', key);
},
oninput() {
// fired on pressing "x" to clear the field
prefs.set('editor.colorpicker.hotkey', '');
},
onpaste(event) {
event.preventDefault();
}
}); });
const popup = showHelp(t('helpKeyMapHotkey'), input); const popup = showHelp(t('helpKeyMapHotkey'), input);
if (this instanceof Element) { if (this instanceof Element) {

View File

@ -267,6 +267,10 @@ input:invalid {
margin-top: 4em; margin-top: 4em;
} }
/************ section editor ***********/ /************ section editor ***********/
.CodeMirror-vscrollbar,
.CodeMirror-hscrollbar {
pointer-events: auto !important; /* FF bug */
}
.section-editor .section { .section-editor .section {
margin: 0 0.7rem; margin: 0 0.7rem;
padding: 1rem; padding: 1rem;
@ -775,6 +779,11 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
padding-left: 4px; padding-left: 4px;
margin-left: 4px; margin-left: 4px;
} }
.beautify-hint {
width: 0;
min-width: 100%;
font-size: 90%;
}
/************ single editor **************/ /************ single editor **************/
.usercss body { .usercss body {

View File

@ -1,7 +1,7 @@
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML /* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch createSourceEditor sessionStorageHash getOwnTab FIREFOX API tryCatch
closeCurrentTab messageBox debounce workerUtil closeCurrentTab messageBox debounce workerUtil
beautify ignoreChromeError initBeautifyButton ignoreChromeError
moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */ moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
/* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */ /* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
'use strict'; 'use strict';
@ -170,12 +170,21 @@ preinit();
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle'); $('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName'); $('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
$('#name').title = usercss ? t('usercssReplaceTemplateName') : ''; $('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
$('#preview-label').classList.toggle('hidden', !style.id); $('#preview-label').classList.toggle('hidden', !style.id);
initBeautifyButton($('#beautify'), () => editor.getEditors());
$('#beautify').onclick = () => beautify(editor.getEditors()); const {onBoundsChanged} = chrome.windows || {};
if (onBoundsChanged) {
// * movement is reported even if the window wasn't resized
// * fired just once when done so debounce is not needed
onBoundsChanged.addListener(wnd => {
// getting the current window id as it may change if the user attached/detached the tab
chrome.windows.getCurrent(ownWnd => {
if (wnd.id === ownWnd.id) rememberWindowSize();
});
});
}
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
debounce(rememberWindowSize, 100); if (!onBoundsChanged) debounce(rememberWindowSize, 100);
detectLayout(); detectLayout();
}); });
detectLayout(); detectLayout();
@ -217,7 +226,7 @@ function preinit() {
}).observe(document, {subtree: true, childList: true}); }).observe(document, {subtree: true, childList: true});
if (chrome.windows) { if (chrome.windows) {
queryTabs({currentWindow: true}).then(tabs => { browser.tabs.query({currentWindow: true}).then(tabs => {
const windowId = tabs[0].windowId; const windowId = tabs[0].windowId;
if (prefs.get('openEditInWindow')) { if (prefs.get('openEditInWindow')) {
if ( if (

View File

@ -61,7 +61,7 @@
border: none; border: none;
background-color: white; background-color: white;
font-weight: bold; font-weight: bold;
white-space: nowrap; white-space: pre; /* issue #1000 */
color: currentColor; /* use the current theme's color instead of UserAgent's CSS */ color: currentColor; /* use the current theme's color instead of UserAgent's CSS */
flex: 1; flex: 1;
} }

View File

@ -1,4 +1,4 @@
/* global CodeMirror focusAccessibility colorMimicry editor /* global CodeMirror focusAccessibility colorMimicry editor chromeLocal
onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */ onDOMready $ $$ $create t debounce tryRegExp stringAsRegExp template */
'use strict'; 'use strict';
@ -915,7 +915,7 @@ onDOMready().then(() => {
function readStorage() { function readStorage() {
chrome.storage.local.get('editor', ({editor = {}}) => { chromeLocal.getValue('editor').then((editor = {}) => {
state.find = editor.find || ''; state.find = editor.find || '';
state.replace = editor.replace || ''; state.replace = editor.replace || '';
state.icase = editor.icase || state.icase; state.icase = editor.icase || state.icase;
@ -924,14 +924,12 @@ onDOMready().then(() => {
function writeStorage() { function writeStorage() {
chrome.storage.local.get('editor', ({editor}) => chromeLocal.getValue('editor').then((editor = {}) =>
chrome.storage.local.set({ chromeLocal.setValue('editor', Object.assign(editor, {
editor: Object.assign(editor || {}, { find: state.find,
find: state.find, replace: state.replace,
replace: state.replace, icase: state.icase,
icase: state.icase, })));
})
}));
} }

View File

@ -1,4 +1,4 @@
/* global showHelp $ $create tryRegExp queryTabs URLS t template openURL */ /* global showHelp $ $create tryRegExp URLS t template openURL */
/* exported regExpTester */ /* exported regExpTester */
'use strict'; 'use strict';
@ -66,7 +66,7 @@ const regExpTester = (() => {
return rxData; return rxData;
}); });
const getMatchInfo = m => m && {text: m[0], pos: m.index}; const getMatchInfo = m => m && {text: m[0], pos: m.index};
queryTabs({}).then(tabs => { browser.tabs.query({}).then(tabs => {
const supported = tabs.map(tab => tab.url) const supported = tabs.map(tab => tab.url)
.filter(url => URLS.supported(url)); .filter(url => URLS.supported(url));
const unique = [...new Set(supported).values()]; const unique = [...new Set(supported).values()];

View File

@ -12,6 +12,7 @@ const rerouteHotkeys = (() => {
'toggleEditorFocus', 'toggleEditorFocus',
'find', 'findNext', 'findPrev', 'replace', 'replaceAll', 'find', 'findNext', 'findPrev', 'replace', 'replaceAll',
'colorpicker', 'colorpicker',
'beautify',
]); ]);
return rerouteHotkeys; return rerouteHotkeys;

View File

@ -1,5 +1,5 @@
/* global template cmFactory $ propertyToCss CssToProperty linter regExpTester /* global template cmFactory $ propertyToCss CssToProperty linter regExpTester
FIREFOX toggleContextMenuDelete beautify showHelp t tryRegExp */ FIREFOX toggleContextMenuDelete initBeautifyButton showHelp t tryRegExp */
/* exported createSection */ /* exported createSection */
'use strict'; 'use strict';
@ -94,6 +94,7 @@ function createSection({
const cm = cmFactory.create(wrapper => { const cm = cmFactory.create(wrapper => {
el.insertBefore(wrapper, $('.code-label', el).nextSibling); el.insertBefore(wrapper, $('.code-label', el).nextSibling);
}, {value: originalSection.code}); }, {value: originalSection.code});
el.CodeMirror = cm; // used by getAssociatedEditor
const changeListeners = new Set(); const changeListeners = new Set();
@ -196,12 +197,12 @@ function createSection({
$('.clone-section', el).addEventListener('click', () => insertSectionAfter(getModel(), section)); $('.clone-section', el).addEventListener('click', () => insertSectionAfter(getModel(), section));
$('.move-section-up', el).addEventListener('click', () => moveSectionUp(section)); $('.move-section-up', el).addEventListener('click', () => moveSectionUp(section));
$('.move-section-down', el).addEventListener('click', () => moveSectionDown(section)); $('.move-section-down', el).addEventListener('click', () => moveSectionDown(section));
$('.beautify-section', el).addEventListener('click', () => beautify([cm]));
$('.restore-section', el).addEventListener('click', () => restoreSection(section)); $('.restore-section', el).addEventListener('click', () => restoreSection(section));
$('.test-regexp', el).addEventListener('click', () => { $('.test-regexp', el).addEventListener('click', () => {
regExpTester.toggle(); regExpTester.toggle();
updateRegexpTester(); updateRegexpTester();
}); });
initBeautifyButton($('.beautify-section', el), () => [cm]);
} }
function handleKeydown(cm, event) { function handleKeydown(cm, event) {

View File

@ -34,7 +34,8 @@ function createSectionsEditor({style, onTitleChanged}) {
$('#from-mozilla').addEventListener('click', () => showMozillaFormatImport()); $('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
$('#save-button').addEventListener('click', saveStyle); $('#save-button').addEventListener('click', saveStyle);
document.addEventListener('wheel', scrollEntirePageOnCtrlShift); document.addEventListener('wheel', scrollEntirePageOnCtrlShift, {passive: false});
CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow';
if (!FIREFOX) { if (!FIREFOX) {
$$([ $$([
@ -154,9 +155,7 @@ function createSectionsEditor({style, onTitleChanged}) {
function closestVisible(nearbyElement) { function closestVisible(nearbyElement) {
const cm = const cm =
nearbyElement instanceof CodeMirror ? nearbyElement : nearbyElement instanceof CodeMirror ? nearbyElement :
nearbyElement instanceof Node && nearbyElement instanceof Node && getAssociatedEditor(nearbyElement) || getLastActivatedEditor();
(nearbyElement.closest('#sections > .section') || {}).CodeMirror ||
getLastActivatedEditor();
if (nearbyElement instanceof Node && cm) { if (nearbyElement instanceof Node && cm) {
const {left, top} = nearbyElement.getBoundingClientRect(); const {left, top} = nearbyElement.getBoundingClientRect();
const bounds = cm.display.wrapper.getBoundingClientRect(); const bounds = cm.display.wrapper.getBoundingClientRect();
@ -228,6 +227,15 @@ function createSectionsEditor({style, onTitleChanged}) {
} }
} }
function getAssociatedEditor(nearbyElement) {
for (let el = nearbyElement; el; el = el.parentElement) {
// added by createSection
if (el.CodeMirror) {
return el.CodeMirror;
}
}
}
function getEditors() { function getEditors() {
return sections.filter(s => !s.isRemoved()).map(s => s.cm); return sections.filter(s => !s.isRemoved()).map(s => s.cm);
} }

View File

@ -10,7 +10,6 @@ function showKeyMapHelp() {
const keyMap = mergeKeyMaps({}, prefs.get('editor.keyMap'), CodeMirror.defaults.extraKeys); const keyMap = mergeKeyMaps({}, prefs.get('editor.keyMap'), CodeMirror.defaults.extraKeys);
const keyMapSorted = Object.keys(keyMap) const keyMapSorted = Object.keys(keyMap)
.map(key => ({key, cmd: keyMap[key]})) .map(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)); .sort((a, b) => (a.cmd < b.cmd || (a.cmd === b.cmd && a.key < b.key) ? -1 : 1));
const table = template.keymapHelp.cloneNode(true); const table = template.keymapHelp.cloneNode(true);
const tBody = table.tBodies[0]; const tBody = table.tBodies[0];

View File

@ -1,4 +1,5 @@
/* exported dirtyReporter memoize clipString sectionsToMozFormat */ /* global CodeMirror $create prefs */
/* exported dirtyReporter memoize clipString sectionsToMozFormat createHotkeyInput */
'use strict'; 'use strict';
function dirtyReporter() { function dirtyReporter() {
@ -135,3 +136,52 @@ function memoize(fn) {
return result; return result;
}; };
} }
/**
* @param {!string} prefId
* @param {?function(isEnter:boolean)} onDone
*/
function createHotkeyInput(prefId, onDone = () => {}) {
return $create('input', {
type: 'search',
spellcheck: false,
value: prefs.get(prefId),
onkeydown(event) {
const key = CodeMirror.keyName(event);
if (key === 'Tab' || key === 'Shift-Tab') {
return;
}
event.preventDefault();
event.stopPropagation();
switch (key) {
case 'Enter':
if (this.checkValidity()) onDone(true);
return;
case 'Esc':
onDone(false);
return;
default:
// disallow: [Shift?] characters, modifiers-only, [modifiers?] + Esc, Tab, nav keys
if (!key || new RegExp('^(' + [
'(Back)?Space',
'(Shift-)?.', // a single character
'(Shift-?|Ctrl-?|Alt-?|Cmd-?){0,2}(|Esc|Tab|(Page)?(Up|Down)|Left|Right|Home|End|Insert|Delete)',
].join('|') + ')$', 'i').test(key)) {
this.value = key || this.value;
this.setCustomValidity('Not allowed');
return;
}
}
this.value = key;
this.setCustomValidity('');
prefs.set(prefId, key);
},
oninput() {
// fired on pressing "x" to clear the field
prefs.set(prefId, '');
},
onpaste(event) {
event.preventDefault();
}
});
}

View File

@ -10,7 +10,6 @@
<link href="install-usercss/install-usercss.css" rel="stylesheet"> <link href="install-usercss/install-usercss.css" rel="stylesheet">
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/msg.js"></script> <script src="js/msg.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -405,13 +405,9 @@
} }
}); });
port.onDisconnect.addListener(() => { port.onDisconnect.addListener(() => {
chrome.tabs.get(tabId, tab => { browser.tabs.get(tabId)
if (chrome.runtime.lastError) { .then(tab => tab.url === initialUrl && location.reload())
closeCurrentTab(); .catch(closeCurrentTab);
} else if (tab.url === initialUrl) {
location.reload();
}
});
}); });
return ({timer = true} = {}) => new Promise((resolve, reject) => { return ({timer = true} = {}) => new Promise((resolve, reject) => {
const id = performance.now(); const id = performance.now();

View File

@ -85,6 +85,27 @@ onDOMready().then(() => {
// set language for CSS :lang and [FF-only] hyphenation // set language for CSS :lang and [FF-only] hyphenation
document.documentElement.setAttribute('lang', chrome.i18n.getUILanguage()); document.documentElement.setAttribute('lang', chrome.i18n.getUILanguage());
// avoid adding # to the page URL when clicking dummy links
document.addEventListener('click', e => {
if (e.target.closest('a[href="#"]')) {
e.preventDefault();
}
});
// update inputs on mousewheel when focused
document.addEventListener('wheel', event => {
const el = document.activeElement;
if (!el || el !== event.target && !el.contains(event.target)) {
return;
}
if (el.tagName === 'SELECT') {
el.selectedIndex = Math.max(0, Math.min(el.length - 1, el.selectedIndex + Math.sign(event.deltaY)));
event.preventDefault();
}
event.stopImmediatePropagation();
}, {
capture: true,
passive: false,
});
function onDOMready() { function onDOMready() {
if (document.readyState !== 'loading') { if (document.readyState !== 'loading') {

View File

@ -1,18 +1,16 @@
/* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError /* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */ closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
/* global promisify */ /* global promisifyChrome */
'use strict'; 'use strict';
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(?:\d+\.){2}(\d+)|$/)[1]); const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(\d+)|$/)[1]);
const OPERA = Boolean(chrome.app) && parseFloat(navigator.userAgent.match(/\bOPR\/(\d+\.\d+)|$/)[1]); const OPERA = Boolean(chrome.app) && parseFloat(navigator.userAgent.match(/\bOPR\/(\d+\.\d+)|$/)[1]);
const VIVALDI = Boolean(chrome.app) && navigator.userAgent.includes('Vivaldi'); const VIVALDI = Boolean(chrome.app) && navigator.userAgent.includes('Vivaldi');
// FIXME: who use this?
// const ANDROID = !chrome.windows;
let FIREFOX = !chrome.app && parseFloat(navigator.userAgent.match(/\bFirefox\/(\d+\.\d+)|$/)[1]); let FIREFOX = !chrome.app && parseFloat(navigator.userAgent.match(/\bFirefox\/(\d+\.\d+)|$/)[1]);
// see PR #781 // see PR #781
const CHROME_HAS_BORDER_BUG = CHROME >= 3167 && CHROME <= 3704; const CHROME_HAS_BORDER_BUG = CHROME >= 62 && CHROME <= 74;
if (!CHROME && !chrome.browserAction.openPopup) { if (!CHROME && !chrome.browserAction.openPopup) {
// in FF pre-57 legacy addons can override useragent so we assume the worst // in FF pre-57 legacy addons can override useragent so we assume the worst
@ -62,7 +60,7 @@ const URLS = {
// Chrome 61.0.3161+ doesn't run content scripts on NTP https://crrev.com/2978953002/ // Chrome 61.0.3161+ doesn't run content scripts on NTP https://crrev.com/2978953002/
// TODO: remove when "minimum_chrome_version": "61" or higher // TODO: remove when "minimum_chrome_version": "61" or higher
chromeProtectsNTP: CHROME >= 3161, chromeProtectsNTP: CHROME >= 61,
userstylesOrgJson: 'https://userstyles.org/styles/chrome/', userstylesOrgJson: 'https://userstyles.org/styles/chrome/',
@ -95,33 +93,20 @@ if (IS_BG) {
// Object.defineProperty(window, 'localStorage', {value: {}}); // Object.defineProperty(window, 'localStorage', {value: {}});
// Object.defineProperty(window, 'sessionStorage', {value: {}}); // Object.defineProperty(window, 'sessionStorage', {value: {}});
const createTab = promisify(chrome.tabs.create.bind(chrome.tabs)); promisifyChrome({
const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs)); tabs: ['create', 'get', 'getCurrent', 'move', 'query', 'update'],
const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs)); windows: ['create', 'update'], // Android doesn't have chrome.windows
const moveTabs = promisify(chrome.tabs.move.bind(chrome.tabs)); });
// Android doesn't have chrome.windows
const updateWindow = chrome.windows && promisify(chrome.windows.update.bind(chrome.windows));
const createWindow = chrome.windows && promisify(chrome.windows.create.bind(chrome.windows));
// FF57+ supports openerTabId, but not in Android // FF57+ supports openerTabId, but not in Android
// (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config) // (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null; const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
function getTab(id) {
return new Promise(resolve =>
chrome.tabs.get(id, tab =>
!chrome.runtime.lastError && resolve(tab)));
}
function getOwnTab() { function getOwnTab() {
return new Promise(resolve => return browser.tabs.getCurrent();
chrome.tabs.getCurrent(tab => resolve(tab)));
} }
function getActiveTab() { function getActiveTab() {
return queryTabs({currentWindow: true, active: true}) return browser.tabs.query({currentWindow: true, active: true})
.then(tabs => tabs[0]); .then(tabs => tabs[0]);
} }
@ -142,7 +127,7 @@ function urlToMatchPattern(url, ignoreSearch) {
function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) { function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
url = new URL(url); url = new URL(url);
return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow}) return browser.tabs.query({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
// FIXME: is tab.url always normalized? // FIXME: is tab.url always normalized?
.then(tabs => tabs.find(matchTab)); .then(tabs => tabs.find(matchTab));
@ -193,8 +178,8 @@ function openURL({
url: url !== tab.url && url.includes('#') ? url : undefined, url: url !== tab.url && url.includes('#') ? url : undefined,
}); });
} }
if (newWindow && createWindow) { if (newWindow && browser.windows) {
return createWindow(Object.assign({url}, windowPosition)) return browser.windows.create(Object.assign({url}, windowPosition))
.then(wnd => wnd.tabs[0]); .then(wnd => wnd.tabs[0]);
} }
return getActiveTab().then((activeTab = {url: ''}) => return getActiveTab().then((activeTab = {url: ''}) =>
@ -207,7 +192,7 @@ function openURL({
if (id != null && !openerTab.incognito && openerTabIdSupported) { if (id != null && !openerTab.incognito && openerTabIdSupported) {
options.openerTabId = id; options.openerTabId = id;
} }
return createTab(options); return browser.tabs.create(options);
} }
} }
@ -234,9 +219,9 @@ function activateTab(tab, {url, index, openerTabId} = {}) {
options.openerTabId = openerTabId; options.openerTabId = openerTabId;
} }
return Promise.all([ return Promise.all([
updateTab(tab.id, options), browser.tabs.update(tab.id, options),
updateWindow && updateWindow(tab.windowId, {focused: true}), browser.windows && browser.windows.update(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index}) index != null && browser.tabs.move(tab.id, {index})
]) ])
.then(() => tab); .then(() => tab);
} }
@ -381,7 +366,8 @@ function download(url, {
body, body,
responseType = 'text', responseType = 'text',
requiredStatusCode = 200, requiredStatusCode = 200,
timeout = 10e3, timeout = 10e3, // connection timeout
loadTimeout = 2 * 60e3, // data transfer timeout (counted from the first remote response)
headers = { headers = {
'Content-type': 'application/x-www-form-urlencoded', 'Content-type': 'application/x-www-form-urlencoded',
}, },
@ -398,23 +384,41 @@ function download(url, {
const usoVars = []; const usoVars = [];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let xhr;
const u = new URL(collapseUsoVars(url)); const u = new URL(collapseUsoVars(url));
if (u.protocol === 'file:' && FIREFOX) { const onTimeout = () => {
if (xhr) xhr.abort();
reject(new Error('Timeout fetching ' + u.href));
};
let timer = setTimeout(onTimeout, timeout);
const switchTimer = () => {
clearTimeout(timer);
timer = loadTimeout && setTimeout(onTimeout, loadTimeout);
};
if (u.protocol === 'file:' && FIREFOX) { // TODO: maybe remove this since FF68+ can't do it anymore
// https://stackoverflow.com/questions/42108782/firefox-webextensions-get-local-files-content-by-path // https://stackoverflow.com/questions/42108782/firefox-webextensions-get-local-files-content-by-path
// FIXME: add FetchController when it is available. // FIXME: add FetchController when it is available.
const timer = setTimeout(reject, timeout, new Error('Timeout fetching ' + u.href));
fetch(u.href, {mode: 'same-origin'}) fetch(u.href, {mode: 'same-origin'})
.then(r => { .then(r => {
clearTimeout(timer); switchTimer();
return r.status === 200 ? r.text() : Promise.reject(r.status); return r.status === 200 ? r.text() : Promise.reject(r.status);
}) })
.catch(reject) .catch(reject)
.then(resolve); .then(text => {
clearTimeout(timer);
resolve(text);
});
return; return;
} }
const xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
xhr.timeout = timeout; xhr.onreadystatechange = () => {
if (xhr.readyState >= XMLHttpRequest.HEADERS_RECEIVED) {
xhr.onreadystatechange = null;
switchTimer();
}
};
xhr.onloadend = event => { xhr.onloadend = event => {
clearTimeout(timer);
if (event.type !== 'error' && ( if (event.type !== 'error' && (
xhr.status === requiredStatusCode || !requiredStatusCode || xhr.status === requiredStatusCode || !requiredStatusCode ||
u.protocol === 'file:')) { u.protocol === 'file:')) {

View File

@ -1,12 +1,12 @@
/* global promisify deepCopy */ /* global promisifyChrome deepCopy */
// deepCopy is only used if the script is executed in extension pages. // deepCopy is only used if the script is executed in extension pages.
'use strict'; 'use strict';
self.msg = self.INJECTED === 1 ? self.msg : (() => { self.msg = self.INJECTED === 1 ? self.msg : (() => {
const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime)); promisifyChrome({
const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs)); runtime: ['sendMessage'],
const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs)); tabs: ['sendMessage', 'query'],
});
const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window; const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
if (isBg) { if (isBg) {
window._msg = { window._msg = {
@ -49,19 +49,21 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
} }
} }
if (chrome.runtime.getBackgroundPage) { if (chrome.runtime.getBackgroundPage) {
return promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))() promisifyChrome({
.catch(() => null); runtime: ['getBackgroundPage'],
});
return browser.runtime.getBackgroundPage().catch(() => null);
} }
return Promise.resolve(null); return Promise.resolve(null);
} }
function send(data, target = 'extension') { function send(data, target = 'extension') {
const message = {data, target}; const message = {data, target};
return runtimeSend(message).then(unwrapData); return browser.runtime.sendMessage(message).then(unwrapData);
} }
function sendTab(tabId, data, options, target = 'tab') { function sendTab(tabId, data, options, target = 'tab') {
return tabSend(tabId, {data, target}, options) return browser.tabs.sendMessage(tabId, {data, target}, options)
.then(unwrapData); .then(unwrapData);
} }
@ -99,7 +101,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
} }
function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') { function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
return tabQuery({}) return browser.tabs.query({})
// TODO: send to activated tabs first? // TODO: send to activated tabs first?
.then(tabs => { .then(tabs => {
const requests = []; const requests = [];
@ -123,7 +125,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
const message = {data: dataObj, target}; const message = {data: dataObj, target};
if (tab && tab.id) { if (tab && tab.id) {
requests.push( requests.push(
tabSend(tab.id, message, options) browser.tabs.sendMessage(tab.id, message, options)
.then(unwrapData) .then(unwrapData)
.catch(ignoreError) .catch(ignoreError)
); );

View File

@ -3,6 +3,8 @@
// eslint-disable-next-line no-unused-expressions // eslint-disable-next-line no-unused-expressions
self.INJECTED !== 1 && (() => { self.INJECTED !== 1 && (() => {
// this part runs in workers, content scripts, our extension pages
if (!Object.entries) { if (!Object.entries) {
Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]); Object.entries = obj => Object.keys(obj).map(k => [k, obj[k]]);
} }
@ -10,9 +12,38 @@ self.INJECTED !== 1 && (() => {
Object.values = obj => Object.keys(obj).map(k => obj[k]); Object.values = obj => Object.keys(obj).map(k => obj[k]);
} }
// the above was shared by content scripts and workers, // don't use self.chrome. It is undefined in Firefox
// the rest is only needed for our extension pages if (typeof chrome !== 'object') return;
if (!self.chrome || !self.chrome.tabs) return; // the rest is for content scripts and our extension pages
self.browser = polyfillBrowser();
/* Promisifies the specified `chrome` methods into `browser`.
The definitions is an object like this: {
'storage.sync': ['get', 'set'], // if deeper than one level, combine the path via `.`
windows: ['create', 'update'], // items and sub-objects will only be created if present in `chrome`
} */
self.promisifyChrome = definitions => {
for (const [scopeName, methods] of Object.entries(definitions)) {
const path = scopeName.split('.');
const src = path.reduce((obj, p) => obj && obj[p], chrome);
if (!src) continue;
const dst = path.reduce((obj, p) => obj[p] || (obj[p] = {}), browser);
for (const name of methods) {
const fn = src[name];
if (!fn || dst[name] && !dst[name].isTrap) continue;
dst[name] = (...args) => new Promise((resolve, reject) =>
fn.call(src, ...args, (...results) =>
chrome.runtime.lastError ?
reject(chrome.runtime.lastError) :
resolve(results.length <= 1 ? results[0] : results)));
// a couple of callbacks have 2 parameters (we don't use those methods, but just in case)
}
}
};
if (!chrome.tabs) return;
// the rest is for our extension pages
if (typeof document === 'object') { if (typeof document === 'object') {
const ELEMENT_METH = { const ELEMENT_METH = {
@ -75,4 +106,27 @@ self.INJECTED !== 1 && (() => {
} catch (err) { } catch (err) {
Object.defineProperty(self, 'sessionStorage', {value: {}}); Object.defineProperty(self, 'sessionStorage', {value: {}});
} }
function polyfillBrowser() {
if (typeof browser === 'object' && browser.runtime) {
return browser;
}
return createTrap(chrome, null);
function createTrap(base, parent) {
const target = typeof base === 'function' ? () => {} : {};
target.isTrap = true;
return new Proxy(target, {
get: (target, prop) => {
if (target[prop]) return target[prop];
if (base[prop] && (typeof base[prop] === 'object' || typeof base[prop] === 'function')) {
target[prop] = createTrap(base[prop], base);
return target[prop];
}
return base[prop];
},
apply: (target, thisArg, args) => base.apply(parent, args)
});
}
}
})(); })();

View File

@ -1,4 +1,4 @@
/* global promisify */ /* global promisifyChrome */
'use strict'; 'use strict';
self.prefs = self.INJECTED === 1 ? self.prefs : (() => { self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
@ -63,6 +63,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
end_with_newline: false, end_with_newline: false,
indent_conditional: true, indent_conditional: true,
}, },
'editor.beautify.hotkey': '',
'editor.lintDelay': 300, // lint gutter marker update delay, ms 'editor.lintDelay': 300, // lint gutter marker update delay, ms
'editor.linter': 'csslint', // 'csslint' or 'stylelint' or '' 'editor.linter': 'csslint', // 'csslint' or 'stylelint' or ''
'editor.lintReportDelay': 500, // lint report update delay, ms 'editor.lintReportDelay': 500, // lint report update delay, ms
@ -110,10 +111,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
specific: new Map(), specific: new Map(),
}; };
const syncSet = promisify(chrome.storage.sync.set.bind(chrome.storage.sync)); promisifyChrome({
const syncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync)); 'storage.sync': ['get', 'set'],
});
const initializing = syncGet('settings') const initializing = browser.storage.sync.get('settings')
.then(result => { .then(result => {
if (result.settings) { if (result.settings) {
setAll(result.settings, true); setAll(result.settings, true);
@ -240,7 +242,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
timer = null; timer = null;
syncSet({settings: values}) browser.storage.sync.set({settings: values})
.then(resolve, reject); .then(resolve, reject);
}); });
}); });

View File

@ -1,22 +0,0 @@
'use strict';
/*
Convert chrome APIs into promises. Example:
const storageSyncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync));
storageSyncGet(['key']).then(result => {...});
*/
self.promisify = self.INJECTED === 1 ? self.promisify : fn =>
(...args) =>
new Promise((resolve, reject) => {
fn(...args, (...result) => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
return;
}
resolve(
result.length === 0 ? undefined :
result.length === 1 ? result[0] : result
);
});
});

View File

@ -1,7 +1,12 @@
/* global loadScript tryJSONparse */ /* global loadScript tryJSONparse promisifyChrome */
/* exported chromeLocal chromeSync */ /* exported chromeLocal chromeSync */
'use strict'; 'use strict';
promisifyChrome({
'storage.local': ['get', 'remove', 'set'],
'storage.sync': ['get', 'remove', 'set'],
});
const [chromeLocal, chromeSync] = (() => { const [chromeLocal, chromeSync] = (() => {
return [ return [
createWrapper('local'), createWrapper('local'),
@ -9,11 +14,11 @@ const [chromeLocal, chromeSync] = (() => {
]; ];
function createWrapper(name) { function createWrapper(name) {
const storage = chrome.storage[name]; const storage = browser.storage[name];
const wrapper = { const wrapper = {
get: data => new Promise(resolve => storage.get(data, resolve)), get: storage.get.bind(storage),
set: data => new Promise(resolve => storage.set(data, () => resolve(data))), set: data => storage.set(data).then(() => data),
remove: data => new Promise(resolve => storage.remove(data, resolve)), remove: storage.remove.bind(storage),
/** /**
* @param {String} key * @param {String} key

View File

@ -6,7 +6,7 @@ const workerUtil = (() => {
const loadedScripts = new Set(); const loadedScripts = new Set();
return {createWorker, createAPI, loadScript, cloneError}; return {createWorker, createAPI, loadScript, cloneError};
function createWorker({url, lifeTime = 30}) { function createWorker({url, lifeTime = 300}) {
let worker; let worker;
let id; let id;
let timer; let timer;

View File

@ -147,7 +147,6 @@
</template> </template>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/prefs.js"></script> <script src="js/prefs.js"></script>

View File

@ -1,11 +1,15 @@
/* global messageBox styleSectionsEqual API onDOMready /* global messageBox styleSectionsEqual API onDOMready
tryJSONparse scrollElementIntoView $ $$ API $create t animateElement tryJSONparse scrollElementIntoView $ $$ API $create t animateElement
styleJSONseemsValid */ styleJSONseemsValid */
/* exported bulkChangeQueue bulkChangeTime */
'use strict'; 'use strict';
const STYLISH_DUMP_FILE_EXT = '.txt'; const STYLISH_DUMP_FILE_EXT = '.txt';
const STYLUS_BACKUP_FILE_EXT = '.json'; const STYLUS_BACKUP_FILE_EXT = '.json';
let bulkChangeQueue = [];
let bulkChangeTime = 0;
onDOMready().then(() => { onDOMready().then(() => {
$('#file-all-styles').onclick = event => { $('#file-all-styles').onclick = event => {
event.preventDefault(); event.preventDefault();
@ -136,6 +140,8 @@ function importFromString(jsonString) {
items.push({info, item}); items.push({info, item});
} }
}); });
bulkChangeQueue.length = 0;
bulkChangeTime = performance.now();
return API.importManyStyles(items.map(i => i.item)) return API.importManyStyles(items.map(i => i.item))
.then(styles => { .then(styles => {
for (let i = 0; i < styles.length; i++) { for (let i = 0; i < styles.length; i++) {

View File

@ -73,18 +73,22 @@ onDOMready().then(() => {
} }
function maybeRefocus(event) { function maybeRefocus(event) {
if (event.altKey || event.ctrlKey || event.metaKey || if (event.altKey || event.metaKey || $('#message-box')) {
event.target.matches('[type="text"], [type="search"], [type="number"]') ||
$('#message-box')) {
return; return;
} }
const inTextInput = event.target.matches('[type=text], [type=search], [type=number]');
const {which: k, key} = event; const {which: k, key} = event;
// focus search field on "/" key // focus search field on "/" or Ctrl-F key
if (key === '/' || !key && k === 191 && !event.shiftKey) { if (event.ctrlKey
? (event.code === 'KeyF' || !event.code && k === 70) && !event.shiftKey
: (key === '/' || !key && k === 191 && !event.shiftKey) && !inTextInput) {
event.preventDefault(); event.preventDefault();
$('#search').focus(); $('#search').focus();
return; return;
} }
if (event.ctrlKey || inTextInput) {
return;
}
const time = performance.now(); const time = performance.now();
if ( if (
// 0-9 // 0-9

View File

@ -8,6 +8,7 @@ global messageBox getStyleWithNoCode
URLS enforceInputRange t tWordBreak formatDate URLS enforceInputRange t tWordBreak formatDate
getOwnTab getActiveTab openURL animateElement sessionStorageHash debounce getOwnTab getActiveTab openURL animateElement sessionStorageHash debounce
scrollElementIntoView CHROME VIVALDI FIREFOX router scrollElementIntoView CHROME VIVALDI FIREFOX router
bulkChangeTime:true bulkChangeQueue
*/ */
'use strict'; 'use strict';
@ -16,6 +17,8 @@ let installed;
const ENTRY_ID_PREFIX_RAW = 'style-'; const ENTRY_ID_PREFIX_RAW = 'style-';
const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW; const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW;
const BULK_THROTTLE_MS = 100;
const newUI = { const newUI = {
enabled: prefs.get('manage.newUI'), enabled: prefs.get('manage.newUI'),
favicons: prefs.get('manage.newUI.favicons'), favicons: prefs.get('manage.newUI.favicons'),
@ -62,11 +65,13 @@ function onRuntimeMessage(msg) {
switch (msg.method) { switch (msg.method) {
case 'styleUpdated': case 'styleUpdated':
case 'styleAdded': case 'styleAdded':
API.getStyle(msg.style.id, true)
.then(style => handleUpdate(style, msg));
break;
case 'styleDeleted': case 'styleDeleted':
handleDelete(msg.style.id); bulkChangeQueue.push(msg);
if (performance.now() - bulkChangeTime < BULK_THROTTLE_MS) {
debounce(handleBulkChange, BULK_THROTTLE_MS);
} else {
handleBulkChange();
}
break; break;
case 'styleApply': case 'styleApply':
case 'styleReplaceAll': case 'styleReplaceAll':
@ -449,6 +454,8 @@ Object.assign(handleEvent, {
API.deleteStyle(id); API.deleteStyle(id);
} }
}); });
const deleteButton = $('#message-box-buttons > button');
if (deleteButton) deleteButton.removeAttribute('data-focused-via-click');
}, },
external(event) { external(event) {
@ -529,6 +536,26 @@ Object.assign(handleEvent, {
}); });
function handleBulkChange() {
for (const msg of bulkChangeQueue) {
const {id} = msg.style;
if (msg.method === 'styleDeleted') {
handleDelete(id);
bulkChangeTime = performance.now();
} else {
handleUpdateForId(id, msg);
}
}
bulkChangeQueue.length = 0;
}
function handleUpdateForId(id, opts) {
return API.getStyle(id, true).then(style => {
handleUpdate(style, opts);
bulkChangeTime = performance.now();
});
}
function handleUpdate(style, {reason, method} = {}) { function handleUpdate(style, {reason, method} = {}) {
if (reason === 'editPreview' || reason === 'editPreviewEnd') return; if (reason === 'editPreview' || reason === 'editPreviewEnd') return;
let entry; let entry;
@ -548,7 +575,7 @@ function handleUpdate(style, {reason, method} = {}) {
handleUpdateInstalled(entry, reason); handleUpdateInstalled(entry, reason);
} }
filterAndAppend({entry}).then(sorter.update); filterAndAppend({entry}).then(sorter.update);
if (!entry.matches('.hidden') && reason !== 'import') { if (!entry.matches('.hidden') && reason !== 'import' && reason !== 'sync') {
animateElement(entry); animateElement(entry);
requestAnimationFrame(() => scrollElementIntoView(entry)); requestAnimationFrame(() => scrollElementIntoView(entry));
} }
@ -625,7 +652,7 @@ function switchUI({styleOnly} = {}) {
filter: none; filter: none;
opacity: 1; opacity: 1;
} }
`) + (CHROME >= 3004 ? ` `) + (CHROME >= 58 ? `
.newUI .entry { .newUI .entry {
contain: strict; contain: strict;
} }
@ -677,15 +704,14 @@ function onVisibilityChange() {
// page restored without reloading via history navigation (currently only in FF) // page restored without reloading via history navigation (currently only in FF)
// the catch here is that DOM may be outdated so we'll at least refresh the just edited style // the catch here is that DOM may be outdated so we'll at least refresh the just edited style
// assuming other changes aren't important enough to justify making a complicated DOM sync // assuming other changes aren't important enough to justify making a complicated DOM sync
case 'visible': case 'visible': {
if (sessionStorage.justEditedStyleId) { const id = sessionStorage.justEditedStyleId;
API.getStyle(Number(sessionStorage.justEditedStyleId), true) if (id) {
.then(style => { handleUpdateForId(Number(id), {method: 'styleUpdated'});
handleUpdate(style, {method: 'styleUpdated'});
});
delete sessionStorage.justEditedStyleId; delete sessionStorage.justEditedStyleId;
} }
break; break;
}
// going away // going away
case 'hidden': case 'hidden':
history.replaceState({scrollY: window.scrollY}, document.title); history.replaceState({scrollY: window.scrollY}, document.title);

View File

@ -32,7 +32,7 @@ const sorter = (() => {
}, },
dateUpdated: { dateUpdated: {
text: t('dateUpdated'), text: t('dateUpdated'),
parse: ({style}) => style.updateDate, parse: ({style}) => style.updateDate || style.installDate,
sorter: sorterType.number sorter: sorterType.number
} }
}; };

View File

@ -1,15 +1,15 @@
{ {
"name": "Stylus", "name": "Stylus",
"version": "1.5.10", "version": "1.5.13",
"minimum_chrome_version": "49", "minimum_chrome_version": "49",
"description": "__MSG_description__", "description": "__MSG_description__",
"homepage_url": "https://add0n.com/stylus.html", "homepage_url": "https://add0n.com/stylus.html",
"manifest_version": 2, "manifest_version": 2,
"icons": { "icons": {
"16": "/images/icon/16.png", "16": "images/icon/16.png",
"32": "/images/icon/32.png", "32": "images/icon/32.png",
"48": "/images/icon/48.png", "48": "images/icon/48.png",
"128": "/images/icon/128.png" "128": "images/icon/128.png"
}, },
"permissions": [ "permissions": [
"tabs", "tabs",
@ -18,6 +18,7 @@
"webRequestBlocking", "webRequestBlocking",
"contextMenus", "contextMenus",
"storage", "storage",
"unlimitedStorage",
"alarms", "alarms",
"identity", "identity",
"<all_urls>" "<all_urls>"
@ -25,7 +26,6 @@
"background": { "background": {
"scripts": [ "scripts": [
"js/polyfill.js", "js/polyfill.js",
"js/promisify.js",
"js/messaging.js", "js/messaging.js",
"js/msg.js", "js/msg.js",
"js/storage-util.js", "js/storage-util.js",
@ -77,7 +77,6 @@
"match_about_blank": true, "match_about_blank": true,
"js": [ "js": [
"js/polyfill.js", "js/polyfill.js",
"js/promisify.js",
"js/msg.js", "js/msg.js",
"js/prefs.js", "js/prefs.js",
"content/style-injector.js", "content/style-injector.js",
@ -99,10 +98,10 @@
], ],
"browser_action": { "browser_action": {
"default_icon": { "default_icon": {
"16": "/images/icon/16w.png", "16": "images/icon/16w.png",
"32": "/images/icon/32w.png", "32": "images/icon/32w.png",
"19": "/images/icon/19w.png", "19": "images/icon/19w.png",
"38": "/images/icon/38w.png" "38": "images/icon/38w.png"
}, },
"default_title": "Stylus", "default_title": "Stylus",
"default_popup": "popup.html" "default_popup": "popup.html"

View File

@ -134,6 +134,15 @@
text-align: center; text-align: center;
} }
.danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
outline: red auto 1px;
}
/* FF ignores color with 'auto' */
.firefox .danger #message-box-buttons > button:not([data-focused-via-click]):first-child:focus {
outline: red solid 1px;
}
.non-windows #message-box-buttons { .non-windows #message-box-buttons {
text-align: right; text-align: right;
direction: rtl; direction: rtl;

View File

@ -21,7 +21,6 @@
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/promisify.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/msg.js"></script> <script src="js/msg.js"></script>
<script src="js/localization.js"></script> <script src="js/localization.js"></script>

View File

@ -8,8 +8,8 @@ setupLivePrefs();
enforceInputRange($('#popupWidth')); enforceInputRange($('#popupWidth'));
setTimeout(splitLongTooltips); setTimeout(splitLongTooltips);
// TODO: add max version to re-enable once crbug.com/996859 is resolved // https://github.com/openstyles/stylus/issues/822
if (!FIREFOX && CHROME >= 3809) { if (!FIREFOX && CHROME >= 76 && CHROME <= 81) {
const dropboxOption = $('option[value="dropbox"]'); const dropboxOption = $('option[value="dropbox"]');
dropboxOption.disabled = true; dropboxOption.disabled = true;
dropboxOption.setAttribute('title', t('hostDisabled')); dropboxOption.setAttribute('title', t('hostDisabled'));
@ -23,7 +23,7 @@ if (CHROME_HAS_BORDER_BUG) {
} }
// collapse #advanced block in Chrome pre-66 (classic chrome://extensions UI) // collapse #advanced block in Chrome pre-66 (classic chrome://extensions UI)
if (!FIREFOX && !OPERA && CHROME < 3343) { if (!FIREFOX && !OPERA && CHROME < 66) {
const block = $('#advanced'); const block = $('#advanced');
$('h1', block).onclick = event => { $('h1', block).onclick = event => {
event.preventDefault(); event.preventDefault();

2789
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,32 @@
{ {
"name": "Stylus", "name": "Stylus",
"version": "1.5.10", "version": "1.5.13",
"description": "Redesign the web with Stylus, a user styles manager", "description": "Redesign the web with Stylus, a user styles manager",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"repository": "openstyles/stylus", "repository": "openstyles/stylus",
"author": "Stylus Team", "author": "Stylus Team",
"devDependencies": { "dependencies": {
"archiver": "^3.1.1", "codemirror": "^5.56.0",
"codemirror": "^5.51.0",
"db-to-cloud": "^0.4.5", "db-to-cloud": "^0.4.5",
"endent": "^1.4.0",
"eslint": "^6.8.0",
"fs-extra": "^8.1.0",
"jsonlint": "^1.6.3", "jsonlint": "^1.6.3",
"less-bundle": "github:openstyles/less-bundle#v0.1.0", "less-bundle": "github:openstyles/less-bundle#v0.1.0",
"lz-string-unsafe": "^1.4.4-fork-1", "lz-string-unsafe": "^1.4.4-fork-1",
"make-fetch-happen": "^7.1.1",
"semver-bundle": "^0.1.1", "semver-bundle": "^0.1.1",
"shx": "^0.3.2",
"stylelint-bundle": "^8.0.0", "stylelint-bundle": "^8.0.0",
"stylus-lang-bundle": "^0.54.5", "stylus-lang-bundle": "^0.54.5",
"usercss-meta": "^0.9.0",
"uuid": "^8.1.0"
},
"devDependencies": {
"archiver": "^4.0.1",
"endent": "^1.4.0",
"eslint": "^7.1.0",
"fs-extra": "^9.0.0",
"make-fetch-happen": "^8.0.7",
"shx": "^0.3.2",
"sync-version": "^1.0.1", "sync-version": "^1.0.1",
"tiny-glob": "^0.2.6", "tiny-glob": "^0.2.6",
"usercss-meta": "^0.9.0", "web-ext": "^4.2.0",
"uuid": "^7.0.0-beta.0",
"web-ext": "^4.1.0",
"webext-tx-fix": "^0.3.3" "webext-tx-fix": "^0.3.3"
}, },
"scripts": { "scripts": {

View File

@ -179,7 +179,6 @@
<script src="manage/config-dialog.js"></script> <script src="manage/config-dialog.js"></script>
<script src="js/polyfill.js"></script> <script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script> <script src="js/dom.js"></script>
<script src="js/messaging.js"></script> <script src="js/messaging.js"></script>
<script src="js/localization.js"></script> <script src="js/localization.js"></script>

View File

@ -341,6 +341,13 @@ a.configure[target="_blank"] .svg-icon.config {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
#confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
outline: red auto 1px;
}
/* FF ignores color with 'auto' */
.firefox #confirm button[data-cmd="ok"]:not([data-focused-via-click]):focus {
outline: red solid 1px;
}
.menu-items-wrapper { .menu-items-wrapper {
width: 80%; width: 80%;
max-height: 80%; max-height: 80%;

View File

@ -15,7 +15,7 @@ const ABOUT_BLANK = 'about:blank';
const ENTRY_ID_PREFIX_RAW = 'style-'; const ENTRY_ID_PREFIX_RAW = 'style-';
const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW; const ENTRY_ID_PREFIX = '#' + ENTRY_ID_PREFIX_RAW;
if (CHROME >= 3345 && CHROME < 3533) { // Chrome 66-69 adds a gap, https://crbug.com/821143 if (CHROME >= 66 && CHROME <= 69) { // Chrome 66-69 adds a gap, https://crbug.com/821143
document.head.appendChild($create('style', 'html { overflow: overlay }')); document.head.appendChild($create('style', 'html { overflow: overlay }'));
} }

View File

@ -1,6 +1,6 @@
/* global tabURL handleEvent $ $$ prefs template FIREFOX chromeLocal debounce /* global tabURL handleEvent $ $$ prefs template FIREFOX chromeLocal debounce
$create t API tWordBreak formatDate tryCatch tryJSONparse LZString $create t API tWordBreak formatDate tryCatch tryJSONparse LZString
ignoreChromeError download */ promisifyChrome download */
'use strict'; 'use strict';
window.addEventListener('showStyles:done', function _() { window.addEventListener('showStyles:done', function _() {
@ -88,6 +88,9 @@ window.addEventListener('showStyles:done', function _() {
return; return;
function init() { function init() {
promisifyChrome({
'storage.local': ['getBytesInUse'], // FF doesn't implement it
});
setTimeout(() => document.body.classList.add(BODY_CLASS)); setTimeout(() => document.body.classList.add(BODY_CLASS));
$('#find-styles-inline-group').classList.add('hidden'); $('#find-styles-inline-group').classList.add('hidden');
@ -711,7 +714,7 @@ window.addEventListener('showStyles:done', function _() {
return chromeLocal.loadLZStringScript().then(() => return chromeLocal.loadLZStringScript().then(() =>
tryJSONparse(LZString.decompressFromUTF16(item.payload))); tryJSONparse(LZString.decompressFromUTF16(item.payload)));
} else if (item) { } else if (item) {
chrome.storage.local.remove(key); chromeLocal.remove(key);
} }
}); });
} }
@ -742,16 +745,8 @@ window.addEventListener('showStyles:done', function _() {
function cleanupCache() { function cleanupCache() {
chromeLocal.remove(CACHE_CLEANUP_NEEDED); chromeLocal.remove(CACHE_CLEANUP_NEEDED);
if (chrome.storage.local.getBytesInUse) { Promise.resolve(!browser.storage.local.getBytesInUse ? 1e99 : browser.storage.local.getBytesInUse())
chrome.storage.local.getBytesInUse(null, size => { .then(size => size > CACHE_SIZE && chromeLocal.get().then(cleanupCacheInternal));
if (size > CACHE_SIZE) {
chrome.storage.local.get(null, cleanupCacheInternal);
}
ignoreChromeError();
});
} else {
chrome.storage.local.get(null, cleanupCacheInternal);
}
} }
function cleanupCacheInternal(storage) { function cleanupCacheInternal(storage) {
@ -764,9 +759,8 @@ window.addEventListener('showStyles:done', function _() {
sortedByTime.slice(0, sortedByTime.length / 2); sortedByTime.slice(0, sortedByTime.length / 2);
const toRemove = expired.length ? expired : sortedByTime; const toRemove = expired.length ? expired : sortedByTime;
if (toRemove.length) { if (toRemove.length) {
chrome.storage.local.remove(toRemove.map(item => item.key), ignoreChromeError); chromeLocal.remove(toRemove.map(item => item.key));
} }
ignoreChromeError();
} }
//endregion //endregion
@ -779,6 +773,7 @@ window.addEventListener('showStyles:done', function _() {
return download(url, { return download(url, {
body: null, body: null,
responseType: 'json', responseType: 'json',
timeout: 60e3,
headers: { headers: {
'Referrer-Policy': 'origin-when-cross-origin', 'Referrer-Policy': 'origin-when-cross-origin',
[xhrSpoofTelltale]: requestId, [xhrSpoofTelltale]: requestId,

View File

@ -106,7 +106,7 @@ const colorConverter = (() => {
if (!type) return; if (!type) return;
const comma = value.includes(',') && !value.includes('/'); const comma = value.includes(',') && !value.includes('/');
const num = value.split(comma ? /\s*,\s*/ : /\s+(?!\/)|\s*\/\s*/); const num = value.trim().split(comma ? /\s*,\s*/ : /\s+(?!\/)|\s*\/\s*/);
if (num.length < 3 || num.length > 4) return; if (num.length < 3 || num.length > 4) return;
if (num[3] && !validateAlpha(num[3])) return null; if (num[3] && !validateAlpha(num[3])) return null;

View File

@ -182,12 +182,13 @@
if (viewFrom > 0 || viewTo < cm.doc.size) { if (viewFrom > 0 || viewTo < cm.doc.size) {
clearTimeout(state.colorizeTimer); clearTimeout(state.colorizeTimer);
state.colorizeTimer = setTimeout(colorizeInvisible, 100, state, viewFrom, viewTo, 0); state.line = 0;
state.colorizeTimer = setTimeout(colorizeInvisible, 100, state, viewFrom, viewTo);
} }
} }
function colorizeInvisible(state, viewFrom, viewTo, line) { function colorizeInvisible(state, viewFrom, viewTo) {
const {cm} = state; const {cm} = state;
const {curOp} = cm; const {curOp} = cm;
if (!curOp) cm.startOperation(); if (!curOp) cm.startOperation();
@ -197,22 +198,19 @@
state.stopped = null; state.stopped = null;
// before the visible range // before the visible range
if (viewFrom) { cm.eachLine(state.line, viewFrom, lineHandle => colorizeLine(state, lineHandle));
state.line = line;
cm.doc.iter(line, viewFrom, lineHandle => colorizeLine(state, lineHandle));
}
// after the visible range // after the visible range
if (!state.stopped && viewTo < cm.doc.size) { if (!state.stopped && viewTo < cm.doc.size) {
state.line = viewTo; state.line = Math.max(viewTo, state.line);
cm.doc.iter(viewTo, cm.doc.size, lineHandle => colorizeLine(state, lineHandle)); cm.eachLine(state.line, cm.doc.size, lineHandle => colorizeLine(state, lineHandle));
} }
updateMarkers(state); updateMarkers(state);
if (!curOp) cm.endOperation(); if (!curOp) cm.endOperation();
if (state.stopped) { if (state.stopped) {
state.colorizeTimer = setTimeout(colorizeInvisible, 0, state, viewFrom, viewFrom, state.line); state.colorizeTimer = setTimeout(colorizeInvisible, 0, state, viewFrom, viewTo);
} }
} }

View File

@ -229,30 +229,33 @@ var CSSLint = (() => {
// Example 1: // Example 1:
/* csslint ignore:start */ /* csslint ignore:start */
// the chunk of code where errors won't be reported /*
// the chunk's start is hardwired to the line of the opening comment the chunk of code where errors won't be reported
// the chunk's end is hardwired to the line of the closing comment the chunk's start is hardwired to the line of the opening comment
/* csslint ignore:end */ the chunk's end is hardwired to the line of the closing comment
*/
/* csslint ignore:end */
// Example 2: // Example 2:
// allow rule violations on the current line:
/* csslint allow:rulename1,rulename2,... */ // foo: bar; /* csslint allow:rulename1,rulename2,... */
// allows to break the specified rules on the next single line of code /* csslint allow:rulename1,rulename2,... */ // foo: bar;
// Example 3: // Example 3:
/* csslint rulename1 */ /* csslint rulename1 */
/* csslint rulename2:N */ /* csslint rulename2:N */
/* csslint rulename3:N, rulename4:N */ /* csslint rulename3:N, rulename4:N */
// entire code is affected; /* entire code is affected;
// comments futher down the code extend/override previous comments of this kind * comments futher down the code extend/override previous comments of this kind
// values for N: * values for N (without the backquotes):
// "2" or "true" means "error" `2` or `true` means "error"
// "1" or nothing means "warning" - note in this case ":" can also be omitted `1` or omitted means "warning" (when omitting, the colon can be omitted too)
// "0" or "false" means "ignore" `0` or `false` means "ignore"
// (the quotes are added here for convenience, don't put them in the actual comments) */
function applyEmbeddedOverrides(text, ruleset, allow, ignore) { function applyEmbeddedOverrides(text, ruleset, allow, ignore) {
let ignoreStart = null; let ignoreStart = null;
@ -282,7 +285,7 @@ var CSSLint = (() => {
allowRuleset[allowRule.trim()] = true; allowRuleset[allowRule.trim()] = true;
num++; num++;
}); });
if (num) allow[lineno + 1] = allowRuleset; if (num) allow[lineno] = allowRuleset;
break; break;
} }

View File

@ -319,7 +319,7 @@ self.parserlib = (() => {
'drop-initial-before-align': 'caps-height | baseline | use-script | before-edge | text-before-edge | ' + 'drop-initial-before-align': 'caps-height | baseline | use-script | before-edge | text-before-edge | ' +
'after-edge | text-after-edge | central | middle | ideographic | alphabetic | ' + 'after-edge | text-after-edge | central | middle | ideographic | alphabetic | ' +
'hanging | mathematical', 'hanging | mathematical',
'drop-initial-size': 'auto | line | <length> | <percentage>', 'drop-initial-size': 'auto | line | <length-percentage>',
'drop-initial-value': '<integer>', 'drop-initial-value': '<integer>',
// E // E
@ -347,19 +347,28 @@ self.parserlib = (() => {
'flood-opacity': '<opacity-value>', 'flood-opacity': '<opacity-value>',
'font': '<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar', 'font': '<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar',
'font-family': '<font-family>', 'font-family': '<font-family>',
'font-feature-settings': '<feature-tag-value> | normal', 'font-feature-settings': '<feature-tag-value># | normal',
'font-kerning': 'auto | normal | none', 'font-kerning': 'auto | normal | none',
'font-language-override': 'normal | <string>',
'font-optical-sizing': 'auto | none',
'font-palette': 'none | normal | light | dark | <ident>',
'font-size': '<font-size>', 'font-size': '<font-size>',
'font-size-adjust': '<number> | none', 'font-size-adjust': '<number> | none',
'font-stretch': '<font-stretch>', 'font-stretch': '<font-stretch>',
'font-style': '<font-style>', 'font-style': '<font-style>',
'font-synthesis': 'none | [ weight || style ]',
'font-synthesis-style': 'auto | none',
'font-synthesis-weight': 'auto | none',
'font-synthesis-small-caps': 'auto | none',
'font-variant': '<font-variant> | normal | none', 'font-variant': '<font-variant> | normal | none',
'font-variant-alternates': '<font-variant-alternates> | normal', 'font-variant-alternates': '<font-variant-alternates> | normal',
'font-variant-caps': '<font-variant-caps> | normal', 'font-variant-caps': '<font-variant-caps> | normal',
'font-variant-east-asian': '<font-variant-east-asian> | normal', 'font-variant-east-asian': '<font-variant-east-asian> | normal',
'font-variant-emoji': 'auto | text | emoji | unicode',
'font-variant-ligatures': '<font-variant-ligatures> | normal | none', 'font-variant-ligatures': '<font-variant-ligatures> | normal | none',
'font-variant-numeric': '<font-variant-numeric> | normal', 'font-variant-numeric': '<font-variant-numeric> | normal',
'font-variant-position': 'normal | sub | super', 'font-variant-position': 'normal | sub | super',
'font-variation-settings': 'normal | [ <string> <number>]#',
'font-weight': '<font-weight>', 'font-weight': '<font-weight>',
'-ms-flex-align': 'start | end | center | stretch | baseline', '-ms-flex-align': 'start | end | center | stretch | baseline',
'-ms-flex-order': '<number>', '-ms-flex-order': '<number>',
@ -392,7 +401,7 @@ self.parserlib = (() => {
'grid-gap': '<row-gap> <column-gap>?', 'grid-gap': '<row-gap> <column-gap>?',
// H // H
'hanging-punctuation': 1, 'hanging-punctuation': 'none | [ first || [ force-end | allow-end ] || last ]',
'height': 'auto | <width-height>', 'height': 'auto | <width-height>',
'hyphenate-after': '<integer> | auto', 'hyphenate-after': '<integer> | auto',
'hyphenate-before': '<integer> | auto', 'hyphenate-before': '<integer> | auto',
@ -431,7 +440,7 @@ self.parserlib = (() => {
'left': '<width>', 'left': '<width>',
'letter-spacing': '<length> | normal', 'letter-spacing': '<length> | normal',
'line-height': '<line-height>', 'line-height': '<line-height>',
'line-break': 'auto | loose | normal | strict', 'line-break': 'auto | loose | normal | strict | anywhere',
'line-stacking': 1, 'line-stacking': 1,
'line-stacking-ruby': 'exclude-ruby | include-ruby', 'line-stacking-ruby': 'exclude-ruby | include-ruby',
'line-stacking-shift': 'consider-shifts | disregard-shifts', 'line-stacking-shift': 'consider-shifts | disregard-shifts',
@ -502,9 +511,14 @@ self.parserlib = (() => {
'overflow-block': '<overflow>', 'overflow-block': '<overflow>',
'overflow-inline': '<overflow>', 'overflow-inline': '<overflow>',
'overflow-style': 1, 'overflow-style': 1,
'overflow-wrap': 'normal | break-word', 'overflow-wrap': 'normal | break-word | anywhere',
'overflow-x': '<overflow>', 'overflow-x': '<overflow>',
'overflow-y': '<overflow>', 'overflow-y': '<overflow>',
'overscroll-behavior': '<overscroll>{1,2}',
'overscroll-behavior-block': '<overscroll>',
'overscroll-behavior-inline': '<overscroll>',
'overscroll-behavior-x': '<overscroll>',
'overscroll-behavior-y': '<overscroll>',
// P // P
'padding': '<padding-width>{1,4}', 'padding': '<padding-width>{1,4}',
@ -564,6 +578,8 @@ self.parserlib = (() => {
// S // S
'scale': 'none | <number>{1,3}', 'scale': 'none | <number>{1,3}',
'scrollbar-color': 'auto | dark | light | <color>{2}',
'scrollbar-width': 'auto | thin | none',
'shape-inside': 'auto | outside-shape | [ <basic-shape> || shape-box ] | <image> | display', 'shape-inside': 'auto | outside-shape | [ <basic-shape> || shape-box ] | <image> | display',
'shape-rendering': 'auto | optimizeSpeed | crispEdges | geometricPrecision', 'shape-rendering': 'auto | optimizeSpeed | crispEdges | geometricPrecision',
'size': 1, 'size': 1,
@ -588,14 +604,14 @@ self.parserlib = (() => {
// T // T
'table-layout': 'auto | fixed', 'table-layout': 'auto | fixed',
'tab-size': '<integer> | <length>', 'tab-size': '<number> | <length>',
'target': 1, 'target': 1,
'target-name': 1, 'target-name': 1,
'target-new': 1, 'target-new': 1,
'target-position': 1, 'target-position': 1,
'text-align': 'start | end | left | right | center | justify | match-parent | justify-all', 'text-align': '<text-align> | justify-all',
'text-align-all': 'start | end | left | right | center | justify | match-parent', 'text-align-all': '<text-align>',
'text-align-last': 'auto | start | end | left | right | center | justify', 'text-align-last': '<text-align> | auto',
'text-anchor': 'start | middle | end', 'text-anchor': 'start | middle | end',
'text-decoration': '<text-decoration-line> || <text-decoration-style> || <color>', 'text-decoration': '<text-decoration-line> || <text-decoration-style> || <color>',
'text-decoration-color': '<color>', 'text-decoration-color': '<color>',
@ -607,13 +623,13 @@ self.parserlib = (() => {
'text-emphasis-style': '<text-emphasis-style>', 'text-emphasis-style': '<text-emphasis-style>',
'text-emphasis-position': '[ over | under ] && [ right | left ]?', 'text-emphasis-position': '[ over | under ] && [ right | left ]?',
'text-height': 1, 'text-height': 1,
'text-indent': '<length> | <percentage>', 'text-indent': '<length-percentage> && hanging? && each-line?',
'text-justify': 'auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida', 'text-justify': 'auto | none | inter-word | inter-character',
'text-outline': 1, 'text-outline': 1,
'text-overflow': 'clip | ellipsis', 'text-overflow': 'clip | ellipsis',
'text-rendering': 'auto | optimizeSpeed | optimizeLegibility | geometricPrecision', 'text-rendering': 'auto | optimizeSpeed | optimizeLegibility | geometricPrecision',
'text-shadow': 'none | [ <color>? && <length>{2,3} ]#', 'text-shadow': 'none | [ <color>? && <length>{2,3} ]#',
'text-transform': 'capitalize | uppercase | lowercase | none', 'text-transform': 'none | [ capitalize | uppercase | lowercase ] || full-width || full-size-kana',
'text-underline-position': 'auto | [ under || [ left | right ] ]', 'text-underline-position': 'auto | [ under || [ left | right ] ]',
'text-wrap': 'normal | none | avoid', 'text-wrap': 'normal | none | avoid',
'top': '<width>', 'top': '<width>',
@ -650,15 +666,14 @@ self.parserlib = (() => {
'volume': 1, 'volume': 1,
// W // W
'white-space': 'normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap |' + 'white-space': 'normal | pre | nowrap | pre-wrap | break-spaces | pre-line',
' -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap',
'white-space-collapse': 1, 'white-space-collapse': 1,
'widows': '<integer>', 'widows': '<integer>',
'width': 'auto | <width-height>', 'width': 'auto | <width-height>',
'will-change': '<will-change>', 'will-change': '<will-change>',
'word-break': 'normal | keep-all | break-all', 'word-break': 'normal | keep-all | break-all | break-word',
'word-spacing': '<length> | normal', 'word-spacing': '<length> | normal',
'word-wrap': 'normal | break-word', 'word-wrap': 'normal | break-word | anywhere',
'writing-mode': 'horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | ' + 'writing-mode': 'horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | ' +
'bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb', 'bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb',
@ -711,7 +726,7 @@ self.parserlib = (() => {
'<clip-source>': '<uri>', '<clip-source>': '<uri>',
'<column-gap>': 'normal | <length> | <percentage>', '<column-gap>': 'normal | <length-percentage>',
'<content-distribution>': 'space-between | space-around | space-evenly | stretch', '<content-distribution>': 'space-between | space-around | space-evenly | stretch',
'<content-position>': 'center | start | end | flex-start | flex-end', '<content-position>': 'center | start | end | flex-start | flex-end',
@ -747,17 +762,17 @@ self.parserlib = (() => {
'<flex-shrink>': '<number>', '<flex-shrink>': '<number>',
'<flex-wrap>': 'nowrap | wrap | wrap-reverse', '<flex-wrap>': 'nowrap | wrap | wrap-reverse',
'<font-size>': '<absolute-size> | <relative-size> | <length> | <percentage>', '<font-size>': '<absolute-size> | <relative-size> | <length-percentage>',
'<font-stretch>': 'normal | ultra-condensed | extra-condensed | condensed | semi-condensed | ' + '<font-stretch>': 'normal | <percentage> | ultra-condensed | extra-condensed | condensed | semi-condensed | ' +
'semi-expanded | expanded | extra-expanded | ultra-expanded', 'semi-expanded | expanded | extra-expanded | ultra-expanded',
'<font-style>': 'normal | italic | oblique', '<font-style>': 'normal | italic | oblique <angle>?',
'<font-variant-caps>': 'small-caps | all-small-caps | petite-caps | all-petite-caps | ' + '<font-variant-caps>': 'small-caps | all-small-caps | petite-caps | all-petite-caps | ' +
'unicase | titling-caps', 'unicase | titling-caps',
'<font-variant-css21>': 'normal | small-caps', '<font-variant-css21>': 'normal | small-caps',
'<font-weight>': 'normal | bold | bolder | lighter | ' + '<font-weight>': 'normal | bold | bolder | lighter | <number>',
'100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900',
'<generic-family>': 'serif | sans-serif | cursive | fantasy | monospace', '<generic-family>': 'serif | sans-serif | cursive | fantasy | monospace | system-ui | emoji | ' +
'math | fangsong | ui-serif | ui-sans-serif | ui-monospace | ui-rounded',
'<geometry-box>': '<shape-box> | fill-box | stroke-box | view-box', '<geometry-box>': '<shape-box> | fill-box | stroke-box | view-box',
@ -798,7 +813,7 @@ self.parserlib = (() => {
'<line>': part => part.type === 'integer', '<line>': part => part.type === 'integer',
'<line-height>': '<number> | <length> | <percentage> | normal', '<line-height>': '<number> | <length-percentage> | normal',
'<line-names>': function (part) { '<line-names>': function (part) {
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
@ -868,6 +883,8 @@ self.parserlib = (() => {
'<string>': part => part.type === 'string', '<string>': part => part.type === 'string',
'<text-align>': 'start | end | left | right | center | justify | match-parent',
'<text-decoration-style>': 'solid | double | dotted | dashed | wavy', '<text-decoration-style>': 'solid | double | dotted | dashed | wavy',
'<time>': part => part.type === 'time', '<time>': part => part.type === 'time',
@ -901,7 +918,7 @@ self.parserlib = (() => {
); );
}, },
'<width>': '<length> | <percentage> | auto', '<width>': '<length-percentage> | auto',
}, },
complex: { complex: {
@ -929,7 +946,7 @@ self.parserlib = (() => {
'[ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ] | ' + '[ left | center | right | <length-percentage> ] [ top | center | bottom | <length-percentage> ] | ' +
'[ left | center | right | top | bottom | <length-percentage> ]', '[ left | center | right | top | bottom | <length-percentage> ]',
'<bg-size>': '[ <length> | <percentage> | auto ]{1,2} | cover | contain', '<bg-size>': '[ <length-percentage> | auto ]{1,2} | cover | contain',
'<border-image-outset>': '[ <length> | <number> ]{1,4}', '<border-image-outset>': '[ <length> | <number> ]{1,4}',
'<border-image-repeat>': '[ stretch | repeat | round | space ]{1,2}', '<border-image-repeat>': '[ stretch | repeat | round | space ]{1,2}',
@ -943,7 +960,7 @@ self.parserlib = (() => {
Matcher.cast('<nonnegative-number-or-percentage>'), Matcher.cast('<nonnegative-number-or-percentage>'),
Matcher.cast('<nonnegative-number-or-percentage>'), Matcher.cast('<nonnegative-number-or-percentage>'),
'fill'), 'fill'),
'<border-image-width>': '[ <length> | <percentage> | <number> | auto ]{1,4}', '<border-image-width>': '[ <length-percentage> | <number> | auto ]{1,4}',
'<border-radius>': '<nonnegative-length-or-percentage>{1,4} [ / <nonnegative-length-or-percentage>{1,4} ]?', '<border-radius>': '<nonnegative-length-or-percentage>{1,4} [ / <nonnegative-length-or-percentage>{1,4} ]?',
'<border-shorthand>': '<border-width> || <border-style> || <color>', '<border-shorthand>': '<border-width> || <border-style> || <color>',
@ -1050,6 +1067,8 @@ self.parserlib = (() => {
'<hsl-color>': '[ <number> | <angle> ] <percentage>{2} [ / <nonnegative-number-or-percentage> ]? | ' + '<hsl-color>': '[ <number> | <angle> ] <percentage>{2} [ / <nonnegative-number-or-percentage> ]? | ' +
'[ <number> | <angle> ] , <percentage>#{2} [ , <nonnegative-number-or-percentage> ]?', '[ <number> | <angle> ] , <percentage>#{2} [ , <nonnegative-number-or-percentage> ]?',
'<overscroll>': 'contain | none | auto',
'<shadow>': 'inset? && [ <length>{2,4} && <color>? ]', '<shadow>': 'inset? && [ <length>{2,4} && <color>? ]',
'<single-timing-function>': 'linear | <cubic-bezier-timing-function> | <step-timing-function> | frames()', '<single-timing-function>': 'linear | <cubic-bezier-timing-function> | <step-timing-function> | frames()',
@ -1076,7 +1095,7 @@ self.parserlib = (() => {
'<will-change>': 'auto | <animateable-feature>#', '<will-change>': 'auto | <animateable-feature>#',
'<x-one-radius>': '[ <length> | <percentage> ]{1,2}', '<x-one-radius>': '<length-percentage>{1,2}',
}, },
functions: { functions: {
@ -1345,165 +1364,154 @@ self.parserlib = (() => {
* The following token names are defined in CSS3 Grammar: * The following token names are defined in CSS3 Grammar:
* https://www.w3.org/TR/css3-syntax/#lexical * https://www.w3.org/TR/css3-syntax/#lexical
*/ */
const Tokens = [ const Tokens = Object.assign([], {
EOF: {}, // must be the first token
}, {
// HTML-style comments // HTML-style comments
{name: 'CDO'}, CDO: {},
{name: 'CDC'}, CDC: {},
// ignorables // ignorables
{name: 'S', whitespace: true}, S: {whitespace: true},
{name: 'COMMENT', whitespace: true, comment: true, hide: true}, COMMENT: {whitespace: true, comment: true, hide: true},
// attribute equality // attribute equality
{name: 'INCLUDES', text: '~='}, INCLUDES: {text: '~='},
{name: 'DASHMATCH', text: '|='}, DASHMATCH: {text: '|='},
{name: 'PREFIXMATCH', text: '^='}, PREFIXMATCH: {text: '^='},
{name: 'SUFFIXMATCH', text: '$='}, SUFFIXMATCH: {text: '$='},
{name: 'SUBSTRINGMATCH', text: '*='}, SUBSTRINGMATCH: {text: '*='},
// identifier types // identifier types
{name: 'STRING'}, STRING: {},
{name: 'IDENT'}, IDENT: {},
{name: 'HASH'}, HASH: {},
// at-keywords // at-keywords
{name: 'IMPORT_SYM', text: '@import'}, IMPORT_SYM: {text: '@import'},
{name: 'PAGE_SYM', text: '@page'}, PAGE_SYM: {text: '@page'},
{name: 'MEDIA_SYM', text: '@media'}, MEDIA_SYM: {text: '@media'},
{name: 'FONT_FACE_SYM', text: '@font-face'}, FONT_FACE_SYM: {text: '@font-face'},
{name: 'CHARSET_SYM', text: '@charset'}, CHARSET_SYM: {text: '@charset'},
{name: 'NAMESPACE_SYM', text: '@namespace'}, NAMESPACE_SYM: {text: '@namespace'},
{name: 'SUPPORTS_SYM', text: '@supports'}, SUPPORTS_SYM: {text: '@supports'},
{name: 'VIEWPORT_SYM', text: ['@viewport', '@-ms-viewport', '@-o-viewport']}, VIEWPORT_SYM: {text: ['@viewport', '@-ms-viewport', '@-o-viewport']},
{name: 'DOCUMENT_SYM', text: ['@document', '@-moz-document']}, DOCUMENT_SYM: {text: ['@document', '@-moz-document']},
{name: 'UNKNOWN_SYM'}, //{ name: "ATKEYWORD"}, UNKNOWN_SYM: {}, //{ name: "ATKEYWORD"},
// CSS3 animations // CSS3 animations
{name: 'KEYFRAMES_SYM', text: ['@keyframes', '@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes']}, KEYFRAMES_SYM: {text: ['@keyframes', '@-webkit-keyframes', '@-moz-keyframes', '@-o-keyframes']},
// important symbol // important symbol
{name: 'IMPORTANT_SYM'}, IMPORTANT_SYM: {},
// measurements // measurements
{name: 'LENGTH'}, LENGTH: {},
{name: 'ANGLE'}, ANGLE: {},
{name: 'TIME'}, TIME: {},
{name: 'FREQ'}, FREQ: {},
{name: 'DIMENSION'}, DIMENSION: {},
{name: 'PERCENTAGE'}, PERCENTAGE: {},
{name: 'NUMBER'}, NUMBER: {},
// functions // functions
{name: 'URI'}, URI: {},
{name: 'FUNCTION'}, FUNCTION: {},
// Unicode ranges // Unicode ranges
{name: 'UNICODE_RANGE'}, UNICODE_RANGE: {},
/* /*
* The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
*/ */
// invalid string // invalid string
{name: 'INVALID'}, INVALID: {},
// combinators // combinators
{name: 'PLUS', text: '+'}, PLUS: {text: '+'},
{name: 'GREATER', text: '>'}, GREATER: {text: '>'},
{name: 'COMMA', text: ','}, COMMA: {text: ','},
{name: 'TILDE', text: '~'}, TILDE: {text: '~'},
COLUMN: {text: '||'},
// modifier // modifier
{name: 'NOT'}, NOT: {},
{name: 'ANY', text: ['any', '-webkit-any', '-moz-any']}, ANY: {text: ['any', '-webkit-any', '-moz-any']},
{name: 'MATCHES'}, IS: {},
{name: 'IS'}, WHERE: {},
/* /*
* Defined in CSS3 Paged Media * Defined in CSS3 Paged Media
*/ */
{name: 'TOPLEFTCORNER_SYM', text: '@top-left-corner'}, TOPLEFTCORNER_SYM: {text: '@top-left-corner'},
{name: 'TOPLEFT_SYM', text: '@top-left'}, TOPLEFT_SYM: {text: '@top-left'},
{name: 'TOPCENTER_SYM', text: '@top-center'}, TOPCENTER_SYM: {text: '@top-center'},
{name: 'TOPRIGHT_SYM', text: '@top-right'}, TOPRIGHT_SYM: {text: '@top-right'},
{name: 'TOPRIGHTCORNER_SYM', text: '@top-right-corner'}, TOPRIGHTCORNER_SYM: {text: '@top-right-corner'},
{name: 'BOTTOMLEFTCORNER_SYM', text: '@bottom-left-corner'}, BOTTOMLEFTCORNER_SYM: {text: '@bottom-left-corner'},
{name: 'BOTTOMLEFT_SYM', text: '@bottom-left'}, BOTTOMLEFT_SYM: {text: '@bottom-left'},
{name: 'BOTTOMCENTER_SYM', text: '@bottom-center'}, BOTTOMCENTER_SYM: {text: '@bottom-center'},
{name: 'BOTTOMRIGHT_SYM', text: '@bottom-right'}, BOTTOMRIGHT_SYM: {text: '@bottom-right'},
{name: 'BOTTOMRIGHTCORNER_SYM', text: '@bottom-right-corner'}, BOTTOMRIGHTCORNER_SYM: {text: '@bottom-right-corner'},
{name: 'LEFTTOP_SYM', text: '@left-top'}, LEFTTOP_SYM: {text: '@left-top'},
{name: 'LEFTMIDDLE_SYM', text: '@left-middle'}, LEFTMIDDLE_SYM: {text: '@left-middle'},
{name: 'LEFTBOTTOM_SYM', text: '@left-bottom'}, LEFTBOTTOM_SYM: {text: '@left-bottom'},
{name: 'RIGHTTOP_SYM', text: '@right-top'}, RIGHTTOP_SYM: {text: '@right-top'},
{name: 'RIGHTMIDDLE_SYM', text: '@right-middle'}, RIGHTMIDDLE_SYM: {text: '@right-middle'},
{name: 'RIGHTBOTTOM_SYM', text: '@right-bottom'}, RIGHTBOTTOM_SYM: {text: '@right-bottom'},
/* /*
* The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
*/ */
{name: 'RESOLUTION', state: 'media'}, RESOLUTION: {state: 'media'},
/* /*
* The following token names are not defined in any CSS specification but are used by the lexer. * The following token names are not defined in any CSS specification but are used by the lexer.
*/ */
// not a real token, but useful for stupid IE filters // not a real token, but useful for stupid IE filters
{name: 'IE_FUNCTION'}, IE_FUNCTION: {},
// part of CSS3 grammar but not the Flex code // part of CSS3 grammar but not the Flex code
{name: 'CHAR'}, CHAR: {},
// TODO: Needed?
// Not defined as tokens, but might as well be // Not defined as tokens, but might as well be
{name: 'PIPE', text: '|'}, PIPE: {text: '|'},
{name: 'SLASH', text: '/'}, SLASH: {text: '/'},
{name: 'MINUS', text: '-'}, MINUS: {text: '-'},
{name: 'STAR', text: '*'}, STAR: {text: '*'},
{name: 'LBRACE', text: '{', endChar: '}'}, LBRACE: {text: '{', endChar: '}'},
{name: 'RBRACE', text: '}'}, RBRACE: {text: '}'},
{name: 'LBRACKET', text: '[', endChar: ']'}, LBRACKET: {text: '[', endChar: ']'},
{name: 'RBRACKET', text: ']'}, RBRACKET: {text: ']'},
{name: 'EQUALS', text: '='}, EQUALS: {text: '='},
{name: 'COLON', text: ':'}, COLON: {text: ':'},
{name: 'SEMICOLON', text: ';'}, SEMICOLON: {text: ';'},
{name: 'LPAREN', text: '(', endChar: ')'}, LPAREN: {text: '(', endChar: ')'},
{name: 'RPAREN', text: ')'}, RPAREN: {text: ')'},
{name: 'DOT', text: '.'}, DOT: {text: '.'},
{name: 'USO_VAR', comment: true}, USO_VAR: {comment: true},
{name: 'CUSTOM_PROP'}, CUSTOM_PROP: {},
]; });
// make Tokens an array of tokens, store the index in original prop, add 'name' to each token
{ const typeMap = new Map();
Tokens.UNKNOWN = -1; for (const [k, val] of Object.entries(Tokens)) {
Tokens.unshift({name: 'EOF'}); const index = Tokens[k] = Tokens.length;
val.name = k;
const nameMap = []; Tokens.push(val);
const typeMap = new Map(); const {text} = val;
for (let i = 0, len = Tokens.length; i < len; i++) { if (text) {
nameMap.push(Tokens[i].name); for (const item of Array.isArray(text) ? text : [text]) {
Tokens[Tokens[i].name] = i; typeMap.set(item, index);
if (Tokens[i].text) {
if (Tokens[i].text instanceof Array) {
for (let j = 0; j < Tokens[i].text.length; j++) {
typeMap.set(Tokens[i].text[j], i);
}
} else {
typeMap.set(Tokens[i].text, i);
}
} }
} }
Tokens.name = function (tt) {
return nameMap[tt];
};
Tokens.type = function (c) {
return typeMap.get(c) || -1;
};
} }
Tokens.UNKNOWN = -1;
Tokens.name = index => (Tokens[index] || {}).name;
Tokens.type = text => typeMap.get(text) || -1;
//endregion //endregion
//region lowerCase helper //region lowerCase helper
@ -2204,6 +2212,7 @@ self.parserlib = (() => {
value === '>' ? 'child' : value === '>' ? 'child' :
value === '+' ? 'adjacent-sibling' : value === '+' ? 'adjacent-sibling' :
value === '~' ? 'sibling' : value === '~' ? 'sibling' :
value === '||' ? 'column' :
!value.trim() ? 'descendant' : !value.trim() ? 'descendant' :
'unknown'; 'unknown';
} }
@ -2603,9 +2612,11 @@ self.parserlib = (() => {
if (expression._i < expression._parts.length - 1) { if (expression._i < expression._parts.length - 1) {
expression.mark(); expression.mark();
expression._i++; expression._i++;
result = ValidationTypes.isType(expression, type); if (ValidationTypes.isType(expression, type)) {
expression.restore(); expression.drop();
expression._i += result ? 1 : 0; } else {
expression.restore();
}
} }
result = true; result = true;
@ -2940,6 +2951,7 @@ self.parserlib = (() => {
* - PREFIXMATCH * - PREFIXMATCH
* - SUFFIXMATCH * - SUFFIXMATCH
* - SUBSTRINGMATCH * - SUBSTRINGMATCH
* - COLUMN
* - CHAR * - CHAR
*/ */
case '|': case '|':
@ -2947,10 +2959,13 @@ self.parserlib = (() => {
case '^': case '^':
case '$': case '$':
case '*': case '*':
return reader.peek() === '=' ? return (
this.comparisonToken(c, pos) : reader.peek() === '=' ?
this.charToken(c, pos); this.comparisonToken(c, pos) :
c === '|' && reader.readMatch('|') ?
this.createToken(Tokens.COLUMN, '||', pos) :
this.charToken(c, pos)
);
/* /*
* Potential tokens: * Potential tokens:
* - STRING * - STRING
@ -3040,8 +3055,8 @@ self.parserlib = (() => {
* Potential tokens: * Potential tokens:
* - ANY * - ANY
* - IS * - IS
* - MATCHES
* - NOT * - NOT
* - WHERE
* - CHAR * - CHAR
*/ */
case ':': case ':':
@ -3255,18 +3270,18 @@ self.parserlib = (() => {
// NOT // NOT
// IS // IS
// ANY // ANY
// MATCHES
// CHAR // CHAR
notOrIsToken(first, pos) { notOrIsToken(first, pos) {
// first is always ':' // first is always ':'
const reader = this._reader; const reader = this._reader;
const func = reader.readMatch(/(not|is|(-(moz|webkit)-)?(any|matches))\(/iy); const func = reader.readMatch(/(not|is|where|(-(moz|webkit)-)?any)\(/iy);
if (func) { if (func) {
const lcase = func[0].toLowerCase(); const lcase = func[0].toLowerCase();
const type = const type =
lcase === 'n' ? Tokens.NOT : lcase === 'n' ? Tokens.NOT :
lcase === 'i' ? Tokens.IS : lcase === 'i' ? Tokens.IS :
lcase === 'm' ? Tokens.MATCHES : Tokens.ANY; lcase === 'w' ? Tokens.WHERE :
Tokens.ANY;
return this.createToken(type, first + func, pos); return this.createToken(type, first + func, pos);
} }
return this.charToken(first, pos); return this.charToken(first, pos);
@ -4017,7 +4032,7 @@ self.parserlib = (() => {
const stream = this._tokenStream; const stream = this._tokenStream;
if (stream.match(Tokens.LPAREN)) { if (stream.match(Tokens.LPAREN)) {
this._ws(); this._ws();
if (stream.match(Tokens.IDENT)) { if (stream.match([Tokens.IDENT, Tokens.CUSTOM_PROP])) {
// look ahead for not keyword, // look ahead for not keyword,
// if not given, continue with declaration condition. // if not given, continue with declaration condition.
const ident = lower(stream._token.value); const ident = lower(stream._token.value);
@ -4366,7 +4381,7 @@ self.parserlib = (() => {
} }
_combinator() { _combinator() {
if (this._tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) { if (this._tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE, Tokens.COLUMN])) {
const value = new Combinator(this._tokenStream._token); const value = new Combinator(this._tokenStream._token);
this._ws(); this._ws();
return value; return value;
@ -4630,7 +4645,8 @@ self.parserlib = (() => {
this._ws(); this._ws();
if (stream.match([Tokens.IDENT])) { if (stream.match([Tokens.IDENT])) {
if (lower(stream._token.value) === 'i') { const caseMod = lower(stream._token.value);
if (caseMod === 'i' || caseMod === 's') {
value += stream._token.value + value += stream._token.value +
this._ws(); this._ws();
} else { } else {
@ -4710,7 +4726,7 @@ self.parserlib = (() => {
_is() { _is() {
const stream = this._tokenStream; const stream = this._tokenStream;
if (!stream.match([Tokens.IS, Tokens.ANY, Tokens.MATCHES])) return null; if (!stream.match([Tokens.IS, Tokens.ANY, Tokens.WHERE])) return null;
let arg; let arg;
const start = stream._token; const start = stream._token;
@ -5421,6 +5437,7 @@ self.parserlib = (() => {
]), ]),
supports: new Map([ supports: new Map([
[Tokens.KEYFRAMES_SYM, Parser.prototype._keyframes],
[Tokens.MEDIA_SYM, Parser.prototype._media], [Tokens.MEDIA_SYM, Parser.prototype._media],
[Tokens.SUPPORTS_SYM, Parser.prototype._supports], [Tokens.SUPPORTS_SYM, Parser.prototype._supports],
[Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced], [Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced],
@ -5428,6 +5445,7 @@ self.parserlib = (() => {
]), ]),
media: new Map([ media: new Map([
[Tokens.KEYFRAMES_SYM, Parser.prototype._keyframes],
[Tokens.MEDIA_SYM, Parser.prototype._media], [Tokens.MEDIA_SYM, Parser.prototype._media],
[Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced], [Tokens.DOCUMENT_SYM, Parser.prototype._documentMisplaced],
[Tokens.SUPPORTS_SYM, Parser.prototype._supports], [Tokens.SUPPORTS_SYM, Parser.prototype._supports],
@ -5443,7 +5461,7 @@ self.parserlib = (() => {
[Tokens.COLON, Parser.prototype._pseudo], [Tokens.COLON, Parser.prototype._pseudo],
[Tokens.IS, Parser.prototype._is], [Tokens.IS, Parser.prototype._is],
[Tokens.ANY, Parser.prototype._is], [Tokens.ANY, Parser.prototype._is],
[Tokens.MATCHES, Parser.prototype._is], [Tokens.WHERE, Parser.prototype._is],
[Tokens.NOT, Parser.prototype._negation], [Tokens.NOT, Parser.prototype._negation],
]), ]),
}; };

View File

@ -1,4 +1,4 @@
## codemirror v5.51.0 ## codemirror v5.56.0
Following files are copied from npm (node_modules): Following files are copied from npm (node_modules):

View File

@ -82,7 +82,9 @@
if (e.keyCode == 13) callback(inp.value, e); if (e.keyCode == 13) callback(inp.value, e);
}); });
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close); if (options.closeOnBlur !== false) CodeMirror.on(dialog, "focusout", function (evt) {
if (evt.relatedTarget !== null) close();
});
} else if (button = dialog.getElementsByTagName("button")[0]) { } else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() { CodeMirror.on(button, "click", function() {
close(); close();

View File

@ -118,16 +118,24 @@
} }
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) { function clear(cm) {
cm.off("cursorActivity", doMatchBrackets);
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
cm.state.matchBrackets.currentlyHighlighted(); cm.state.matchBrackets.currentlyHighlighted();
cm.state.matchBrackets.currentlyHighlighted = null; cm.state.matchBrackets.currentlyHighlighted = null;
} }
} }
if (old && old != CodeMirror.Init) {
cm.off("cursorActivity", doMatchBrackets);
cm.off("focus", doMatchBrackets)
cm.off("blur", clear)
clear(cm);
}
if (val) { if (val) {
cm.state.matchBrackets = typeof val == "object" ? val : {}; cm.state.matchBrackets = typeof val == "object" ? val : {};
cm.on("cursorActivity", doMatchBrackets); cm.on("cursorActivity", doMatchBrackets);
cm.on("focus", doMatchBrackets)
cm.on("blur", clear)
} }
}); });

View File

@ -85,11 +85,16 @@
}, },
pick: function(data, i) { pick: function(data, i) {
var completion = data.list[i]; var completion = data.list[i], self = this;
if (completion.hint) completion.hint(this.cm, data, completion); this.cm.operation(function() {
else this.cm.replaceRange(getText(completion), completion.from || data.from, if (completion.hint)
completion.to || data.to, "complete"); completion.hint(self.cm, data, completion);
CodeMirror.signal(data, "pick", completion); else
self.cm.replaceRange(getText(completion), completion.from || data.from,
completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion);
self.cm.scrollIntoView();
})
this.close(); this.close();
}, },
@ -99,9 +104,14 @@
this.debounce = 0; this.debounce = 0;
} }
var identStart = this.startPos;
if(this.data) {
identStart = this.data.from;
}
var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
pos.ch < this.startPos.ch || this.cm.somethingSelected() || pos.ch < identStart.ch || this.cm.somethingSelected() ||
(!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
this.close(); this.close();
} else { } else {
@ -369,11 +379,14 @@
}, },
scrollToActive: function() { scrollToActive: function() {
var node = this.hints.childNodes[this.selectedHint] var margin = this.completion.options.scrollMargin || 0;
if (node.offsetTop < this.hints.scrollTop) var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
this.hints.scrollTop = node.offsetTop - 3; var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) var firstNode = this.hints.firstChild;
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; if (node1.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop;
else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop;
}, },
screenAmount: function() { screenAmount: function() {

View File

@ -12,11 +12,14 @@
"use strict"; "use strict";
var GUTTER_ID = "CodeMirror-lint-markers"; var GUTTER_ID = "CodeMirror-lint-markers";
function showTooltip(e, content) { function showTooltip(cm, e, content) {
var tt = document.createElement("div"); var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip"; tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
tt.appendChild(content.cloneNode(true)); tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt); if (cm.state.lint.options.selfContain)
cm.getWrapperElement().appendChild(tt);
else
document.body.appendChild(tt);
function position(e) { function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
@ -38,8 +41,8 @@
setTimeout(function() { rm(tt); }, 600); setTimeout(function() { rm(tt); }, 600);
} }
function showTooltipFor(e, content, node) { function showTooltipFor(cm, e, content, node) {
var tooltip = showTooltip(e, content); var tooltip = showTooltip(cm, e, content);
function hide() { function hide() {
CodeMirror.off(node, "mouseout", hide); CodeMirror.off(node, "mouseout", hide);
if (tooltip) { hideTooltip(tooltip); tooltip = null; } if (tooltip) { hideTooltip(tooltip); tooltip = null; }
@ -78,7 +81,7 @@
state.marked.length = 0; state.marked.length = 0;
} }
function makeMarker(labels, severity, multiple, tooltips) { function makeMarker(cm, labels, severity, multiple, tooltips) {
var marker = document.createElement("div"), inner = marker; var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity; marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) { if (multiple) {
@ -87,7 +90,7 @@
} }
if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
showTooltipFor(e, labels, inner); showTooltipFor(cm, e, labels, inner);
}); });
return marker; return marker;
@ -113,9 +116,9 @@
var tip = document.createElement("div"); var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity; tip.className = "CodeMirror-lint-message-" + severity;
if (typeof ann.messageHTML != 'undefined') { if (typeof ann.messageHTML != 'undefined') {
tip.innerHTML = ann.messageHTML; tip.innerHTML = ann.messageHTML;
} else { } else {
tip.appendChild(document.createTextNode(ann.message)); tip.appendChild(document.createTextNode(ann.message));
} }
return tip; return tip;
} }
@ -186,7 +189,7 @@
} }
if (state.hasGutter) if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, anns.length > 1,
state.options.tooltips)); state.options.tooltips));
} }
if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
@ -199,14 +202,14 @@
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
} }
function popupTooltips(annotations, e) { function popupTooltips(cm, annotations, e) {
var target = e.target || e.srcElement; var target = e.target || e.srcElement;
var tooltip = document.createDocumentFragment(); var tooltip = document.createDocumentFragment();
for (var i = 0; i < annotations.length; i++) { for (var i = 0; i < annotations.length; i++) {
var ann = annotations[i]; var ann = annotations[i];
tooltip.appendChild(annotationTooltip(ann)); tooltip.appendChild(annotationTooltip(ann));
} }
showTooltipFor(e, tooltip, target); showTooltipFor(cm, e, tooltip, target);
} }
function onMouseOver(cm, e) { function onMouseOver(cm, e) {
@ -220,7 +223,7 @@
var ann = spans[i].__annotation; var ann = spans[i].__annotation;
if (ann) annotations.push(ann); if (ann) annotations.push(ann);
} }
if (annotations.length) popupTooltips(annotations, e); if (annotations.length) popupTooltips(cm, annotations, e);
} }
CodeMirror.defineOption("lint", false, function(cm, val, old) { CodeMirror.defineOption("lint", false, function(cm, val, old) {

View File

@ -240,7 +240,7 @@
var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to)) var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
// Implements weird auto-growing behavior on null-matches for // Implements weird auto-growing behavior on null-matches for
// backwards-compatiblity with the vim code (unfortunately) // backwards-compatibility with the vim code (unfortunately)
while (result && CodeMirror.cmpPos(result.from, result.to) == 0) { while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
if (reverse) { if (reverse) {
if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1) if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)

View File

@ -404,7 +404,8 @@
"Ctrl-X H": "selectAll", "Ctrl-X H": "selectAll",
"Ctrl-Q Tab": repeated("insertTab"), "Ctrl-Q Tab": repeated("insertTab"),
"Ctrl-U": addPrefixMap "Ctrl-U": addPrefixMap,
"fallthrough": "default"
}); });
var prefixMap = {"Ctrl-G": clearPrefix}; var prefixMap = {"Ctrl-G": clearPrefix};

View File

@ -628,6 +628,7 @@
"Cmd-K Cmd-C": "showInCenter", "Cmd-K Cmd-C": "showInCenter",
"Cmd-K Cmd-G": "clearBookmarks", "Cmd-K Cmd-G": "clearBookmarks",
"Cmd-K Cmd-Backspace": "delLineLeft", "Cmd-K Cmd-Backspace": "delLineLeft",
"Cmd-K Cmd-1": "foldAll",
"Cmd-K Cmd-0": "unfoldAll", "Cmd-K Cmd-0": "unfoldAll",
"Cmd-K Cmd-J": "unfoldAll", "Cmd-K Cmd-J": "unfoldAll",
"Ctrl-Shift-Up": "addCursorToPrevLine", "Ctrl-Shift-Up": "addCursorToPrevLine",
@ -689,6 +690,7 @@
"Ctrl-K Ctrl-C": "showInCenter", "Ctrl-K Ctrl-C": "showInCenter",
"Ctrl-K Ctrl-G": "clearBookmarks", "Ctrl-K Ctrl-G": "clearBookmarks",
"Ctrl-K Ctrl-Backspace": "delLineLeft", "Ctrl-K Ctrl-Backspace": "delLineLeft",
"Ctrl-K Ctrl-1": "foldAll",
"Ctrl-K Ctrl-0": "unfoldAll", "Ctrl-K Ctrl-0": "unfoldAll",
"Ctrl-K Ctrl-J": "unfoldAll", "Ctrl-K Ctrl-J": "unfoldAll",
"Ctrl-Alt-Up": "addCursorToPrevLine", "Ctrl-Alt-Up": "addCursorToPrevLine",

View File

@ -234,7 +234,6 @@
{ name: 'undo', shortName: 'u' }, { name: 'undo', shortName: 'u' },
{ name: 'redo', shortName: 'red' }, { name: 'redo', shortName: 'red' },
{ name: 'set', shortName: 'se' }, { name: 'set', shortName: 'se' },
{ name: 'set', shortName: 'se' },
{ name: 'setlocal', shortName: 'setl' }, { name: 'setlocal', shortName: 'setl' },
{ name: 'setglobal', shortName: 'setg' }, { name: 'setglobal', shortName: 'setg' },
{ name: 'sort', shortName: 'sor' }, { name: 'sort', shortName: 'sor' },
@ -295,16 +294,16 @@
clearFatCursorMark(cm); clearFatCursorMark(cm);
var ranges = cm.listSelections(), result = [] var ranges = cm.listSelections(), result = []
for (var i = 0; i < ranges.length; i++) { for (var i = 0; i < ranges.length; i++) {
var range = ranges[i] var range = ranges[i];
if (range.empty()) { if (range.empty()) {
if (range.anchor.ch < cm.getLine(range.anchor.line).length) { var lineLength = cm.getLine(range.anchor.line).length;
if (range.anchor.ch < lineLength) {
result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1), result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1),
{className: "cm-fat-cursor-mark"})) {className: "cm-fat-cursor-mark"}));
} else { } else {
var widget = document.createElement("span") result.push(cm.markText(Pos(range.anchor.line, lineLength - 1),
widget.textContent = "\u00a0" Pos(range.anchor.line, lineLength),
widget.className = "cm-fat-cursor-mark" {className: "cm-fat-cursor-mark"}));
result.push(cm.setBookmark(range.anchor, {widget: widget}))
} }
} }
} }
@ -1603,10 +1602,10 @@
} }
if (vim.visualMode) { if (vim.visualMode) {
if (!(vim.visualBlock && newHead.ch === Infinity)) { if (!(vim.visualBlock && newHead.ch === Infinity)) {
newHead = clipCursorToContent(cm, newHead, vim.visualBlock); newHead = clipCursorToContent(cm, newHead);
} }
if (newAnchor) { if (newAnchor) {
newAnchor = clipCursorToContent(cm, newAnchor, true); newAnchor = clipCursorToContent(cm, newAnchor);
} }
newAnchor = newAnchor || oldAnchor; newAnchor = newAnchor || oldAnchor;
sel.anchor = newAnchor; sel.anchor = newAnchor;
@ -2200,8 +2199,7 @@
vimGlobalState.registerController.pushText( vimGlobalState.registerController.pushText(
args.registerName, 'delete', text, args.registerName, 'delete', text,
args.linewise, vim.visualBlock); args.linewise, vim.visualBlock);
var includeLineBreak = vim.insertMode return clipCursorToContent(cm, finalHead);
return clipCursorToContent(cm, finalHead, includeLineBreak);
}, },
indent: function(cm, args, ranges) { indent: function(cm, args, ranges) {
var vim = cm.state.vim; var vim = cm.state.vim;
@ -2460,8 +2458,7 @@
vim.visualLine = !!actionArgs.linewise; vim.visualLine = !!actionArgs.linewise;
vim.visualBlock = !!actionArgs.blockwise; vim.visualBlock = !!actionArgs.blockwise;
head = clipCursorToContent( head = clipCursorToContent(
cm, Pos(anchor.line, anchor.ch + repeat - 1), cm, Pos(anchor.line, anchor.ch + repeat - 1));
true /** includeLineBreak */);
vim.sel = { vim.sel = {
anchor: anchor, anchor: anchor,
head: head head: head
@ -2837,10 +2834,11 @@
* Clips cursor to ensure that line is within the buffer's range * Clips cursor to ensure that line is within the buffer's range
* If includeLineBreak is true, then allow cur.ch == lineLength. * If includeLineBreak is true, then allow cur.ch == lineLength.
*/ */
function clipCursorToContent(cm, cur, includeLineBreak) { function clipCursorToContent(cm, cur) {
var vim = cm.state.vim;
var includeLineBreak = vim.insertMode || vim.visualMode;
var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() ); var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
var maxCh = lineLength(cm, line) - 1; var maxCh = lineLength(cm, line) - 1 + !!includeLineBreak;
maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
var ch = Math.min(Math.max(0, cur.ch), maxCh); var ch = Math.min(Math.max(0, cur.ch), maxCh);
return Pos(line, ch); return Pos(line, ch);
} }
@ -3205,10 +3203,8 @@
vim.visualMode = false; vim.visualMode = false;
vim.visualLine = false; vim.visualLine = false;
vim.visualBlock = false; vim.visualBlock = false;
CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"}); if (!vim.insertMode) CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
if (vim.fakeCursor) { clearFakeCursor(vim);
vim.fakeCursor.clear();
}
} }
// Remove any trailing newlines from the selection. For // Remove any trailing newlines from the selection. For
@ -4186,7 +4182,8 @@
} }
function makePrompt(prefix, desc) { function makePrompt(prefix, desc) {
var raw = '<span style="font-family: monospace; white-space: pre">' + var raw = '<span style="font-family: monospace; white-space: pre">' +
(prefix || "") + '<input type="text"></span>'; (prefix || "") + '<input type="text" autocorrect="off" ' +
'autocapitalize="off" spellcheck="false"></span>';
if (desc) if (desc)
raw += ' <span style="color: #888">' + desc + '</span>'; raw += ' <span style="color: #888">' + desc + '</span>';
return raw; return raw;
@ -4459,7 +4456,7 @@
} }
// Parse command name. // Parse command name.
var commandMatch = inputStream.match(/^(\w+)/); var commandMatch = inputStream.match(/^(\w+|!!|@@|[!#&*<=>@~])/);
if (commandMatch) { if (commandMatch) {
result.commandName = commandMatch[1]; result.commandName = commandMatch[1];
} else { } else {
@ -5357,14 +5354,34 @@
updateFakeCursor(cm); updateFakeCursor(cm);
} }
} }
/**
* Keeps track of a fake cursor to support visual mode cursor behavior.
*/
function updateFakeCursor(cm) { function updateFakeCursor(cm) {
var className = 'cm-animate-fat-cursor';
var vim = cm.state.vim; var vim = cm.state.vim;
var from = clipCursorToContent(cm, copyCursor(vim.sel.head)); var from = clipCursorToContent(cm, copyCursor(vim.sel.head));
var to = offsetCursor(from, 0, 1); var to = offsetCursor(from, 0, 1);
clearFakeCursor(vim);
// In visual mode, the cursor may be positioned over EOL.
if (from.ch == cm.getLine(from.line).length) {
var widget = document.createElement("span");
widget.textContent = "\u00a0";
widget.className = className;
vim.fakeCursorBookmark = cm.setBookmark(from, {widget: widget});
} else {
vim.fakeCursor = cm.markText(from, to, {className: className});
}
}
function clearFakeCursor(vim) {
if (vim.fakeCursor) { if (vim.fakeCursor) {
vim.fakeCursor.clear(); vim.fakeCursor.clear();
vim.fakeCursor = null;
}
if (vim.fakeCursorBookmark) {
vim.fakeCursorBookmark.clear();
vim.fakeCursorBookmark = null;
} }
vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
} }
function handleExternalSelection(cm, vim) { function handleExternalSelection(cm, vim) {
var anchor = cm.getCursor('anchor'); var anchor = cm.getCursor('anchor');

View File

@ -164,17 +164,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-scroll { .CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */ overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */ /* 50px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */ /* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px; margin-bottom: -50px; margin-right: -50px;
padding-bottom: 30px; padding-bottom: 50px;
height: 100%; height: 100%;
outline: none; /* Prevent dragging from highlighting the element */ outline: none; /* Prevent dragging from highlighting the element */
position: relative; position: relative;
} }
.CodeMirror-sizer { .CodeMirror-sizer {
position: relative; position: relative;
border-right: 30px solid transparent; border-right: 50px solid transparent;
} }
/* The fake, visible scrollbars. Used to force redraw during scrolling /* The fake, visible scrollbars. Used to force redraw during scrolling
@ -212,7 +212,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
height: 100%; height: 100%;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
margin-bottom: -30px; margin-bottom: -50px;
} }
.CodeMirror-gutter-wrapper { .CodeMirror-gutter-wrapper {
position: absolute; position: absolute;

View File

@ -204,7 +204,7 @@
} }
// Number of pixels added to scroller and sizer to hide scrollbar // Number of pixels added to scroller and sizer to hide scrollbar
var scrollerGap = 30; var scrollerGap = 50;
// Returned or thrown by various protocols to signal 'I'm not // Returned or thrown by various protocols to signal 'I'm not
// handling this'. // handling this'.
@ -485,14 +485,15 @@
for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
order.push(new BidiSpan(0, start, i$7)); order.push(new BidiSpan(0, start, i$7));
} else { } else {
var pos = i$7, at = order.length; var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
for (var j$2 = pos; j$2 < i$7;) { for (var j$2 = pos; j$2 < i$7;) {
if (countsAsNum.test(types[j$2])) { if (countsAsNum.test(types[j$2])) {
if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
var nstart = j$2; var nstart = j$2;
for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
order.splice(at, 0, new BidiSpan(2, nstart, j$2)); order.splice(at, 0, new BidiSpan(2, nstart, j$2));
at += isRTL;
pos = j$2; pos = j$2;
} else { ++j$2; } } else { ++j$2; }
} }
@ -1200,7 +1201,7 @@
var prop = lineClass[1] ? "bgClass" : "textClass"; var prop = lineClass[1] ? "bgClass" : "textClass";
if (output[prop] == null) if (output[prop] == null)
{ output[prop] = lineClass[2]; } { output[prop] = lineClass[2]; }
else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
{ output[prop] += " " + lineClass[2]; } { output[prop] += " " + lineClass[2]; }
} } } }
return type return type
@ -2959,7 +2960,7 @@
var x, y, space = display.lineSpace.getBoundingClientRect(); var x, y, space = display.lineSpace.getBoundingClientRect();
// Fails unpredictably on IE[67] when mouse is dragged around quickly. // Fails unpredictably on IE[67] when mouse is dragged around quickly.
try { x = e.clientX - space.left; y = e.clientY - space.top; } try { x = e.clientX - space.left; y = e.clientY - space.top; }
catch (e) { return null } catch (e$1) { return null }
var coords = coordsChar(cm, x, y), line; var coords = coordsChar(cm, x, y), line;
if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
@ -3550,7 +3551,7 @@
} }
function setScrollTop(cm, val, forceScroll) { function setScrollTop(cm, val, forceScroll) {
val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
if (cm.display.scroller.scrollTop == val && !forceScroll) { return } if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
cm.doc.scrollTop = val; cm.doc.scrollTop = val;
cm.display.scrollbars.setScrollTop(val); cm.display.scrollbars.setScrollTop(val);
@ -3560,7 +3561,7 @@
// Sync scroller and scrollbar, ensure the gutter elements are // Sync scroller and scrollbar, ensure the gutter elements are
// aligned. // aligned.
function setScrollLeft(cm, val, isScroller, forceScroll) { function setScrollLeft(cm, val, isScroller, forceScroll) {
val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
cm.doc.scrollLeft = val; cm.doc.scrollLeft = val;
alignHorizontally(cm); alignHorizontally(cm);
@ -4049,7 +4050,8 @@
function restoreSelection(snapshot) { function restoreSelection(snapshot) {
if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
snapshot.activeElt.focus(); snapshot.activeElt.focus();
if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
var sel = window.getSelection(), range = document.createRange(); var sel = window.getSelection(), range = document.createRange();
range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
range.collapse(false); range.collapse(false);
@ -4147,6 +4149,8 @@
update.visible = visibleLines(cm.display, cm.doc, viewport); update.visible = visibleLines(cm.display, cm.doc, viewport);
if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
{ break } { break }
} else if (first) {
update.visible = visibleLines(cm.display, cm.doc, viewport);
} }
if (!updateDisplayIfNeeded(cm, update)) { break } if (!updateDisplayIfNeeded(cm, update)) { break }
updateHeightsInViewport(cm); updateHeightsInViewport(cm);
@ -6496,7 +6500,7 @@
text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
origin: "paste"}; origin: "paste"};
makeChange(cm.doc, change); makeChange(cm.doc, change);
setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
})(); })();
} }
}; };
@ -6541,7 +6545,7 @@
cm.display.input.focus(); cm.display.input.focus();
} }
} }
catch(e){} catch(e$1){}
} }
} }
@ -6829,7 +6833,7 @@
function endOfLine(visually, cm, lineObj, lineNo, dir) { function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) { if (visually) {
if (cm.getOption("direction") == "rtl") { dir = -dir; } if (cm.doc.direction == "rtl") { dir = -dir; }
var order = getOrder(lineObj, cm.doc.direction); var order = getOrder(lineObj, cm.doc.direction);
if (order) { if (order) {
var part = dir < 0 ? lst(order) : order[0]; var part = dir < 0 ? lst(order) : order[0];
@ -7084,7 +7088,7 @@
var line = getLine(cm.doc, start.line); var line = getLine(cm.doc, start.line);
var order = getOrder(line, cm.doc.direction); var order = getOrder(line, cm.doc.direction);
if (!order || order[0].level == 0) { if (!order || order[0].level == 0) {
var firstNonWS = Math.max(0, line.text.search(/\S/)); var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
} }
@ -7187,6 +7191,7 @@
var lastStoppedKey = null; var lastStoppedKey = null;
function onKeyDown(e) { function onKeyDown(e) {
var cm = this; var cm = this;
if (e.target && e.target != cm.display.input.getField()) { return }
cm.curOp.focus = activeElt(); cm.curOp.focus = activeElt();
if (signalDOMEvent(cm, e)) { return } if (signalDOMEvent(cm, e)) { return }
// IE does strange things with escape. // IE does strange things with escape.
@ -7230,6 +7235,7 @@
function onKeyPress(e) { function onKeyPress(e) {
var cm = this; var cm = this;
if (e.target && e.target != cm.display.input.getField()) { return }
if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
var keyCode = e.keyCode, charCode = e.charCode; var keyCode = e.keyCode, charCode = e.charCode;
if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
@ -7378,8 +7384,8 @@
if (!behavior.addNew) if (!behavior.addNew)
{ extendSelection(cm.doc, pos, null, null, behavior.extend); } { extendSelection(cm.doc, pos, null, null, behavior.extend); }
// Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
if (webkit || ie && ie_version == 9) if ((webkit && !safari) || ie && ie_version == 9)
{ setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
else else
{ display.input.focus(); } { display.input.focus(); }
} }
@ -7591,7 +7597,7 @@
mY = e.touches[0].clientY; mY = e.touches[0].clientY;
} else { } else {
try { mX = e.clientX; mY = e.clientY; } try { mX = e.clientX; mY = e.clientY; }
catch(e) { return false } catch(e$1) { return false }
} }
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
if (prevent) { e_preventDefault(e); } if (prevent) { e_preventDefault(e); }
@ -7691,7 +7697,7 @@
for (var i = newBreaks.length - 1; i >= 0; i--) for (var i = newBreaks.length - 1; i >= 0; i--)
{ replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
}); });
option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
if (old != Init) { cm.refresh(); } if (old != Init) { cm.refresh(); }
}); });
@ -7755,6 +7761,12 @@
} }
cm.display.input.readOnlyChanged(val); cm.display.input.readOnlyChanged(val);
}); });
option("screenReaderLabel", null, function (cm, val) {
val = (val === '') ? null : val;
cm.display.input.screenReaderLabelChanged(val);
});
option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
option("dragDrop", true, dragDropChanged); option("dragDrop", true, dragDropChanged);
option("allowDropFileTypes", null); option("allowDropFileTypes", null);
@ -8105,7 +8117,7 @@
{ from = Pos(from.line, from.ch - deleted); } { from = Pos(from.line, from.ch - deleted); }
else if (cm.state.overwrite && !paste) // Handle overwrite else if (cm.state.overwrite && !paste) // Handle overwrite
{ to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
{ from = to = Pos(from.line, 0); } { from = to = Pos(from.line, 0); }
} }
var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
@ -8586,7 +8598,7 @@
clearCaches(this); clearCaches(this);
scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
updateGutterSpace(this.display); updateGutterSpace(this.display);
if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
{ estimateLineHeights(this); } { estimateLineHeights(this); }
signal(this, "refresh", this); signal(this, "refresh", this);
}), }),
@ -8640,7 +8652,7 @@
var oldPos = pos; var oldPos = pos;
var origDir = dir; var origDir = dir;
var lineObj = getLine(doc, pos.line); var lineObj = getLine(doc, pos.line);
var lineDir = visually && doc.cm && doc.cm.getOption("direction") == "rtl" ? -dir : dir; var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
function findNextLine() { function findNextLine() {
var l = pos.line + lineDir; var l = pos.line + lineDir;
if (l < doc.first || l >= doc.first + doc.size) { return false } if (l < doc.first || l >= doc.first + doc.size) { return false }
@ -8735,8 +8747,16 @@
var div = input.div = display.lineDiv; var div = input.div = display.lineDiv;
disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
function belongsToInput(e) {
for (var t = e.target; t; t = t.parentNode) {
if (t == div) { return true }
if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
}
return false
}
on(div, "paste", function (e) { on(div, "paste", function (e) {
if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
// IE doesn't fire input events, so we schedule a read for the pasted content in this way // IE doesn't fire input events, so we schedule a read for the pasted content in this way
if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
}); });
@ -8761,7 +8781,7 @@
}); });
function onCopyCut(e) { function onCopyCut(e) {
if (signalDOMEvent(cm, e)) { return } if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
if (cm.somethingSelected()) { if (cm.somethingSelected()) {
setLastCopied({lineWise: false, text: cm.getSelections()}); setLastCopied({lineWise: false, text: cm.getSelections()});
if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
@ -8803,9 +8823,18 @@
on(div, "cut", onCopyCut); on(div, "cut", onCopyCut);
}; };
ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
// Label for screenreaders, accessibility
if(label) {
this.div.setAttribute('aria-label', label);
} else {
this.div.removeAttribute('aria-label');
}
};
ContentEditableInput.prototype.prepareSelection = function () { ContentEditableInput.prototype.prepareSelection = function () {
var result = prepareSelection(this.cm, false); var result = prepareSelection(this.cm, false);
result.focus = this.cm.state.focused; result.focus = document.activeElement == this.div;
return result return result
}; };
@ -8901,7 +8930,7 @@
ContentEditableInput.prototype.focus = function () { ContentEditableInput.prototype.focus = function () {
if (this.cm.options.readOnly != "nocursor") { if (this.cm.options.readOnly != "nocursor") {
if (!this.selectionInEditor()) if (!this.selectionInEditor() || document.activeElement != this.div)
{ this.showSelection(this.prepareSelection(), true); } { this.showSelection(this.prepareSelection(), true); }
this.div.focus(); this.div.focus();
} }
@ -9343,6 +9372,15 @@
this.textarea = this.wrapper.firstChild; this.textarea = this.wrapper.firstChild;
}; };
TextareaInput.prototype.screenReaderLabelChanged = function (label) {
// Label for screenreaders, accessibility
if(label) {
this.textarea.setAttribute('aria-label', label);
} else {
this.textarea.removeAttribute('aria-label');
}
};
TextareaInput.prototype.prepareSelection = function () { TextareaInput.prototype.prepareSelection = function () {
// Redraw the selection and/or cursor // Redraw the selection and/or cursor
var cm = this.cm, display = cm.display, doc = cm.doc; var cm = this.cm, display = cm.display, doc = cm.doc;
@ -9733,7 +9771,7 @@
addLegacyProps(CodeMirror); addLegacyProps(CodeMirror);
CodeMirror.version = "5.51.0"; CodeMirror.version = "5.56.0";
return CodeMirror; return CodeMirror;

View File

@ -455,81 +455,98 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
"alignment-baseline", "anchor-point", "animation", "animation-delay", "alignment-baseline", "anchor-point", "animation", "animation-delay",
"animation-direction", "animation-duration", "animation-fill-mode", "animation-direction", "animation-duration", "animation-fill-mode",
"animation-iteration-count", "animation-name", "animation-play-state", "animation-iteration-count", "animation-name", "animation-play-state",
"animation-timing-function", "appearance", "azimuth", "backface-visibility", "animation-timing-function", "appearance", "azimuth", "backdrop-filter",
"background", "background-attachment", "background-blend-mode", "background-clip", "backface-visibility", "background", "background-attachment",
"background-color", "background-image", "background-origin", "background-position", "background-blend-mode", "background-clip", "background-color",
"background-repeat", "background-size", "baseline-shift", "binding", "background-image", "background-origin", "background-position",
"bleed", "bookmark-label", "bookmark-level", "bookmark-state", "background-position-x", "background-position-y", "background-repeat",
"bookmark-target", "border", "border-bottom", "border-bottom-color", "background-size", "baseline-shift", "binding", "bleed", "block-size",
"border-bottom-left-radius", "border-bottom-right-radius", "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target",
"border-bottom-style", "border-bottom-width", "border-collapse", "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
"border-color", "border-image", "border-image-outset", "border-bottom-right-radius", "border-bottom-style", "border-bottom-width",
"border-collapse", "border-color", "border-image", "border-image-outset",
"border-image-repeat", "border-image-slice", "border-image-source", "border-image-repeat", "border-image-slice", "border-image-source",
"border-image-width", "border-left", "border-left-color", "border-image-width", "border-left", "border-left-color", "border-left-style",
"border-left-style", "border-left-width", "border-radius", "border-right", "border-left-width", "border-radius", "border-right", "border-right-color",
"border-right-color", "border-right-style", "border-right-width", "border-right-style", "border-right-width", "border-spacing", "border-style",
"border-spacing", "border-style", "border-top", "border-top-color", "border-top", "border-top-color", "border-top-left-radius",
"border-top-left-radius", "border-top-right-radius", "border-top-style", "border-top-right-radius", "border-top-style", "border-top-width",
"border-top-width", "border-width", "bottom", "box-decoration-break", "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing",
"box-shadow", "box-sizing", "break-after", "break-before", "break-inside", "break-after", "break-before", "break-inside", "caption-side", "caret-color",
"caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count", "clear", "clip", "color", "color-profile", "column-count", "column-fill",
"column-fill", "column-gap", "column-rule", "column-rule-color", "column-gap", "column-rule", "column-rule-color", "column-rule-style",
"column-rule-style", "column-rule-width", "column-span", "column-width", "column-rule-width", "column-span", "column-width", "columns", "contain",
"columns", "content", "counter-increment", "counter-reset", "crop", "cue", "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after",
"cue-after", "cue-before", "cursor", "direction", "display", "cue-before", "cursor", "direction", "display", "dominant-baseline",
"dominant-baseline", "drop-initial-after-adjust", "drop-initial-after-adjust", "drop-initial-after-align",
"drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
"drop-initial-before-align", "drop-initial-size", "drop-initial-value", "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position",
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow",
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into",
"float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", "font", "font-family", "font-feature-settings", "font-kerning",
"font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", "font-language-override", "font-optical-sizing", "font-size",
"font-stretch", "font-style", "font-synthesis", "font-variant", "font-size-adjust", "font-stretch", "font-style", "font-synthesis",
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant", "font-variant-alternates", "font-variant-caps",
"font-variant-ligatures", "font-variant-numeric", "font-variant-position", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric",
"font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "font-variant-position", "font-variation-settings", "font-weight", "gap",
"grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-rows",
"grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-column", "grid-column-end", "grid-column-gap", "grid-column-start",
"grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", "grid-row-start",
"grid-template-rows", "hanging-punctuation", "height", "hyphens", "grid-template", "grid-template-areas", "grid-template-columns",
"icon", "image-orientation", "image-rendering", "image-resolution", "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon",
"inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing", "image-orientation", "image-rendering", "image-resolution", "inline-box-align",
"line-break", "line-height", "line-stacking", "line-stacking-ruby", "inset", "inset-block", "inset-block-end", "inset-block-start", "inset-inline",
"inset-inline-end", "inset-inline-start", "isolation", "justify-content",
"justify-items", "justify-self", "left", "letter-spacing", "line-break",
"line-height", "line-height-step", "line-stacking", "line-stacking-ruby",
"line-stacking-shift", "line-stacking-strategy", "list-style", "line-stacking-shift", "line-stacking-strategy", "list-style",
"list-style-image", "list-style-position", "list-style-type", "margin", "list-style-image", "list-style-position", "list-style-type", "margin",
"margin-bottom", "margin-left", "margin-right", "margin-top", "margin-bottom", "margin-left", "margin-right", "margin-top", "marks",
"marks", "marquee-direction", "marquee-loop", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed",
"marquee-play-count", "marquee-speed", "marquee-style", "max-height", "marquee-style", "max-block-size", "max-height", "max-inline-size",
"max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index", "max-width", "min-block-size", "min-height", "min-inline-size", "min-width",
"nav-left", "nav-right", "nav-up", "object-fit", "object-position", "mix-blend-mode", "move-to", "nav-down", "nav-index", "nav-left", "nav-right",
"opacity", "order", "orphans", "outline", "nav-up", "object-fit", "object-position", "offset", "offset-anchor",
"outline-color", "outline-offset", "outline-style", "outline-width", "offset-distance", "offset-path", "offset-position", "offset-rotate",
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", "opacity", "order", "orphans", "outline", "outline-color", "outline-offset",
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "outline-style", "outline-width", "overflow", "overflow-style",
"page", "page-break-after", "page-break-before", "page-break-inside", "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom",
"page-policy", "pause", "pause-after", "pause-before", "perspective", "padding-left", "padding-right", "padding-top", "page", "page-break-after",
"perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position", "page-break-before", "page-break-inside", "page-policy", "pause",
"presentation-level", "punctuation-trim", "quotes", "region-break-after", "pause-after", "pause-before", "perspective", "perspective-origin", "pitch",
"region-break-before", "region-break-inside", "region-fragment", "pitch-range", "place-content", "place-items", "place-self", "play-during",
"rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "position", "presentation-level", "punctuation-trim", "quotes",
"right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", "region-break-after", "region-break-before", "region-break-inside",
"ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", "region-fragment", "rendering-intent", "resize", "rest", "rest-after",
"shape-outside", "size", "speak", "speak-as", "speak-header", "rest-before", "richness", "right", "rotate", "rotation", "rotation-point",
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "row-gap", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span",
"tab-size", "table-layout", "target", "target-name", "target-new", "scale", "scroll-behavior", "scroll-margin", "scroll-margin-block",
"target-position", "text-align", "text-align-last", "text-decoration", "scroll-margin-block-end", "scroll-margin-block-start", "scroll-margin-bottom",
"scroll-margin-inline", "scroll-margin-inline-end",
"scroll-margin-inline-start", "scroll-margin-left", "scroll-margin-right",
"scroll-margin-top", "scroll-padding", "scroll-padding-block",
"scroll-padding-block-end", "scroll-padding-block-start",
"scroll-padding-bottom", "scroll-padding-inline", "scroll-padding-inline-end",
"scroll-padding-inline-start", "scroll-padding-left", "scroll-padding-right",
"scroll-padding-top", "scroll-snap-align", "scroll-snap-type",
"shape-image-threshold", "shape-inside", "shape-margin", "shape-outside",
"size", "speak", "speak-as", "speak-header", "speak-numeral",
"speak-punctuation", "speech-rate", "stress", "string-set", "tab-size",
"table-layout", "target", "target-name", "target-new", "target-position",
"text-align", "text-align-last", "text-combine-upright", "text-decoration",
"text-decoration-color", "text-decoration-line", "text-decoration-skip", "text-decoration-color", "text-decoration-line", "text-decoration-skip",
"text-decoration-style", "text-emphasis", "text-emphasis-color", "text-decoration-skip-ink", "text-decoration-style", "text-emphasis",
"text-emphasis-position", "text-emphasis-style", "text-height", "text-emphasis-color", "text-emphasis-position", "text-emphasis-style",
"text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", "text-height", "text-indent", "text-justify", "text-orientation",
"text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", "text-outline", "text-overflow", "text-rendering", "text-shadow",
"text-wrap", "top", "transform", "transform-origin", "transform-style", "text-size-adjust", "text-space-collapse", "text-transform",
"transition", "transition-delay", "transition-duration", "text-underline-position", "text-wrap", "top", "transform", "transform-origin",
"transition-property", "transition-timing-function", "unicode-bidi", "transform-style", "transition", "transition-delay", "transition-duration",
"user-select", "vertical-align", "visibility", "voice-balance", "voice-duration", "transition-property", "transition-timing-function", "translate",
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", "unicode-bidi", "user-select", "vertical-align", "visibility", "voice-balance",
"voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break", "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate",
"word-spacing", "word-wrap", "z-index", "voice-stress", "voice-volume", "volume", "white-space", "widows", "width",
"will-change", "word-break", "word-spacing", "word-wrap", "writing-mode", "z-index",
// SVG-specific // SVG-specific
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
"flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
@ -543,16 +560,28 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
], propertyKeywords = keySet(propertyKeywords_); ], propertyKeywords = keySet(propertyKeywords_);
var nonStandardPropertyKeywords_ = [ var nonStandardPropertyKeywords_ = [
"border-block", "border-block-color", "border-block-end",
"border-block-end-color", "border-block-end-style", "border-block-end-width",
"border-block-start", "border-block-start-color", "border-block-start-style",
"border-block-start-width", "border-block-style", "border-block-width",
"border-inline", "border-inline-color", "border-inline-end",
"border-inline-end-color", "border-inline-end-style",
"border-inline-end-width", "border-inline-start", "border-inline-start-color",
"border-inline-start-style", "border-inline-start-width",
"border-inline-style", "border-inline-width", "margin-block",
"margin-block-end", "margin-block-start", "margin-inline", "margin-inline-end",
"margin-inline-start", "padding-block", "padding-block-end",
"padding-block-start", "padding-inline", "padding-inline-end",
"padding-inline-start", "scroll-snap-stop", "scrollbar-3d-light-color",
"scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color", "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
"scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color", "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
"scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside", "scrollbar-track-color", "searchfield-cancel-button", "searchfield-decoration",
"searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", "searchfield-results-button", "searchfield-results-decoration", "shape-inside", "zoom"
"searchfield-results-decoration", "zoom"
], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_); ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
var fontProperties_ = [ var fontProperties_ = [
"font-family", "src", "unicode-range", "font-variant", "font-feature-settings", "font-display", "font-family", "src", "unicode-range", "font-variant",
"font-stretch", "font-weight", "font-style" "font-feature-settings", "font-stretch", "font-weight", "font-style"
], fontProperties = keySet(fontProperties_); ], fontProperties = keySet(fontProperties_);
var counterDescriptors_ = [ var counterDescriptors_ = [

View File

@ -98,10 +98,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (ch == "`") { } else if (ch == "`") {
state.tokenize = tokenQuasi; state.tokenize = tokenQuasi;
return tokenQuasi(stream, state); return tokenQuasi(stream, state);
} else if (ch == "#") { } else if (ch == "#" && stream.peek() == "!") {
stream.skipToEnd(); stream.skipToEnd();
return ret("error", "error"); return ret("meta", "meta");
} else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) { } else if (ch == "#" && stream.eatWhile(wordRE)) {
return ret("variable", "property")
} else if (ch == "<" && stream.match("!--") ||
(ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
stream.skipToEnd() stream.skipToEnd()
return ret("comment", "comment") return ret("comment", "comment")
} else if (isOperatorChar.test(ch)) { } else if (isOperatorChar.test(ch)) {
@ -113,6 +116,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (ch == ">") stream.eat(ch) if (ch == ">") stream.eat(ch)
} }
} }
if (ch == "?" && stream.eat(".")) return ret(".")
return ret("operator", "operator", stream.current()); return ret("operator", "operator", stream.current());
} else if (wordRE.test(ch)) { } else if (wordRE.test(ch)) {
stream.eatWhile(wordRE); stream.eatWhile(wordRE);
@ -417,7 +421,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} }
function parenExpr(type) { function parenExpr(type) {
if (type != "(") return pass() if (type != "(") return pass()
return cont(pushlex(")"), expression, expect(")"), poplex) return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
} }
function expressionInner(type, value, noComma) { function expressionInner(type, value, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) { if (cx.state.fatArrowAt == cx.stream.start) {
@ -446,7 +450,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} }
function maybeoperatorComma(type, value) { function maybeoperatorComma(type, value) {
if (type == ",") return cont(expression); if (type == ",") return cont(maybeexpression);
return maybeoperatorNoComma(type, value, false); return maybeoperatorNoComma(type, value, false);
} }
function maybeoperatorNoComma(type, value, noComma) { function maybeoperatorNoComma(type, value, noComma) {
@ -455,7 +459,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
if (type == "operator") { if (type == "operator") {
if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false)) if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
if (value == "?") return cont(expression, expect(":"), expr); if (value == "?") return cont(expression, expect(":"), expr);
return cont(expr); return cont(expr);
@ -757,11 +761,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} }
if (type == "variable" || cx.style == "keyword") { if (type == "variable" || cx.style == "keyword") {
cx.marked = "property"; cx.marked = "property";
return cont(isTS ? classfield : functiondef, classBody); return cont(classfield, classBody);
} }
if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody); if (type == "number" || type == "string") return cont(classfield, classBody);
if (type == "[") if (type == "[")
return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody) return cont(expression, maybetype, expect("]"), classfield, classBody)
if (value == "*") { if (value == "*") {
cx.marked = "keyword"; cx.marked = "keyword";
return cont(classBody); return cont(classBody);

View File

@ -76,7 +76,7 @@
if (ch == "#") { if (ch == "#") {
stream.next(); stream.next();
// Hex color // Hex color
if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) { if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b(?!-)/i)) {
return ["atom", "atom"]; return ["atom", "atom"];
} }
// ID selector // ID selector

View File

@ -11,7 +11,7 @@
.cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; } .cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; }
.cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; } .cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; }
.cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; } .cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; }
.cm-s-zenburn { background-color: #3f3f3f; color: #dcdccc; } .cm-s-zenburn.CodeMirror { background-color: #3f3f3f; color: #dcdccc; }
.cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; } .cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; }
.cm-s-zenburn span.cm-comment { color: #7f9f7f; } .cm-s-zenburn span.cm-comment { color: #7f9f7f; }
.cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; } .cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; }

2
vendor/uuid/LICENSE vendored
View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2010-2016 Robert Kieffer and other contributors Copyright (c) 2010-2020 Robert Kieffer and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,4 +1,4 @@
## uuid v7.0.0-beta.0 ## uuid v8.1.0
Following files are copied from npm (node_modules): Following files are copied from npm (node_modules):

View File

@ -1 +1 @@
!function(o,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(o=o||self).uuidv4=e()}(this,(function(){"use strict";var o="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof window.msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto),e=new Uint8Array(16);function r(){if(!o)throw new Error("uuid: This browser does not seem to support crypto.getRandomValues(). If you need to support this browser, please provide a custom random number generator through options.rng.");return o(e)}for(var n=[],t=0;t<256;++t)n[t]=(t+256).toString(16).substr(1);return function(o,e,t){var u=e&&t||0;"string"==typeof o&&(e="binary"===o?new Array(16):null,o=null);var i=(o=o||{}).random||(o.rng||r)();if(i[6]=15&i[6]|64,i[8]=63&i[8]|128,e)for(var s=0;s<16;++s)e[u+s]=i[s];return e||function(o,e){var r=e||0,t=n;return[t[o[r++]],t[o[r++]],t[o[r++]],t[o[r++]],"-",t[o[r++]],t[o[r++]],"-",t[o[r++]],t[o[r++]],"-",t[o[r++]],t[o[r++]],"-",t[o[r++]],t[o[r++]],t[o[r++]],t[o[r++]],t[o[r++]],t[o[r++]]].join("")}(i)}})); !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).uuidv4=e()}(this,(function(){"use strict";var t="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto),e=new Uint8Array(16);function n(){if(!t)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return t(e)}for(var o=[],r=0;r<256;++r)o.push((r+256).toString(16).substr(1));return function(t,e,r){"string"==typeof t&&(e="binary"===t?new Uint8Array(16):null,t=null);var u=(t=t||{}).random||(t.rng||n)();if(u[6]=15&u[6]|64,u[8]=63&u[8]|128,e){for(var i=r||0,d=0;d<16;++d)e[i+d]=u[d];return e}return function(t,e){var n=e||0,r=o;return(r[t[n+0]]+r[t[n+1]]+r[t[n+2]]+r[t[n+3]]+"-"+r[t[n+4]]+r[t[n+5]]+"-"+r[t[n+6]]+r[t[n+7]]+"-"+r[t[n+8]]+r[t[n+9]]+"-"+r[t[n+10]]+r[t[n+11]]+r[t[n+12]]+r[t[n+13]]+r[t[n+14]]+r[t[n+15]]).toLowerCase()}(u)}}));