diff options
Diffstat (limited to 'src/content/presenters')
-rw-r--r-- | src/content/presenters/ConsoleFramePresenter.ts | 12 | ||||
-rw-r--r-- | src/content/presenters/FindPresenter.ts | 5 | ||||
-rw-r--r-- | src/content/presenters/FocusPresenter.ts | 13 | ||||
-rw-r--r-- | src/content/presenters/FollowPresenter.ts | 83 | ||||
-rw-r--r-- | src/content/presenters/Hint.ts | 66 | ||||
-rw-r--r-- | src/content/presenters/NavigationPresenter.ts | 19 | ||||
-rw-r--r-- | src/content/presenters/ScrollPresenter.ts | 20 |
7 files changed, 121 insertions, 97 deletions
diff --git a/src/content/presenters/ConsoleFramePresenter.ts b/src/content/presenters/ConsoleFramePresenter.ts index 63c78fb..26522c4 100644 --- a/src/content/presenters/ConsoleFramePresenter.ts +++ b/src/content/presenters/ConsoleFramePresenter.ts @@ -6,17 +6,17 @@ export default interface ConsoleFramePresenter { export class ConsoleFramePresenterImpl implements ConsoleFramePresenter { initialize(): void { - const iframe = document.createElement('iframe'); - iframe.src = browser.runtime.getURL('build/console.html'); - iframe.id = 'vimvixen-console-frame'; - iframe.className = 'vimvixen-console-frame'; + const iframe = document.createElement("iframe"); + iframe.src = browser.runtime.getURL("build/console.html"); + iframe.id = "vimvixen-console-frame"; + iframe.className = "vimvixen-console-frame"; document.body.append(iframe); } blur(): void { - const ele = document.getElementById('vimvixen-console-frame'); + const ele = document.getElementById("vimvixen-console-frame"); if (!ele) { - throw new Error('console frame not created'); + throw new Error("console frame not created"); } ele.blur(); } diff --git a/src/content/presenters/FindPresenter.ts b/src/content/presenters/FindPresenter.ts index 98d8088..117142c 100644 --- a/src/content/presenters/FindPresenter.ts +++ b/src/content/presenters/FindPresenter.ts @@ -1,4 +1,3 @@ - export default interface FindPresenter { find(keyword: string, backwards: boolean): boolean; @@ -18,7 +17,8 @@ interface MyWindow extends Window { aWrapAround?: boolean, aWholeWord?: boolean, aSearchInFrames?: boolean, - aShowDialog?: boolean): boolean; + aShowDialog?: boolean + ): boolean; } // eslint-disable-next-line no-var, vars-on-top, init-declarations @@ -29,7 +29,6 @@ export class FindPresenterImpl implements FindPresenter { const caseSensitive = false; const wrapScan = true; - // NOTE: aWholeWord dows not implemented, and aSearchInFrames does not work // because of same origin policy const found = window.find(keyword, caseSensitive, backwards, wrapScan); diff --git a/src/content/presenters/FocusPresenter.ts b/src/content/presenters/FocusPresenter.ts index 842c41e..4d70a6e 100644 --- a/src/content/presenters/FocusPresenter.ts +++ b/src/content/presenters/FocusPresenter.ts @@ -1,4 +1,4 @@ -import * as doms from '../../shared/utils/dom'; +import * as doms from "../../shared/utils/dom"; export default interface FocusPresenter { focusFirstElement(): boolean; @@ -6,9 +6,13 @@ export default interface FocusPresenter { export class FocusPresenterImpl implements FocusPresenter { focusFirstElement(): boolean { - const inputTypes = ['email', 'number', 'search', 'tel', 'text', 'url']; - const inputSelector = inputTypes.map(type => `input[type=${type}]`).join(','); - const targets = window.document.querySelectorAll(inputSelector + ',textarea'); + const inputTypes = ["email", "number", "search", "tel", "text", "url"]; + const inputSelector = inputTypes + .map((type) => `input[type=${type}]`) + .join(","); + const targets = window.document.querySelectorAll( + inputSelector + ",textarea" + ); const target = Array.from(targets).find(doms.isVisible); if (target instanceof HTMLInputElement) { target.focus(); @@ -20,4 +24,3 @@ export class FocusPresenterImpl implements FocusPresenter { return false; } } - diff --git a/src/content/presenters/FollowPresenter.ts b/src/content/presenters/FollowPresenter.ts index fef8140..8aef819 100644 --- a/src/content/presenters/FollowPresenter.ts +++ b/src/content/presenters/FollowPresenter.ts @@ -1,11 +1,18 @@ -import Hint, { InputHint, LinkHint } from './Hint'; -import * as doms from '../../shared/utils/dom'; +import Hint, { InputHint, LinkHint } from "./Hint"; +import * as doms from "../../shared/utils/dom"; const TARGET_SELECTOR = [ - 'a', 'button', 'input', 'textarea', 'area', - '[contenteditable=true]', '[contenteditable=""]', '[tabindex]', - '[role="button"]', 'summary' -].join(','); + "a", + "button", + "input", + "textarea", + "area", + "[contenteditable=true]", + '[contenteditable=""]', + "[tabindex]", + '[role="button"]', + "summary", +].join(","); interface Size { width: number; @@ -21,11 +28,9 @@ const inViewport = ( win: Window, element: Element, viewSize: Size, - framePosition: Point, + framePosition: Point ): boolean => { - const { - top, left, bottom, right - } = doms.viewportRect(element); + const { top, left, bottom, right } = doms.viewportRect(element); const doc = win.document; const frameWidth = doc.documentElement.clientWidth; const frameHeight = doc.documentElement.clientHeight; @@ -34,9 +39,12 @@ const inViewport = ( // out of frame return false; } - if (right + framePosition.x < 0 || bottom + framePosition.y < 0 || - left + framePosition.x > viewSize.width || - top + framePosition.y > viewSize.height) { + if ( + right + framePosition.x < 0 || + bottom + framePosition.y < 0 || + left + framePosition.x > viewSize.width || + top + framePosition.y > viewSize.height + ) { // out of viewport return false; } @@ -47,11 +55,11 @@ const isAriaHiddenOrAriaDisabled = (win: Window, element: Element): boolean => { if (!element || win.document.documentElement === element) { return false; } - for (const attr of ['aria-hidden', 'aria-disabled']) { + for (const attr of ["aria-hidden", "aria-disabled"]) { const value = element.getAttribute(attr); if (value !== null) { const hidden = value.toLowerCase(); - if (hidden === '' || hidden === 'true') { + if (hidden === "" || hidden === "true") { return true; } } @@ -88,8 +96,10 @@ export class FollowPresenterImpl implements FollowPresenter { const min = Math.min(targets.length, tags.length); for (let i = 0; i < min; ++i) { const target = targets[i]; - if (target instanceof HTMLAnchorElement || - target instanceof HTMLAreaElement) { + if ( + target instanceof HTMLAnchorElement || + target instanceof HTMLAreaElement + ) { this.hints.push(new LinkHint(target, tags[i])); } else { this.hints.push(new InputHint(target, tags[i])); @@ -98,35 +108,40 @@ export class FollowPresenterImpl implements FollowPresenter { } filterHints(prefix: string): void { - const shown = this.hints.filter(h => h.getTag().startsWith(prefix)); - const hidden = this.hints.filter(h => !h.getTag().startsWith(prefix)); + const shown = this.hints.filter((h) => h.getTag().startsWith(prefix)); + const hidden = this.hints.filter((h) => !h.getTag().startsWith(prefix)); - shown.forEach(h => h.show()); - hidden.forEach(h => h.hide()); + shown.forEach((h) => h.show()); + hidden.forEach((h) => h.hide()); } clearHints(): void { - this.hints.forEach(h => h.remove()); + this.hints.forEach((h) => h.remove()); this.hints = []; } getHint(tag: string): Hint | undefined { - return this.hints.find(h => h.getTag() === tag); + return this.hints.find((h) => h.getTag() === tag); } private getTargets(viewSize: Size, framePosition: Point): HTMLElement[] { const all = window.document.querySelectorAll(TARGET_SELECTOR); - const filtered = Array.prototype.filter.call(all, (element: HTMLElement) => { - const style = window.getComputedStyle(element); - - // AREA's 'display' in Browser style is 'none' - return (element.tagName === 'AREA' || style.display !== 'none') && - style.visibility !== 'hidden' && - (element as HTMLInputElement).type !== 'hidden' && - element.offsetHeight > 0 && - !isAriaHiddenOrAriaDisabled(window, element) && - inViewport(window, element, viewSize, framePosition); - }); + const filtered = Array.prototype.filter.call( + all, + (element: HTMLElement) => { + const style = window.getComputedStyle(element); + + // AREA's 'display' in Browser style is 'none' + return ( + (element.tagName === "AREA" || style.display !== "none") && + style.visibility !== "hidden" && + (element as HTMLInputElement).type !== "hidden" && + element.offsetHeight > 0 && + !isAriaHiddenOrAriaDisabled(window, element) && + inViewport(window, element, viewSize, framePosition) + ); + } + ); return filtered; } } diff --git a/src/content/presenters/Hint.ts b/src/content/presenters/Hint.ts index 44b8185..3f39060 100644 --- a/src/content/presenters/Hint.ts +++ b/src/content/presenters/Hint.ts @@ -1,4 +1,4 @@ -import * as doms from '../../shared/utils/dom'; +import * as doms from "../../shared/utils/dom"; interface Point { x: number; @@ -8,7 +8,7 @@ interface Point { const hintPosition = (element: Element): Point => { const { left, top, right, bottom } = doms.viewportRect(element); - if (element.tagName !== 'AREA') { + if (element.tagName !== "AREA") { return { x: left, y: top }; } @@ -28,17 +28,17 @@ export default abstract class Hint { const doc = target.ownerDocument; if (doc === null) { - throw new TypeError('ownerDocument is null'); + throw new TypeError("ownerDocument is null"); } const { x, y } = hintPosition(target); const { scrollX, scrollY } = window; - const hint = doc.createElement('span'); - hint.className = 'vimvixen-hint'; + const hint = doc.createElement("span"); + hint.className = "vimvixen-hint"; hint.textContent = tag; - hint.style.left = x + scrollX + 'px'; - hint.style.top = y + scrollY + 'px'; + hint.style.left = x + scrollX + "px"; + hint.style.top = y + scrollY + "px"; doc.body.append(hint); @@ -47,11 +47,11 @@ export default abstract class Hint { } show(): void { - this.hint.style.display = 'inline'; + this.hint.style.display = "inline"; } hide(): void { - this.hint.style.display = 'none'; + this.hint.style.display = "none"; } remove(): void { @@ -77,7 +77,7 @@ export class LinkHint extends Hint { } getLinkTarget(): string | null { - return this.target.getAttribute('target'); + return this.target.getAttribute("target"); } click(): void { @@ -97,31 +97,31 @@ export class InputHint extends Hint { activate(): void { const target = this.target; switch (target.tagName.toLowerCase()) { - case 'input': - switch ((target as HTMLInputElement).type) { - case 'file': - case 'checkbox': - case 'radio': - case 'submit': - case 'reset': - case 'button': - case 'image': - case 'color': - return target.click(); - default: + case "input": + switch ((target as HTMLInputElement).type) { + case "file": + case "checkbox": + case "radio": + case "submit": + case "reset": + case "button": + case "image": + case "color": + return target.click(); + default: + return target.focus(); + } + case "textarea": return target.focus(); - } - case 'textarea': - return target.focus(); - case 'button': - case 'summary': - return target.click(); - default: - if (doms.isContentEditable(target)) { - return target.focus(); - } else if (target.hasAttribute('tabindex')) { + case "button": + case "summary": return target.click(); - } + default: + if (doms.isContentEditable(target)) { + return target.focus(); + } else if (target.hasAttribute("tabindex")) { + return target.click(); + } } } } diff --git a/src/content/presenters/NavigationPresenter.ts b/src/content/presenters/NavigationPresenter.ts index 951e62a..3edcd12 100644 --- a/src/content/presenters/NavigationPresenter.ts +++ b/src/content/presenters/NavigationPresenter.ts @@ -8,7 +8,7 @@ export default interface NavigationPresenter { openLinkNext(): void; } -const REL_PATTERN: {[key: string]: RegExp} = { +const REL_PATTERN: { [key: string]: RegExp } = { prev: /^(?:prev(?:ious)?|older)\b|\u2039|\u2190|\xab|\u226a|<</i, next: /^(?:next|newer)\b|\u203a|\u2192|\xbb|\u226b|>>/i, }; @@ -18,7 +18,7 @@ const REL_PATTERN: {[key: string]: RegExp} = { // eslint-disable-next-line func-style function selectLast<E extends Element>( selector: string, - filter?: (e: E) => boolean, + filter?: (e: E) => boolean ): E | null { let nodes = Array.from( window.document.querySelectorAll(selector) as NodeListOf<E> @@ -40,15 +40,15 @@ export class NavigationPresenterImpl implements NavigationPresenter { } openLinkPrev(): void { - this.linkRel('prev'); + this.linkRel("prev"); } openLinkNext(): void { - this.linkRel('next'); + this.linkRel("next"); } // Code common to linkPrev and linkNext which navigates to the specified page. - private linkRel(rel: 'prev' | 'next'): void { + private linkRel(rel: "prev" | "next"): void { const link = selectLast<HTMLLinkElement>(`link[rel~=${rel}][href]`); if (link) { window.location.href = link.href; @@ -57,10 +57,11 @@ export class NavigationPresenterImpl implements NavigationPresenter { const pattern = REL_PATTERN[rel]; - const a = selectLast<HTMLAnchorElement>(`a[rel~=${rel}][href]`) || - // `innerText` is much slower than `textContent`, but produces much better - // (i.e. less unexpected) results - selectLast('a[href]', lnk => pattern.test(lnk.innerText)); + const a = + selectLast<HTMLAnchorElement>(`a[rel~=${rel}][href]`) || + // `innerText` is much slower than `textContent`, but produces much better + // (i.e. less unexpected) results + selectLast("a[href]", (lnk) => pattern.test(lnk.innerText)); if (a) { a.click(); diff --git a/src/content/presenters/ScrollPresenter.ts b/src/content/presenters/ScrollPresenter.ts index 387ab62..f1a6402 100644 --- a/src/content/presenters/ScrollPresenter.ts +++ b/src/content/presenters/ScrollPresenter.ts @@ -1,4 +1,4 @@ -import * as doms from '../../shared/utils/dom'; +import * as doms from "../../shared/utils/dom"; const SCROLL_DELTA_X = 64; const SCROLL_DELTA_Y = 64; @@ -9,13 +9,19 @@ let lastTimeoutId: number | null = null; const isScrollableStyle = (element: Element): boolean => { const { overflowX, overflowY } = window.getComputedStyle(element); - return !(overflowX !== 'scroll' && overflowX !== 'auto' && - overflowY !== 'scroll' && overflowY !== 'auto'); + return !( + overflowX !== "scroll" && + overflowX !== "auto" && + overflowY !== "scroll" && + overflowY !== "auto" + ); }; const isOverflowed = (element: Element): boolean => { - return element.scrollWidth > element.clientWidth || - element.scrollHeight > element.clientHeight; + return ( + element.scrollWidth > element.clientWidth || + element.scrollHeight > element.clientHeight + ); }; // Find a visiable and scrollable element by depth-first search. Currently @@ -73,7 +79,7 @@ class Scroller { this.element.scrollTo({ left: x, top: y, - behavior: 'smooth', + behavior: "smooth", }); this.prepareReset(); } @@ -94,7 +100,7 @@ class Scroller { } } -export type Point = { x: number, y: number }; +export type Point = { x: number; y: number }; export default interface ScrollPresenter { getScroll(): Point; |