From 4c9d0433a6ac851e72d50d6fb0451baa9d35fd35 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 15 Oct 2017 09:02:09 +0900 Subject: make top-content component and frame-content component --- test/content/components/common/follow.html | 12 ++++++ test/content/components/common/follow.test.js | 18 +++++++++ test/content/components/common/hint.html | 1 + test/content/components/common/hint.test.js | 58 +++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 test/content/components/common/follow.html create mode 100644 test/content/components/common/follow.test.js create mode 100644 test/content/components/common/hint.html create mode 100644 test/content/components/common/hint.test.js (limited to 'test/content/components/common') diff --git a/test/content/components/common/follow.html b/test/content/components/common/follow.html new file mode 100644 index 0000000..eb0decd --- /dev/null +++ b/test/content/components/common/follow.html @@ -0,0 +1,12 @@ + + + + link + invisible 1 + invisible 2 + not link +
link
+
link
+
link
+ + diff --git a/test/content/components/common/follow.test.js b/test/content/components/common/follow.test.js new file mode 100644 index 0000000..97bd1d2 --- /dev/null +++ b/test/content/components/common/follow.test.js @@ -0,0 +1,18 @@ +import { expect } from "chai"; +import FollowComponent from 'content/components/common/follow'; + +describe('FollowComponent', () => { + describe('#getTargetElements', () => { + beforeEach(() => { + document.body.innerHTML = __html__['test/content/components/common/follow.html']; + }); + + it('returns visible links', () => { + let targets = FollowComponent.getTargetElements(window); + expect(targets).to.have.lengthOf(3); + + let ids = Array.prototype.map.call(targets, (e) => e.id); + expect(ids).to.include.members(['visible_a', 'editable_div_1', 'editable_div_2']); + }); + }); +}); diff --git a/test/content/components/common/hint.html b/test/content/components/common/hint.html new file mode 100644 index 0000000..b50c5fe --- /dev/null +++ b/test/content/components/common/hint.html @@ -0,0 +1 @@ +link diff --git a/test/content/components/common/hint.test.js b/test/content/components/common/hint.test.js new file mode 100644 index 0000000..ced2fde --- /dev/null +++ b/test/content/components/common/hint.test.js @@ -0,0 +1,58 @@ +import { expect } from "chai"; +import Hint from 'content/components/common/hint'; + +describe('Hint class', () => { + beforeEach(() => { + document.body.innerHTML = __html__['test/content/components/common/hint.html']; + }); + + describe('#constructor', () => { + it('creates a hint element with tag name', () => { + let link = document.getElementById('test-link'); + let hint = new Hint(link, 'abc'); + expect(hint.element.textContent.trim()).to.be.equal('abc'); + }); + + it('throws an exception when non-element given', () => { + expect(() => new Hint(window, 'abc')).to.throw(TypeError); + }); + }); + + describe('#show', () => { + it('shows an element', () => { + let link = document.getElementById('test-link'); + let hint = new Hint(link, 'abc'); + hint.hide(); + hint.show(); + + expect(hint.element.style.display).to.not.equal('none'); + }); + }); + + describe('#hide', () => { + it('hides an element', () => { + let link = document.getElementById('test-link'); + let hint = new Hint(link, 'abc'); + hint.hide(); + + expect(hint.element.style.display).to.equal('none'); + }); + }); + + describe('#remove', () => { + it('removes an element', () => { + let link = document.getElementById('test-link'); + let hint = new Hint(link, 'abc'); + + expect(hint.element.parentElement).to.not.be.null; + hint.remove(); + expect(hint.element.parentElement).to.be.null; + }); + }); + + describe('#activate', () => { + // TODO test activations + }); +}); + + -- cgit v1.2.3 From 45b3b0510fd0babe392ee46319fa345433af7736 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sun, 15 Oct 2017 21:59:37 +0900 Subject: follow links for multi-frames in viewport --- .eslintrc | 1 + src/content/components/common/follow.js | 31 ++++++++++++++-------- .../components/top-content/follow-controller.js | 18 ++++++++++++- test/content/components/common/follow.test.js | 5 +++- 4 files changed, 42 insertions(+), 13 deletions(-) (limited to 'test/content/components/common') diff --git a/.eslintrc b/.eslintrc index d244c8f..5636171 100644 --- a/.eslintrc +++ b/.eslintrc @@ -29,6 +29,7 @@ "id-length": "off", "indent": ["error", 2], "jsx-quotes": ["error", "prefer-single"], + "max-params": ["error", 5], "max-statements": ["error", 15], "multiline-ternary": "off", "newline-after-var": "off", diff --git a/src/content/components/common/follow.js b/src/content/components/common/follow.js index a5fbab4..92d8822 100644 --- a/src/content/components/common/follow.js +++ b/src/content/components/common/follow.js @@ -6,16 +6,25 @@ const TARGET_SELECTOR = [ '[contenteditable=true]', '[contenteditable=""]' ].join(','); -const inWindow = (win, element) => { +const inViewport = (win, element, viewSize, framePosition) => { let { top, left, bottom, right } = element.getBoundingClientRect(); let doc = win.doc; - return ( - top >= 0 && left >= 0 && - bottom <= (win.innerHeight || doc.documentElement.clientHeight) && - right <= (win.innerWidth || doc.documentElement.clientWidth) - ); + let frameWidth = win.innerWidth || doc.documentElement.clientWidth; + let frameHeight = win.innerHeight || doc.documentElement.clientHeight; + + if (right < 0 || bottom < 0 || top > frameHeight || left > frameWidth) { + // out of frame + return false; + } + if (right + framePosition.x < 0 || bottom + framePosition.y < 0 || + left + framePosition.x > viewSize.width || + top + framePosition.y > viewSize.height) { + // out of viewport + return false; + } + return true; }; export default class Follow { @@ -60,8 +69,8 @@ export default class Follow { }); } - countHints(sender) { - this.targets = Follow.getTargetElements(this.win); + countHints(sender, viewSize, framePosition) { + this.targets = Follow.getTargetElements(this.win, viewSize, framePosition); sender.postMessage(JSON.stringify({ type: messages.FOLLOW_RESPONSE_COUNT_TARGETS, count: this.targets.length, @@ -133,7 +142,7 @@ export default class Follow { onMessage(message, sender) { switch (message.type) { case messages.FOLLOW_REQUEST_COUNT_TARGETS: - return this.countHints(sender); + return this.countHints(sender, message.viewSize, message.framePosition); case messages.FOLLOW_CREATE_HINTS: return this.createHints(message.keysArray, message.newTab); case messages.FOLLOW_SHOW_HINTS: @@ -145,7 +154,7 @@ export default class Follow { } } - static getTargetElements(win) { + static getTargetElements(win, viewSize, framePosition) { let all = win.document.querySelectorAll(TARGET_SELECTOR); let filtered = Array.prototype.filter.call(all, (element) => { let style = win.getComputedStyle(element); @@ -153,7 +162,7 @@ export default class Follow { style.visibility !== 'hidden' && element.type !== 'hidden' && element.offsetHeight > 0 && - inWindow(win, element); + inViewport(win, element, viewSize, framePosition); }); return filtered; } diff --git a/src/content/components/top-content/follow-controller.js b/src/content/components/top-content/follow-controller.js index 0474690..29f40b3 100644 --- a/src/content/components/top-content/follow-controller.js +++ b/src/content/components/top-content/follow-controller.js @@ -87,8 +87,24 @@ export default class FollowController { count() { this.producer = new HintKeyProducer(DEFAULT_HINT_CHARSET); - broadcastMessage(this.win, { + let doc = this.win.document; + let viewWidth = this.win.innerWidth || doc.documentElement.clientWidth; + let viewHeight = this.win.innerHeight || doc.documentElement.clientHeight; + let frameElements = this.win.document.querySelectorAll('frame,iframe'); + + this.win.postMessage(JSON.stringify({ type: messages.FOLLOW_REQUEST_COUNT_TARGETS, + viewSize: { width: viewWidth, height: viewHeight }, + framePosition: { x: 0, y: 0 }, + }), '*'); + frameElements.forEach((element) => { + let { left: frameX, top: frameY } = element.getBoundingClientRect(); + let message = JSON.stringify({ + type: messages.FOLLOW_REQUEST_COUNT_TARGETS, + viewSize: { width: viewWidth, height: viewHeight }, + framePosition: { x: frameX, y: frameY }, + }); + element.contentWindow.postMessage(message, '*'); }); } diff --git a/test/content/components/common/follow.test.js b/test/content/components/common/follow.test.js index 97bd1d2..1fc935e 100644 --- a/test/content/components/common/follow.test.js +++ b/test/content/components/common/follow.test.js @@ -8,7 +8,10 @@ describe('FollowComponent', () => { }); it('returns visible links', () => { - let targets = FollowComponent.getTargetElements(window); + let targets = FollowComponent.getTargetElements( + window, + { width: window.innerWidth, height: window.innerHeight }, + { x: 0, y: 0 }); expect(targets).to.have.lengthOf(3); let ids = Array.prototype.map.call(targets, (e) => e.id); -- cgit v1.2.3