aboutsummaryrefslogtreecommitdiff
path: root/src/content/presenters/Hint.ts
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2019-05-19 15:59:05 +0900
committerGitHub <noreply@github.com>2019-05-19 15:59:05 +0900
commit3f4bc62ed515f1c5da90ee1c3e42f3d435ea6e39 (patch)
tree8af9f8e5b12d007ce9628b40f3046b73f18e29f8 /src/content/presenters/Hint.ts
parent6ec560bca33e774ff7e363270c423c919fdcf4ce (diff)
parentc4dcdff9844e2404e3bc035f4cea9fce2f7770ab (diff)
Merge pull request #587 from ueokande/refactor-content
Refactor content scripts
Diffstat (limited to 'src/content/presenters/Hint.ts')
-rw-r--r--src/content/presenters/Hint.ts127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/content/presenters/Hint.ts b/src/content/presenters/Hint.ts
new file mode 100644
index 0000000..60c0f4c
--- /dev/null
+++ b/src/content/presenters/Hint.ts
@@ -0,0 +1,127 @@
+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();
+ }
+ }
+ }
+}