diff --git a/background/settings.ts b/background/settings.ts index a1550130e..6d75ab5d4 100644 --- a/background/settings.ts +++ b/background/settings.ts @@ -233,6 +233,9 @@ export const updatePayload_ = function (shortKey: keyof SettingsNS.FrontendCompl && Build.OS & kBOS.MAC && (Build.OS === kBOS.MAC as number || !os_) ? kKeyLayout.ignoreCaps : 0) break case "d": value = value ? " D" : ""; break + case "p": case "y": + value = value.replace(":default", shortKey === "p" ? defaults_.passEsc : defaults_.ignoreReadonly) + break default: if (0) { shortKey satisfies never } break // lgtm [js/unreachable-statement] } return obj ? (obj as Generalized)[shortKey] = value : value @@ -357,6 +360,7 @@ saladict@crimx.com` filterLinkHints: false, grabBackFocus: false, hideHud: false, + ignoreReadonly: "#read-only-cursor-text-area", // GitHub file content keyLayout: kKeyLayout.Default, keyboard: [560, 33], keyupTime: 120, @@ -505,7 +509,7 @@ export const frontUpdateAllowed_: ReadonlyArray { } } lastWndFocusTime = 0; - if (getEditableType_(target)) { + let match: boolean | void, readOnly: boolean, type: EditableType | boolean + if (type = getEditableType_(target)) { if (grabBackFocus) { (grabBackFocus as Exclude)(event, target); + } else if (readOnly = + (type as number | boolean as EditableType) > EditableType.MaxNotTextBox && (target as TextElement).readOnly + || (type as number | boolean as EditableType) > EditableType.MaxNotEditableElement + && !isAriaFalse_(target, kAria.readOnly), + readOnly && (match = safeCall(testMatch, fgCache.y, [target]), match != null ? match : (fgCache.y = ""))) { + /* empty */ } else { esc!(HandlerResult.Nothing) lock_ = target diff --git a/i18n/zh/options.json b/i18n/zh/options.json index b2355bd8b..c35652c07 100644 --- a/i18n/zh/options.json +++ b/i18n/zh/options.json @@ -164,6 +164,8 @@ "95": "如何在其它扩展程序的私有页面中使用 Vimium C?", "95_2": "ESC 键兼容模式", "95_3": "输入文字时,指定在哪些文本框上放行“ESC”键的按下事件并推迟移除焦点。默认值可便于在部分网站上收起搜索建议。", + "95_4": "要忽略的只读文本", + "95_5": "如果键盘焦点在只读文本上,指定在哪些文本框上要继续触发快捷键。", "96": "定制搜索框页面", "97": "例如:", "98": "或", diff --git a/i18n/zh_TW/options.json b/i18n/zh_TW/options.json index ecc466c53..868d8f0e1 100644 --- a/i18n/zh_TW/options.json +++ b/i18n/zh_TW/options.json @@ -164,6 +164,8 @@ "95": "如何在其他擴充程式的私有頁面中使用 Vimium C?", "95_2": "ESC 鍵兼容模式", "95_3": "輸入文字時,指定在哪些輸入框上放行“ESC”鍵的按下事件並推遲移除焦點。默認值可便於在部分網站上收起搜尋建議。", + "95_4": "要忽略的只讀文字", + "95_5": "如果鍵盤焦點在只讀文字上,指定在哪些文字方塊上要繼續觸發快速鍵。", "96": "定制搜尋框頁面", "97": "例如:", "98": "或", diff --git a/lib/dom_utils.ts b/lib/dom_utils.ts index 00c2a9cbc..4f10978ff 100644 --- a/lib/dom_utils.ts +++ b/lib/dom_utils.ts @@ -15,7 +15,7 @@ interface kNodeToType { export const DAC = "DOMActivate", MDW = "mousedown", CLK = "click", HDN = "hidden", NONE = "none" export const INP = "input", BU = "blur", ALA = "aria-label", PGH = "pagehide" export const kDir = ["backward", "forward"] as const, kGCh = "character" -export const AriaArray = ["aria-hidden", "aria-disabled", "aria-haspopup"] as const +export const AriaArray = ["aria-hidden", "aria-disabled", "aria-haspopup", "aria-readonly"] as const //#region data and DOM-shortcut section @@ -423,8 +423,9 @@ export const isRawStyleVisible = (style: CSSStyleDeclaration): boolean => style. export const isAriaFalse_ = (element: SafeElement, ariaType: kAria): boolean => { let s = Build.BTypes === BrowserType.Safari as number|| !(Build.BTypes & ~(BrowserType.Chrome | BrowserType.Safari)) - && Build.MinCVer >= BrowserVer.MinCorrectAriaSelected ? ariaType > kAria.disabled ? element.ariaHasPopup - : ariaType < kAria.disabled ? element.ariaHidden : element.ariaDisabled as string | null + && Build.MinCVer >= BrowserVer.MinCorrectAriaSelected + ? ariaType > kAria.disabled ? ariaType > kAria.hasPopup ? element.ariaReadOnly : element.ariaHasPopup + : ariaType < kAria.disabled ? element.ariaHidden : element.ariaDisabled as string | null : element.getAttribute(AriaArray[ariaType]) return s === null || (!!s && Lower(s) === "false") || !!(evenHidden_ & (kHidden.BASE_ARIA << ariaType)) } diff --git a/pages/options.html b/pages/options.html index 8c1b64ab3..074329d6f 100644 --- a/pages/options.html +++ b/pages/options.html @@ -706,14 +706,26 @@ Compatibility of Escape - +
Delete all to reset this option.
-
+
In plain insert mode, specify when to pass Escape keydown events to pages and delay blurring. The default value may collapse some search suggestions.
+ + Ignore list of readonly textbox + + +
Delete all to reset this option.
+ +
+ If a readonly textbox gets focused, specify if to keep a current normal mode, + or otherwise to enter insert mode. +
+ Preferred Vomnibar page diff --git a/pages/options_base.ts b/pages/options_base.ts index a596bcd5c..d44f646cc 100644 --- a/pages/options_base.ts +++ b/pages/options_base.ts @@ -205,12 +205,12 @@ export const bgSettings_ = { }, valuesToLoad_: { __proto__: null as never, - filterLinkHints: "f", keyLayout: "l", mouseReachable: "e", + filterLinkHints: "f", ignoreReadonly: "y", keyLayout: "l", mouseReachable: "e", keyboard: "k", keyupTime: "u", linkHintCharacters: "c", linkHintNumbers: "n", passEsc: "p", regexFindMode: "r", smoothScroll: "s", scrollStepSize: "t", waitForEnter: "w" } satisfies SettingsNS.AutoSyncedNameMap & SafeObject as SettingsNS.AutoSyncedNameMap, complexValuesToLoad_: { - __proto__: null as never, c: 1, n: 1, l: 1, d: 1 + __proto__: null as never, c: 1, n: 1, l: 1, d: 1, p: 1, y: 1 } satisfies TypedSafeEnum } diff --git a/pages/options_defs.ts b/pages/options_defs.ts index 41a0e89bc..124d1687b 100644 --- a/pages/options_defs.ts +++ b/pages/options_defs.ts @@ -357,6 +357,22 @@ export class NonEmptyTextOption_ extends TextOption_< } } +const kPseudoDefault = ":default" + +export class CssSelectorOption_ extends TextOption_<"passEsc" | "ignoreReadonly"> { + override readValueFromElement_(): string { + let value = super.readValueFromElement_() + value = value.replace( /:default\(.*?\)/, kPseudoDefault) + value = value !== kPseudoDefault ? value.replace( /,\s+/g, ",") : bgSettings_.defaults_[this.field_] + return value + } + override formatValue_ (value: string): string { + value = value !== bgSettings_.defaults_[this.field_] ? value : kPseudoDefault + value = value.replace(kPseudoDefault, `${kPseudoDefault}(${bgSettings_.defaults_[this.field_]})`) + return value.replace( /,/g, ", ") + } +} + export type JSONOptionNames = PossibleOptionNames export class JSONOption_ extends TextOption_ { override formatValue_ (obj: AllowedOptions[T]): string { @@ -537,7 +553,7 @@ export const createNewOption_ = ((): (_element: const types = { Number: NumberOption_, Boolean: BooleanOption_, Text: TextOption_, NonEmptyText: NonEmptyTextOption_, JSON: JSONOption_, MaskedText: MaskedText_, - ExclusionRules: ExclusionRulesOption_ + ExclusionRules: ExclusionRulesOption_, CssSelector: CssSelectorOption_, } const createOption = (element: HTMLElement): Option_ => { const cls = types[(element.dataset as KnownOptionsDataset).model as "Text"] @@ -777,11 +793,6 @@ Option_.all_.autoReduceMotion.onSave_ = function (): void { }) } -Option_.all_.passEsc.readValueFromElement_ = function (): string { - return NonEmptyTextOption_.prototype.readValueFromElement_.call(this).replace( /, /g, ",") -} -Option_.all_.passEsc.formatValue_ = (value: string): string => value.replace( /,/g, ", ") - const onBeforeUnload = (): string => { setTimeout((): void => { // wait until the confirmation dialog returning setTimeout((): void => { // ensure the result is neither closing nor reloading diff --git a/typings/base/base.d.ts b/typings/base/base.d.ts index d72f38a62..4ea27c047 100644 --- a/typings/base/base.d.ts +++ b/typings/base/base.d.ts @@ -294,6 +294,7 @@ interface Element { ariaDisabled?: boolean | string | null ariaHasPopup?: string | null ariaHidden?: string | null + ariaReadOnly?: string | null } interface HTMLElement { focus (options?: { preventScroll?: boolean }): void diff --git a/typings/messages.d.ts b/typings/messages.d.ts index c6a45b3ba..2cbc4a057 100644 --- a/typings/messages.d.ts +++ b/typings/messages.d.ts @@ -42,7 +42,7 @@ interface ParsedSearch { /** error */ e?: string | null; } -declare const enum kAria { hidden = 0, disabled = 1, hasPopup = 2 } +declare const enum kAria { hidden = 0, disabled = 1, hasPopup = 2, readOnly = 3 } declare const enum kHidden { None = 0, VisibilityHidden = 1, OverflowHidden = 2, Size0 = 4, BASE_ARIA = 16, AriaHidden = BASE_ARIA << kAria.hidden, AriaDisabled = BASE_ARIA << kAria.disabled, diff --git a/typings/vimium_c.d.ts b/typings/vimium_c.d.ts index 93a3fbd6a..e90f792f4 100644 --- a/typings/vimium_c.d.ts +++ b/typings/vimium_c.d.ts @@ -296,6 +296,7 @@ declare const enum kKeyLayout { } declare namespace SettingsNS { interface DirectlySyncedItems { + /** ignoreReadonly */ y: ["ignoreReadonly", string] /** keyLayout */ l: ["keyLayout", kKeyLayout] /** keyboard */ k: ["keyboard", [delay: number, interval: number, /** on Firefox */ screenRefreshRate?: number]] /** linkHintCharacters */ c: ["linkHintCharacters", string]; @@ -351,7 +352,7 @@ declare namespace SettingsNS { interface AutoSyncedItems extends DirectlySyncedItems {} interface FrontendSettingsSyncingItems extends AutoSyncedItems, ManuallySyncedItems {} - type FrontendComplexSyncingItems = Pick + type FrontendComplexSyncingItems = Pick interface DeclaredFrontendValues extends SelectValueType, DeclaredConstValues { } type AutoSyncedNameMap = SelectNameToKey