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",
"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": {
"message": "Rücke @media / @supports ein",
"description": "CSS-beautifier option"

View File

@ -1297,6 +1297,10 @@
"message": "Beautify",
"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": {
"message": "Indent @media, @supports",
"description": "CSS-beautifier option"

View File

@ -93,7 +93,7 @@
"description": ""
},
"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"
},
"checkAllUpdatesForce": {
@ -101,7 +101,7 @@
"description": "Label for the button to apply all detected updates"
},
"checkForUpdate": {
"message": "Rechercher des mises à jour",
"message": "Rechercher une mise à jour",
"description": "Label for the button to check a single style for an update"
},
"checkingForUpdate": {
@ -292,6 +292,10 @@
"message": "Glisser votre fichier de sauvegarde nimporte où sur cette page pour limporter.",
"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": {
"message": "Supprimer",
"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",
"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": {
"message": "Rechargement immédiat",
"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"
},
"manageOnlyNonUsercss": {
"message": "Uniquement les styles non Usercss",
"message": "Styles non Usercss uniquement",
"description": "Checkbox to show only non-Usercss (standard) styles"
},
"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"
},
"manageOnlyUsercss": {
"message": "Uniquement les styles Usercss",
"message": "Styles Usercss uniquement",
"description": "Checkbox to show only Usercss styles"
},
"menuShowBadge": {
@ -1464,7 +1476,7 @@
"description": "Note in the toolbar popup for some file types that cannot be accessed"
},
"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"
},
"updateAllCheckSucceededSomeEdited": {

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1285,6 +1285,10 @@
"message": "Mooier maken",
"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": {
"message": "@media, @supports inspringen",
"description": "CSS-beautifier option"

View File

@ -1309,6 +1309,10 @@
"message": "Upiększ",
"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": {
"message": "Wcięcie @media, @supports",
"description": "CSS-beautifier option"

View File

@ -244,6 +244,14 @@
"message": "Да",
"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": {
"message": "Дата установки",
"description": "Option text for the user to sort the style by install date"
@ -284,6 +292,10 @@
"message": "Перетащите файл с резервной копией стилей в любое место этой страницы, чтобы импортировать его.",
"description": "Drag'n'drop message"
},
"dragDropUsercssTabstrip": {
"message": "Чтобы установить файл, поместите его на вкладку (область, где отображаются заголовки вкладок).",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": {
"message": "Удалить",
"description": "Label for the context menu item in the editor to delete selected text"
@ -441,6 +453,10 @@
"message": "Нажмите клавишу",
"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": {
"message": "Добавить к стилю",
"description": "Label for the button to import a style and append to the existing sections"
@ -613,6 +629,14 @@
"message": "Ошибка слежения за файлом",
"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": {
"message": "Автозагрузка изменений",
"description": "The label of live-reload feature"
@ -909,6 +933,10 @@
"message": "Менеджер",
"description": "Link to open the manage page."
},
"openOptions": {
"message": "Опции",
"description": "Go to Options UI"
},
"openStylesManager": {
"message": "Менеджер стилей",
"description": "Label for the style maanger opener in the browser action context menu."
@ -969,6 +997,10 @@
"message": "Обновления",
"description": ""
},
"optionsCustomizeSync": {
"message": "Синхронизация с облаком",
"description": ""
},
"optionsHeading": {
"message": "Настройки",
"description": "Heading for options section on manage page."
@ -1013,6 +1045,70 @@
"message": "Интервал обновления стилей, в часах (укажите 0 для выключения)",
"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": {
"message": "Текущая страница",
"description": "Tooltip for the current page index in search results"
@ -1089,6 +1185,10 @@
"message": "Временно применяет стиль пока вы его редактируете.\nСохраните стиль, чтобы сделать изменения постоянными.",
"description": "Tooltip for the checkbox in style editor to enable live preview while editing."
},
"reload": {
"message": "Перезагрузить расширение Stylus",
"description": "Context menu reload"
},
"replace": {
"message": "Заменить",
"description": "Label before the replace input field in the editor shown on Ctrl-H"
@ -1496,6 +1596,10 @@
"message": "Экспорт Dropbox",
"description": ""
},
"syncDropboxDeprecated": {
"message": "Импорт/экспорт Dropbox заменен более продвинутой синхронизацией стилей на странице параметров.",
"description": ""
},
"retrieveDropboxSync": {
"message": "Импорт Dropbox",
"description": ""

View File

@ -40,6 +40,10 @@
"message": "Şuraya uygulanır",
"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": {
"message": "regexp ile eşleşen URL'ler",
"description": "Option to make the style apply to the entered string as a regular expression"
@ -68,6 +72,14 @@
"message": "Yazar",
"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": {
"message": "Dışa aktar",
"description": ""
@ -76,6 +88,10 @@
"message": "Tüm stiller için güncellemeleri denetle",
"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": {
"message": "Güncellemeleri denetle",
"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",
"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": {
"message": "Yazarken tamamla",
"description": "Label for the checkbox in the style editor."
@ -104,6 +124,14 @@
"message": "Akıllı girintili tab kullan",
"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": {
"message": "Vurgulama",
"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",
"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": {
"message": "Tab büyüklüğü",
"description": "Label for the text box controlling tab size option for the style editor."
@ -120,10 +168,30 @@
"message": "Tema",
"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": {
"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"
},
"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": {
"message": "İptal",
"description": ""
@ -180,6 +248,10 @@
"message": "Güncelleme tarihi",
"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": {
"message": "öntanımlı",
"description": "Default CodeMirror CSS theme option on the edit style page"
@ -204,6 +276,14 @@
"message": "Devre dışı bırak",
"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": {
"message": "Sil",
"description": "Label for the context menu item in the editor to delete selected text"
@ -237,6 +317,14 @@
"message": "Etkinleştir",
"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": {
"message": "Dışa aktar",
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
@ -249,10 +337,34 @@
"message": "Anasayfa",
"description": "Label for the external link to style's homepage"
},
"externalLink": {
"message": "Harici bağlantı",
"description": "Label for external links"
},
"externalSupport": {
"message": "Destek",
"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": {
"message": "Stil bul",
"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",
"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": {
"message": "Ekle",
"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": {
"message": "Devre dışı",
"description": "Used in various lists/options to indicate that something is disabled"
@ -289,6 +413,10 @@
"message": "Önceki",
"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": {
"message": "Kaydedildi",
"description": "Used in various parts of the UI to indicate that something was saved"
@ -305,10 +433,26 @@
"message": "Yardım",
"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": {
"message": "Stile ekle",
"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": {
"message": "İçe aktar",
"description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)"
@ -317,10 +461,18 @@
"message": "Stilin üzerine yaz",
"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": {
"message": "eklendi",
"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": {
"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"
@ -640,6 +792,10 @@
"message": "Güncelleme kontrolü geçmişi",
"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": {
"message": "Stil güncel.",
"description": "Text that displays when an update check completed and no update is available"
@ -652,6 +808,38 @@
"message": "Kurulan güncellemeler:",
"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": {
"message": "bu 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",
"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": {
"message": "Dropbox'a aktar",
"description": ""
},
"overwriteFileExport": {
"message": "Mevcut bir dosyanın üzerine yazmak istiyor musunuz?",
"description": ""
},
"exportSavedSuccess": {
"message": "Dosya başarıyla kaydedildi",
"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": {
"message": "Tüm stiller alınıyor...",
"description": ""
@ -676,6 +884,10 @@
"message": "Stiller sıkıştırılıyor...",
"description": ""
},
"unzipStyles": {
"message": "Stiller açılıyor...",
"description": ""
},
"readingStyles": {
"message": "Stiller okunuyor...",
"description": ""

View File

@ -125,7 +125,7 @@
"description": "Label for the checkbox in the style editor."
},
"cm_autocompleteOnTyping": {
"message": "输入时自动完成",
"message": "输入时自动补全",
"description": "Label for the checkbox in the style editor."
},
"cm_colorpicker": {
@ -133,7 +133,7 @@
"description": "Label for the checkbox controlling colorpicker option for the style editor."
},
"cm_indentWithTabs": {
"message": "使用 Tab 智能缩进",
"message": "使用Tab智能缩进",
"description": "Label for the checkbox controlling tabs with smart indentation option for the style editor."
},
"cm_keyMap": {
@ -153,7 +153,7 @@
"description": "Style editor's 'highglight' drop-down list option: highlight the occurrences of currently selected text"
},
"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"
},
"cm_resizeGripHint": {
@ -244,6 +244,14 @@
"message": "是",
"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": {
"message": "安装日期",
"description": "Option text for the user to sort the style by install date"
@ -284,6 +292,10 @@
"message": "把 JSON 备份文件拖放到页面的任意位置,即可导入",
"description": "Drag'n'drop message"
},
"dragDropUsercssTabstrip": {
"message": "要安装文件,就将其放在选项卡条(显示选项卡标题的区域)上。",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": {
"message": "删除",
"description": "Label for the context menu item in the editor to delete selected text"
@ -441,6 +453,10 @@
"message": "按下热键",
"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": {
"message": "追加到样式",
"description": "Label for the button to import a style and append to the existing sections"
@ -609,6 +625,14 @@
"message": "查看文件时发生错误",
"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": {
"message": "动态刷新",
"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"
},
"manageNewUI": {
"message": "新的样式管理界面",
"message": "新版管理器",
"description": "Label for the checkbox that toggles the new UI on manage page"
},
"manageOnlyDisabled": {
@ -905,6 +929,10 @@
"message": "管理样式",
"description": "Link to open the manage page."
},
"openOptions": {
"message": "选项",
"description": "Go to Options UI"
},
"openStylesManager": {
"message": "打开样式管理器",
"description": "Label for the style maanger opener in the browser action context menu."
@ -965,6 +993,10 @@
"message": "检测更新",
"description": ""
},
"optionsCustomizeSync": {
"message": "同步到云端",
"description": ""
},
"optionsHeading": {
"message": "选项",
"description": "Heading for options section on manage page."
@ -1009,6 +1041,70 @@
"message": "每隔 N 小时检查所有样式更新0 为关闭检查)",
"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": {
"message": "当前页数",
"description": "Tooltip for the current page index in search results"
@ -1085,6 +1181,10 @@
"message": "无需保存即可临时应用样式预览效果。保存以使更改永久生效。",
"description": "Tooltip for the checkbox in style editor to enable live preview while editing."
},
"reload": {
"message": "重新加载 Stylus 扩展程序",
"description": "Context menu reload"
},
"replace": {
"message": "替换",
"description": "Label before the replace input field in the editor shown on Ctrl-H"
@ -1209,6 +1309,10 @@
"message": "格式化",
"description": "Label for the CSS-beautifier button on the edit style page"
},
"styleBeautifyHint": {
"message": "提示: 你可以不显示此面板, 而是用右键“美化”按钮 或用下面定义的快捷键来美化.",
"description": "Hint shown inside the CSS-beautifier panel"
},
"styleBeautifyIndentConditional": {
"message": "缩进 @media、@supports",
"description": "CSS-beautifier option"
@ -1218,7 +1322,7 @@
"description": "CSS-beautifier option"
},
"styleCancelEditLabel": {
"message": "返回管理页面",
"message": "返回管理",
"description": "Label for cancel button for style editing"
},
"styleChangesNotSaved": {
@ -1492,6 +1596,10 @@
"message": "导出至 Dropbox",
"description": ""
},
"syncDropboxDeprecated": {
"message": "在选项页面中, Dropbox 导入/导出 已经替换为更高级的样式同步方式。",
"description": ""
},
"retrieveDropboxSync": {
"message": "从 Dropbox 导入",
"description": ""

View File

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

View File

@ -1,7 +1,7 @@
/* global download prefs openURL FIREFOX CHROME
URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab
findExistingTab activateTab isTabReplaceable getActiveTab
tabManager colorScheme */
'use strict';
@ -10,6 +10,20 @@ var backgroundWorker = workerUtil.createWorker({
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 || {}, {
deleteStyle: styleManager.deleteStyle,
editSave: styleManager.editSave,
@ -70,8 +84,8 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
},
optionsCustomizeHotkeys() {
return browser.runtime.openOptionsPage()
.then(() => new Promise(resolve => setTimeout(resolve, 100)))
return browserCommands.openOptions()
.then(() => new Promise(resolve => setTimeout(resolve, 500)))
.then(() => msg.broadcastExtension({method: 'optionsCustomizeHotkeys'}));
},
@ -86,9 +100,6 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
openManage
});
// eslint-disable-next-line no-var
var browserCommands, contextMenus;
// *************************************************************************
// register all listeners
msg.on(onRuntimeMessage);
@ -148,17 +159,6 @@ chrome.runtime.onInstalled.addListener(({reason}) => {
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
contextMenus = {
@ -218,7 +218,7 @@ function createContextMenus(ids) {
if (chrome.contextMenus) {
// 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, checked) => chrome.contextMenus.update(id, {checked}, ignoreChromeError));
@ -337,7 +337,7 @@ function openManage({options = false, search} = {}) {
if (isTabReplaceable(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 */
'use strict';
@ -55,7 +55,7 @@ const contentScripts = (() => {
}
function injectToAllTabs() {
return queryTabs({}).then(tabs => {
return browser.tabs.query({}).then(tabs => {
for (const tab of tabs) {
// skip unloaded/discarded/chrome tabs
if (!tab.width || tab.discarded || !URLS.supported(tab.url)) continue;

View File

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

View File

@ -3,7 +3,7 @@
'use strict';
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();
prefs.subscribe([

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,49 @@
/* global loadScript css_beautify showHelp prefs t $ $create */
/* exported beautify */
/* global editor createHotkeyInput moveFocus CodeMirror */
/* exported initBeautifyButton */
'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')
.then(() => {
if (!window.css_beautify && window.exports) {
@ -19,7 +60,41 @@ function beautify(scope) {
}
options.indent_size = tabs ? 1 : prefs.get('editor.tabSize');
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'),
$create([
$create('.beautify-options', [
@ -32,6 +107,10 @@ function beautify(scope) {
$createLabeledCheckbox('preserve_newlines', 'styleBeautifyPreserveNewlines'),
$createLabeledCheckbox('indent_conditional', 'styleBeautifyIndentConditional'),
]),
$create('p.beautify-hint', [
$create('span', t('styleBeautifyHint') + '\u00A0'),
createHotkeyInput(HOTKEY_ID, () => moveFocus($('#help-popup'), 1)),
]),
$create('.buttons', [
$create('button', {
attributes: {role: 'close'},
@ -60,32 +139,6 @@ function beautify(scope) {
$('#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}) => {
const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0;
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-offset: -2px;
}
.CodeMirror-bookmark {
background: linear-gradient(to right, currentColor, transparent);
position: absolute;
width: 2em;
opacity: .5;
}
@supports (-moz-appearance:none) {
/* restrict to FF */
.CodeMirror-focused {

View File

@ -8,6 +8,8 @@
prefs.reset('editor.keyMap');
}
const CM_BOOKMARK = 'CodeMirror-bookmark';
const CM_BOOKMARK_GUTTER = CM_BOOKMARK + 'gutter';
const defaults = {
autoCloseBrackets: prefs.get('editor.autoCloseBrackets'),
mode: 'css',
@ -15,6 +17,7 @@
lineWrapping: prefs.get('editor.lineWrapping'),
foldGutter: true,
gutters: [
CM_BOOKMARK_GUTTER,
'CodeMirror-linenumbers',
'CodeMirror-foldgutter',
...(prefs.get('editor.linter') ? ['CodeMirror-lint-markers'] : []),
@ -102,92 +105,19 @@
}
Object.assign(CodeMirror.mimeModes['text/css'].propertyKeywords, {
// CSS Backgrounds and Borders Module L4
'background-position-x': 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,
'mask-image': true,
'mix-blend-mode': true,
'overscroll-behavior': true,
'rotate': 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, {
'darkgrey': true,
'darkslategrey': true,
'dimgrey': true,
'grey': true,
'lightgrey': true,
'lightslategrey': true,
'slategrey': true,
@ -242,22 +172,27 @@
CodeMirror.commands[name] = (...args) => editor[name](...args);
}
// speedup: reuse the old folding marks
// TODO: remove when https://github.com/codemirror/CodeMirror/pull/6010 is shipped in /vendor
const {setGutterMarker} = CodeMirror.prototype;
CodeMirror.prototype.setGutterMarker = function (line, gutterID, value) {
const o = this.state.foldGutter.options;
if (typeof o.indicatorOpen === 'string' ||
typeof o.indicatorFolded === 'string') {
const old = line.gutterMarkers && line.gutterMarkers[gutterID];
// 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;
}
const elBookmark = document.createElement('div');
elBookmark.className = CM_BOOKMARK;
elBookmark.textContent = '\u00A0';
const clearMarker = function () {
const line = this.lines[0];
CodeMirror.TextMarker.prototype.clear.apply(this);
if (!line.markedSpans.some(span => span.marker.sublimeBookmark)) {
this.doc.setGutterMarker(line, CM_BOOKMARK_GUTTER, null);
}
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
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_VAR_KEYWORD = /(^|[^-\w\u0080-\uFFFF])var\(/iy;
const RX_END_OF_VAR = /[\s,)]|$/g;
const RX_CONSUME_PROP = /[-\w]*\s*:\s?|$/y;
const originalHelper = CodeMirror.hint.css || (() => {});
const helper = cm => {
@ -372,7 +308,15 @@ CodeMirror.hint && (() => {
}
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

View File

@ -1,4 +1,4 @@
/* global CodeMirror showHelp cmFactory onDOMready $ $create prefs t */
/* global CodeMirror showHelp cmFactory onDOMready $ prefs t createHotkeyInput */
'use strict';
(() => {
@ -62,46 +62,8 @@
function configureColorpicker(event) {
event.preventDefault();
const input = $create('input', {
type: 'search',
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 input = createHotkeyInput('editor.colorpicker.hotkey', () => {
$('#help-popup .dismiss').onclick();
});
const popup = showHelp(t('helpKeyMapHotkey'), input);
if (this instanceof Element) {

View File

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

View File

@ -1,7 +1,7 @@
/* 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
beautify ignoreChromeError
initBeautifyButton ignoreChromeError
moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
/* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
'use strict';
@ -170,12 +170,21 @@ preinit();
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
$('#preview-label').classList.toggle('hidden', !style.id);
$('#beautify').onclick = () => beautify(editor.getEditors());
initBeautifyButton($('#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', () => {
debounce(rememberWindowSize, 100);
if (!onBoundsChanged) debounce(rememberWindowSize, 100);
detectLayout();
});
detectLayout();
@ -217,7 +226,7 @@ function preinit() {
}).observe(document, {subtree: true, childList: true});
if (chrome.windows) {
queryTabs({currentWindow: true}).then(tabs => {
browser.tabs.query({currentWindow: true}).then(tabs => {
const windowId = tabs[0].windowId;
if (prefs.get('openEditInWindow')) {
if (

View File

@ -61,7 +61,7 @@
border: none;
background-color: white;
font-weight: bold;
white-space: nowrap;
white-space: pre; /* issue #1000 */
color: currentColor; /* use the current theme's color instead of UserAgent's CSS */
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 */
'use strict';
@ -915,7 +915,7 @@ onDOMready().then(() => {
function readStorage() {
chrome.storage.local.get('editor', ({editor = {}}) => {
chromeLocal.getValue('editor').then((editor = {}) => {
state.find = editor.find || '';
state.replace = editor.replace || '';
state.icase = editor.icase || state.icase;
@ -924,14 +924,12 @@ onDOMready().then(() => {
function writeStorage() {
chrome.storage.local.get('editor', ({editor}) =>
chrome.storage.local.set({
editor: Object.assign(editor || {}, {
find: state.find,
replace: state.replace,
icase: state.icase,
})
}));
chromeLocal.getValue('editor').then((editor = {}) =>
chromeLocal.setValue('editor', Object.assign(editor, {
find: state.find,
replace: state.replace,
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 */
'use strict';
@ -66,7 +66,7 @@ const regExpTester = (() => {
return rxData;
});
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)
.filter(url => URLS.supported(url));
const unique = [...new Set(supported).values()];

View File

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

View File

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

View File

@ -34,7 +34,8 @@ function createSectionsEditor({style, onTitleChanged}) {
$('#from-mozilla').addEventListener('click', () => showMozillaFormatImport());
$('#save-button').addEventListener('click', saveStyle);
document.addEventListener('wheel', scrollEntirePageOnCtrlShift);
document.addEventListener('wheel', scrollEntirePageOnCtrlShift, {passive: false});
CodeMirror.defaults.extraKeys['Shift-Ctrl-Wheel'] = 'scrollWindow';
if (!FIREFOX) {
$$([
@ -154,9 +155,7 @@ function createSectionsEditor({style, onTitleChanged}) {
function closestVisible(nearbyElement) {
const cm =
nearbyElement instanceof CodeMirror ? nearbyElement :
nearbyElement instanceof Node &&
(nearbyElement.closest('#sections > .section') || {}).CodeMirror ||
getLastActivatedEditor();
nearbyElement instanceof Node && getAssociatedEditor(nearbyElement) || getLastActivatedEditor();
if (nearbyElement instanceof Node && cm) {
const {left, top} = nearbyElement.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() {
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 keyMapSorted = Object.keys(keyMap)
.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));
const table = template.keymapHelp.cloneNode(true);
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';
function dirtyReporter() {
@ -135,3 +136,52 @@ function memoize(fn) {
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">
<script src="js/polyfill.js"></script>
<script src="js/promisify.js"></script>
<script src="js/msg.js"></script>
<script src="js/messaging.js"></script>
<script src="js/prefs.js"></script>

View File

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

View File

@ -85,6 +85,27 @@ onDOMready().then(() => {
// set language for CSS :lang and [FF-only] hyphenation
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() {
if (document.readyState !== 'loading') {

View File

@ -1,18 +1,16 @@
/* exported getTab getActiveTab onTabReady stringAsRegExp openURL ignoreChromeError
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
/* global promisify */
/* global promisifyChrome */
'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 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]);
// 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) {
// 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/
// TODO: remove when "minimum_chrome_version": "61" or higher
chromeProtectsNTP: CHROME >= 3161,
chromeProtectsNTP: CHROME >= 61,
userstylesOrgJson: 'https://userstyles.org/styles/chrome/',
@ -95,33 +93,20 @@ if (IS_BG) {
// Object.defineProperty(window, 'localStorage', {value: {}});
// Object.defineProperty(window, 'sessionStorage', {value: {}});
const createTab = promisify(chrome.tabs.create.bind(chrome.tabs));
const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs));
const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs));
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));
promisifyChrome({
tabs: ['create', 'get', 'getCurrent', 'move', 'query', 'update'],
windows: ['create', 'update'], // Android doesn't have chrome.windows
});
// FF57+ supports openerTabId, but not in Android
// (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;
function getTab(id) {
return new Promise(resolve =>
chrome.tabs.get(id, tab =>
!chrome.runtime.lastError && resolve(tab)));
}
function getOwnTab() {
return new Promise(resolve =>
chrome.tabs.getCurrent(tab => resolve(tab)));
return browser.tabs.getCurrent();
}
function getActiveTab() {
return queryTabs({currentWindow: true, active: true})
return browser.tabs.query({currentWindow: true, active: true})
.then(tabs => tabs[0]);
}
@ -142,7 +127,7 @@ function urlToMatchPattern(url, ignoreSearch) {
function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
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?
.then(tabs => tabs.find(matchTab));
@ -193,8 +178,8 @@ function openURL({
url: url !== tab.url && url.includes('#') ? url : undefined,
});
}
if (newWindow && createWindow) {
return createWindow(Object.assign({url}, windowPosition))
if (newWindow && browser.windows) {
return browser.windows.create(Object.assign({url}, windowPosition))
.then(wnd => wnd.tabs[0]);
}
return getActiveTab().then((activeTab = {url: ''}) =>
@ -207,7 +192,7 @@ function openURL({
if (id != null && !openerTab.incognito && openerTabIdSupported) {
options.openerTabId = id;
}
return createTab(options);
return browser.tabs.create(options);
}
}
@ -234,9 +219,9 @@ function activateTab(tab, {url, index, openerTabId} = {}) {
options.openerTabId = openerTabId;
}
return Promise.all([
updateTab(tab.id, options),
updateWindow && updateWindow(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index})
browser.tabs.update(tab.id, options),
browser.windows && browser.windows.update(tab.windowId, {focused: true}),
index != null && browser.tabs.move(tab.id, {index})
])
.then(() => tab);
}
@ -381,7 +366,8 @@ function download(url, {
body,
responseType = 'text',
requiredStatusCode = 200,
timeout = 10e3,
timeout = 10e3, // connection timeout
loadTimeout = 2 * 60e3, // data transfer timeout (counted from the first remote response)
headers = {
'Content-type': 'application/x-www-form-urlencoded',
},
@ -398,23 +384,41 @@ function download(url, {
const usoVars = [];
return new Promise((resolve, reject) => {
let xhr;
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
// FIXME: add FetchController when it is available.
const timer = setTimeout(reject, timeout, new Error('Timeout fetching ' + u.href));
fetch(u.href, {mode: 'same-origin'})
.then(r => {
clearTimeout(timer);
switchTimer();
return r.status === 200 ? r.text() : Promise.reject(r.status);
})
.catch(reject)
.then(resolve);
.then(text => {
clearTimeout(timer);
resolve(text);
});
return;
}
const xhr = new XMLHttpRequest();
xhr.timeout = timeout;
xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState >= XMLHttpRequest.HEADERS_RECEIVED) {
xhr.onreadystatechange = null;
switchTimer();
}
};
xhr.onloadend = event => {
clearTimeout(timer);
if (event.type !== 'error' && (
xhr.status === requiredStatusCode || !requiredStatusCode ||
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.
'use strict';
self.msg = self.INJECTED === 1 ? self.msg : (() => {
const runtimeSend = promisify(chrome.runtime.sendMessage.bind(chrome.runtime));
const tabSend = chrome.tabs && promisify(chrome.tabs.sendMessage.bind(chrome.tabs));
const tabQuery = chrome.tabs && promisify(chrome.tabs.query.bind(chrome.tabs));
promisifyChrome({
runtime: ['sendMessage'],
tabs: ['sendMessage', 'query'],
});
const isBg = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
if (isBg) {
window._msg = {
@ -49,19 +49,21 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
}
}
if (chrome.runtime.getBackgroundPage) {
return promisify(chrome.runtime.getBackgroundPage.bind(chrome.runtime))()
.catch(() => null);
promisifyChrome({
runtime: ['getBackgroundPage'],
});
return browser.runtime.getBackgroundPage().catch(() => null);
}
return Promise.resolve(null);
}
function send(data, target = 'extension') {
const message = {data, target};
return runtimeSend(message).then(unwrapData);
return browser.runtime.sendMessage(message).then(unwrapData);
}
function sendTab(tabId, data, options, target = 'tab') {
return tabSend(tabId, {data, target}, options)
return browser.tabs.sendMessage(tabId, {data, target}, options)
.then(unwrapData);
}
@ -99,7 +101,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
}
function broadcastTab(data, filter, options, ignoreExtension = false, target = 'tab') {
return tabQuery({})
return browser.tabs.query({})
// TODO: send to activated tabs first?
.then(tabs => {
const requests = [];
@ -123,7 +125,7 @@ self.msg = self.INJECTED === 1 ? self.msg : (() => {
const message = {data: dataObj, target};
if (tab && tab.id) {
requests.push(
tabSend(tab.id, message, options)
browser.tabs.sendMessage(tab.id, message, options)
.then(unwrapData)
.catch(ignoreError)
);

View File

@ -3,6 +3,8 @@
// eslint-disable-next-line no-unused-expressions
self.INJECTED !== 1 && (() => {
// this part runs in workers, content scripts, our extension pages
if (!Object.entries) {
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]);
}
// the above was shared by content scripts and workers,
// the rest is only needed for our extension pages
if (!self.chrome || !self.chrome.tabs) return;
// don't use self.chrome. It is undefined in Firefox
if (typeof chrome !== 'object') 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') {
const ELEMENT_METH = {
@ -75,4 +106,27 @@ self.INJECTED !== 1 && (() => {
} catch (err) {
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';
self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
@ -63,6 +63,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
end_with_newline: false,
indent_conditional: true,
},
'editor.beautify.hotkey': '',
'editor.lintDelay': 300, // lint gutter marker update delay, ms
'editor.linter': 'csslint', // 'csslint' or 'stylelint' or ''
'editor.lintReportDelay': 500, // lint report update delay, ms
@ -110,10 +111,11 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
specific: new Map(),
};
const syncSet = promisify(chrome.storage.sync.set.bind(chrome.storage.sync));
const syncGet = promisify(chrome.storage.sync.get.bind(chrome.storage.sync));
promisifyChrome({
'storage.sync': ['get', 'set'],
});
const initializing = syncGet('settings')
const initializing = browser.storage.sync.get('settings')
.then(result => {
if (result.settings) {
setAll(result.settings, true);
@ -240,7 +242,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
timer = null;
syncSet({settings: values})
browser.storage.sync.set({settings: values})
.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 */
'use strict';
promisifyChrome({
'storage.local': ['get', 'remove', 'set'],
'storage.sync': ['get', 'remove', 'set'],
});
const [chromeLocal, chromeSync] = (() => {
return [
createWrapper('local'),
@ -9,11 +14,11 @@ const [chromeLocal, chromeSync] = (() => {
];
function createWrapper(name) {
const storage = chrome.storage[name];
const storage = browser.storage[name];
const wrapper = {
get: data => new Promise(resolve => storage.get(data, resolve)),
set: data => new Promise(resolve => storage.set(data, () => resolve(data))),
remove: data => new Promise(resolve => storage.remove(data, resolve)),
get: storage.get.bind(storage),
set: data => storage.set(data).then(() => data),
remove: storage.remove.bind(storage),
/**
* @param {String} key

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -134,6 +134,15 @@
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 {
text-align: right;
direction: rtl;

View File

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

View File

@ -8,8 +8,8 @@ setupLivePrefs();
enforceInputRange($('#popupWidth'));
setTimeout(splitLongTooltips);
// TODO: add max version to re-enable once crbug.com/996859 is resolved
if (!FIREFOX && CHROME >= 3809) {
// https://github.com/openstyles/stylus/issues/822
if (!FIREFOX && CHROME >= 76 && CHROME <= 81) {
const dropboxOption = $('option[value="dropbox"]');
dropboxOption.disabled = true;
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)
if (!FIREFOX && !OPERA && CHROME < 3343) {
if (!FIREFOX && !OPERA && CHROME < 66) {
const block = $('#advanced');
$('h1', block).onclick = event => {
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",
"version": "1.5.10",
"version": "1.5.13",
"description": "Redesign the web with Stylus, a user styles manager",
"license": "GPL-3.0-only",
"repository": "openstyles/stylus",
"author": "Stylus Team",
"devDependencies": {
"archiver": "^3.1.1",
"codemirror": "^5.51.0",
"dependencies": {
"codemirror": "^5.56.0",
"db-to-cloud": "^0.4.5",
"endent": "^1.4.0",
"eslint": "^6.8.0",
"fs-extra": "^8.1.0",
"jsonlint": "^1.6.3",
"less-bundle": "github:openstyles/less-bundle#v0.1.0",
"lz-string-unsafe": "^1.4.4-fork-1",
"make-fetch-happen": "^7.1.1",
"semver-bundle": "^0.1.1",
"shx": "^0.3.2",
"stylelint-bundle": "^8.0.0",
"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",
"tiny-glob": "^0.2.6",
"usercss-meta": "^0.9.0",
"uuid": "^7.0.0-beta.0",
"web-ext": "^4.1.0",
"web-ext": "^4.2.0",
"webext-tx-fix": "^0.3.3"
},
"scripts": {

View File

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

View File

@ -341,6 +341,13 @@ a.configure[target="_blank"] .svg-icon.config {
overflow: hidden;
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 {
width: 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 = '#' + 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 }'));
}

View File

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

View File

@ -106,7 +106,7 @@ const colorConverter = (() => {
if (!type) return;
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[3] && !validateAlpha(num[3])) return null;

View File

@ -182,12 +182,13 @@
if (viewFrom > 0 || viewTo < cm.doc.size) {
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 {curOp} = cm;
if (!curOp) cm.startOperation();
@ -197,22 +198,19 @@
state.stopped = null;
// before the visible range
if (viewFrom) {
state.line = line;
cm.doc.iter(line, viewFrom, lineHandle => colorizeLine(state, lineHandle));
}
cm.eachLine(state.line, viewFrom, lineHandle => colorizeLine(state, lineHandle));
// after the visible range
if (!state.stopped && viewTo < cm.doc.size) {
state.line = viewTo;
cm.doc.iter(viewTo, cm.doc.size, lineHandle => colorizeLine(state, lineHandle));
state.line = Math.max(viewTo, state.line);
cm.eachLine(state.line, cm.doc.size, lineHandle => colorizeLine(state, lineHandle));
}
updateMarkers(state);
if (!curOp) cm.endOperation();
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:
/* 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's end is hardwired to the line of the closing comment
/* csslint ignore:end */
/* 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's end is hardwired to the line of the closing comment
*/
/* csslint ignore:end */
// Example 2:
// allow rule violations on the current line:
/* csslint allow:rulename1,rulename2,... */
// allows to break the specified rules on the next single line of code
// foo: bar; /* csslint allow:rulename1,rulename2,... */
/* csslint allow:rulename1,rulename2,... */ // foo: bar;
// Example 3:
/* csslint rulename1 */
/* csslint rulename2:N */
/* csslint rulename3:N, rulename4:N */
/* csslint rulename1 */
/* csslint rulename2:N */
/* csslint rulename3:N, rulename4:N */
// entire code is affected;
// comments futher down the code extend/override previous comments of this kind
// values for N:
// "2" or "true" means "error"
// "1" or nothing means "warning" - note in this case ":" can also be omitted
// "0" or "false" means "ignore"
// (the quotes are added here for convenience, don't put them in the actual comments)
/* entire code is affected;
* comments futher down the code extend/override previous comments of this kind
* values for N (without the backquotes):
`2` or `true` means "error"
`1` or omitted means "warning" (when omitting, the colon can be omitted too)
`0` or `false` means "ignore"
*/
function applyEmbeddedOverrides(text, ruleset, allow, ignore) {
let ignoreStart = null;
@ -282,7 +285,7 @@ var CSSLint = (() => {
allowRuleset[allowRule.trim()] = true;
num++;
});
if (num) allow[lineno + 1] = allowRuleset;
if (num) allow[lineno] = allowRuleset;
break;
}

View File

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

View File

@ -82,7 +82,9 @@
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]) {
CodeMirror.on(button, "click", function() {
close();

View File

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

View File

@ -85,11 +85,16 @@
},
pick: function(data, i) {
var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion);
else this.cm.replaceRange(getText(completion), completion.from || data.from,
completion.to || data.to, "complete");
CodeMirror.signal(data, "pick", completion);
var completion = data.list[i], self = this;
this.cm.operation(function() {
if (completion.hint)
completion.hint(self.cm, data, 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();
},
@ -99,9 +104,14 @@
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);
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)))) {
this.close();
} else {
@ -369,11 +379,14 @@
},
scrollToActive: function() {
var node = this.hints.childNodes[this.selectedHint]
if (node.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
var margin = this.completion.options.scrollMargin || 0;
var node1 = this.hints.childNodes[Math.max(0, this.selectedHint - margin)];
var node2 = this.hints.childNodes[Math.min(this.data.list.length - 1, this.selectedHint + margin)];
var firstNode = this.hints.firstChild;
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() {

View File

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

View File

@ -240,7 +240,7 @@
var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
// 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) {
if (reverse) {
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-Q Tab": repeated("insertTab"),
"Ctrl-U": addPrefixMap
"Ctrl-U": addPrefixMap,
"fallthrough": "default"
});
var prefixMap = {"Ctrl-G": clearPrefix};

View File

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

View File

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

View File

@ -164,17 +164,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
.CodeMirror-scroll {
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 */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
margin-bottom: -50px; margin-right: -50px;
padding-bottom: 50px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
border-right: 50px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
@ -212,7 +212,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
margin-bottom: -50px;
}
.CodeMirror-gutter-wrapper {
position: absolute;

View File

@ -204,7 +204,7 @@
}
// 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
// handling this'.
@ -485,14 +485,15 @@
for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
order.push(new BidiSpan(0, start, i$7));
} 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 (var j$2 = pos; j$2 < i$7;) {
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;
for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
order.splice(at, 0, new BidiSpan(2, nstart, j$2));
at += isRTL;
pos = j$2;
} else { ++j$2; }
}
@ -1200,7 +1201,7 @@
var prop = lineClass[1] ? "bgClass" : "textClass";
if (output[prop] == null)
{ 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]; }
} }
return type
@ -2959,7 +2960,7 @@
var x, y, space = display.lineSpace.getBoundingClientRect();
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
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;
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;
@ -3550,7 +3551,7 @@
}
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 }
cm.doc.scrollTop = val;
cm.display.scrollbars.setScrollTop(val);
@ -3560,7 +3561,7 @@
// Sync scroller and scrollbar, ensure the gutter elements are
// aligned.
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 }
cm.doc.scrollLeft = val;
alignHorizontally(cm);
@ -4049,7 +4050,8 @@
function restoreSelection(snapshot) {
if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
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();
range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
range.collapse(false);
@ -4147,6 +4149,8 @@
update.visible = visibleLines(cm.display, cm.doc, viewport);
if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
{ break }
} else if (first) {
update.visible = visibleLines(cm.display, cm.doc, viewport);
}
if (!updateDisplayIfNeeded(cm, update)) { break }
updateHeightsInViewport(cm);
@ -6496,7 +6500,7 @@
text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
origin: "paste"};
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();
}
}
catch(e){}
catch(e$1){}
}
}
@ -6829,7 +6833,7 @@
function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) {
if (cm.getOption("direction") == "rtl") { dir = -dir; }
if (cm.doc.direction == "rtl") { dir = -dir; }
var order = getOrder(lineObj, cm.doc.direction);
if (order) {
var part = dir < 0 ? lst(order) : order[0];
@ -7084,7 +7088,7 @@
var line = getLine(cm.doc, start.line);
var order = getOrder(line, cm.doc.direction);
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;
return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
}
@ -7187,6 +7191,7 @@
var lastStoppedKey = null;
function onKeyDown(e) {
var cm = this;
if (e.target && e.target != cm.display.input.getField()) { return }
cm.curOp.focus = activeElt();
if (signalDOMEvent(cm, e)) { return }
// IE does strange things with escape.
@ -7230,6 +7235,7 @@
function onKeyPress(e) {
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 }
var keyCode = e.keyCode, charCode = e.charCode;
if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
@ -7378,8 +7384,8 @@
if (!behavior.addNew)
{ extendSelection(cm.doc, pos, null, null, behavior.extend); }
// Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
if (webkit || ie && ie_version == 9)
{ setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); }
if ((webkit && !safari) || ie && ie_version == 9)
{ setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
else
{ display.input.focus(); }
}
@ -7591,7 +7597,7 @@
mY = e.touches[0].clientY;
} else {
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 (prevent) { e_preventDefault(e); }
@ -7691,7 +7697,7 @@
for (var i = newBreaks.length - 1; i >= 0; i--)
{ 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");
if (old != Init) { cm.refresh(); }
});
@ -7755,6 +7761,12 @@
}
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("dragDrop", true, dragDropChanged);
option("allowDropFileTypes", null);
@ -8105,7 +8117,7 @@
{ from = Pos(from.line, from.ch - deleted); }
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)); }
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); }
}
var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
@ -8586,7 +8598,7 @@
clearCaches(this);
scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
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); }
signal(this, "refresh", this);
}),
@ -8640,7 +8652,7 @@
var oldPos = pos;
var origDir = dir;
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() {
var l = pos.line + lineDir;
if (l < doc.first || l >= doc.first + doc.size) { return false }
@ -8735,8 +8747,16 @@
var div = input.div = display.lineDiv;
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) {
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
if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
});
@ -8761,7 +8781,7 @@
});
function onCopyCut(e) {
if (signalDOMEvent(cm, e)) { return }
if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
if (cm.somethingSelected()) {
setLastCopied({lineWise: false, text: cm.getSelections()});
if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
@ -8803,9 +8823,18 @@
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 () {
var result = prepareSelection(this.cm, false);
result.focus = this.cm.state.focused;
result.focus = document.activeElement == this.div;
return result
};
@ -8901,7 +8930,7 @@
ContentEditableInput.prototype.focus = function () {
if (this.cm.options.readOnly != "nocursor") {
if (!this.selectionInEditor())
if (!this.selectionInEditor() || document.activeElement != this.div)
{ this.showSelection(this.prepareSelection(), true); }
this.div.focus();
}
@ -9343,6 +9372,15 @@
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 () {
// Redraw the selection and/or cursor
var cm = this.cm, display = cm.display, doc = cm.doc;
@ -9733,7 +9771,7 @@
addLegacyProps(CodeMirror);
CodeMirror.version = "5.51.0";
CodeMirror.version = "5.56.0";
return CodeMirror;

View File

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

View File

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

View File

@ -76,7 +76,7 @@
if (ch == "#") {
stream.next();
// 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"];
}
// ID selector

View File

@ -11,7 +11,7 @@
.cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; }
.cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; }
.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-comment { color: #7f9f7f; }
.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)
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
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):

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)}}));