A fork of https://github.com/ueokande/vim-vixen
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
2.5 KiB
127 lines
2.5 KiB
import * as doms from '../../shared/utils/dom'; |
|
|
|
interface Point { |
|
x: number; |
|
y: number; |
|
} |
|
|
|
const hintPosition = (element: Element): Point => { |
|
let { left, top, right, bottom } = doms.viewportRect(element); |
|
|
|
if (element.tagName !== 'AREA') { |
|
return { x: left, y: top }; |
|
} |
|
|
|
return { |
|
x: (left + right) / 2, |
|
y: (top + bottom) / 2, |
|
}; |
|
}; |
|
|
|
export default abstract class Hint { |
|
private hint: HTMLElement; |
|
|
|
private tag: string; |
|
|
|
constructor(target: HTMLElement, tag: string) { |
|
this.tag = tag; |
|
|
|
let doc = target.ownerDocument; |
|
if (doc === null) { |
|
throw new TypeError('ownerDocument is null'); |
|
} |
|
|
|
let { x, y } = hintPosition(target); |
|
let { scrollX, scrollY } = window; |
|
|
|
let hint = doc.createElement('span'); |
|
hint.className = 'vimvixen-hint'; |
|
hint.textContent = tag; |
|
hint.style.left = x + scrollX + 'px'; |
|
hint.style.top = y + scrollY + 'px'; |
|
|
|
doc.body.append(hint); |
|
|
|
this.hint = hint; |
|
this.show(); |
|
} |
|
|
|
show(): void { |
|
this.hint.style.display = 'inline'; |
|
} |
|
|
|
hide(): void { |
|
this.hint.style.display = 'none'; |
|
} |
|
|
|
remove(): void { |
|
this.hint.remove(); |
|
} |
|
|
|
getTag(): string { |
|
return this.tag; |
|
} |
|
} |
|
|
|
export class LinkHint extends Hint { |
|
private target: HTMLAnchorElement | HTMLAreaElement; |
|
|
|
constructor(target: HTMLAnchorElement | HTMLAreaElement, tag: string) { |
|
super(target, tag); |
|
|
|
this.target = target; |
|
} |
|
|
|
getLink(): string { |
|
return this.target.href; |
|
} |
|
|
|
getLinkTarget(): string | null { |
|
return this.target.getAttribute('target'); |
|
} |
|
|
|
click(): void { |
|
this.target.click(); |
|
} |
|
} |
|
|
|
export class InputHint extends Hint { |
|
private target: HTMLElement; |
|
|
|
constructor(target: HTMLElement, tag: string) { |
|
super(target, tag); |
|
|
|
this.target = target; |
|
} |
|
|
|
activate(): void { |
|
let 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: |
|
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')) { |
|
return target.click(); |
|
} |
|
} |
|
} |
|
}
|
|
|