Popup refactor (#518)
* Add default * Convert function to non-static * Remove static for private functions * Replace .call * Move functions with side effects into a synchronous prepare function * Rename variables with "container" to "frame" in _initializeFrame * Rename variables with "container" to "frame" * Rename getContainer to getFrame * Rename getContainerRect to getFrameRect * Organize and simplify * Fix incorrect change of "popup" => "this" * Move initial _updateVisibility into prepare()
This commit is contained in:
parent
3949db26d7
commit
48cf646973
@ -41,7 +41,7 @@ class Frontend {
|
|||||||
this._optionsUpdatePending = false;
|
this._optionsUpdatePending = false;
|
||||||
this._textScanner = new TextScanner({
|
this._textScanner = new TextScanner({
|
||||||
node: window,
|
node: window,
|
||||||
ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getContainer()],
|
ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getFrame()],
|
||||||
ignorePoint: (x, y) => this._popup.containsPoint(x, y),
|
ignorePoint: (x, y) => this._popup.containsPoint(x, y),
|
||||||
search: this._search.bind(this)
|
search: this._search.bind(this)
|
||||||
});
|
});
|
||||||
|
@ -87,6 +87,7 @@ class PopupFactory {
|
|||||||
popup.setParent(parent);
|
popup.setParent(parent);
|
||||||
}
|
}
|
||||||
this._popups.set(id, popup);
|
this._popups.set(id, popup);
|
||||||
|
popup.prepare();
|
||||||
return popup;
|
return popup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +169,7 @@ class PopupFactory {
|
|||||||
|
|
||||||
_convertPopupPointToRootPagePoint(popup, x, y) {
|
_convertPopupPointToRootPagePoint(popup, x, y) {
|
||||||
if (popup.parent !== null) {
|
if (popup.parent !== null) {
|
||||||
const popupRect = popup.parent.getContainerRect();
|
const popupRect = popup.parent.getFrameRect();
|
||||||
x += popupRect.x;
|
x += popupRect.x;
|
||||||
y += popupRect.y;
|
y += popupRect.y;
|
||||||
}
|
}
|
||||||
|
@ -36,23 +36,18 @@ class Popup {
|
|||||||
this._options = null;
|
this._options = null;
|
||||||
this._optionsContext = null;
|
this._optionsContext = null;
|
||||||
this._contentScale = 1.0;
|
this._contentScale = 1.0;
|
||||||
this._containerSizeContentScale = null;
|
|
||||||
this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');
|
this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');
|
||||||
this._previousOptionsContextSource = null;
|
this._previousOptionsContextSource = null;
|
||||||
this._containerSecret = null;
|
|
||||||
this._containerToken = null;
|
|
||||||
|
|
||||||
this._container = document.createElement('iframe');
|
this._frameSizeContentScale = null;
|
||||||
this._container.className = 'yomichan-float';
|
this._frameSecret = null;
|
||||||
this._container.addEventListener('mousedown', (e) => e.stopPropagation());
|
this._frameToken = null;
|
||||||
this._container.addEventListener('scroll', (e) => e.stopPropagation());
|
this._frame = document.createElement('iframe');
|
||||||
this._container.style.width = '0px';
|
this._frame.className = 'yomichan-float';
|
||||||
this._container.style.height = '0px';
|
this._frame.style.width = '0';
|
||||||
this._container.addEventListener('load', this._onFrameLoad.bind(this));
|
this._frame.style.height = '0';
|
||||||
|
|
||||||
this._fullscreenEventListeners = new EventListenerCollection();
|
this._fullscreenEventListeners = new EventListenerCollection();
|
||||||
|
|
||||||
this._updateVisibility();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public properties
|
// Public properties
|
||||||
@ -79,6 +74,13 @@ class Popup {
|
|||||||
|
|
||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
this._updateVisibility();
|
||||||
|
this._frame.addEventListener('mousedown', (e) => e.stopPropagation());
|
||||||
|
this._frame.addEventListener('scroll', (e) => e.stopPropagation());
|
||||||
|
this._frame.addEventListener('load', this._onFrameLoad.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
isProxy() {
|
isProxy() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -118,7 +120,7 @@ class Popup {
|
|||||||
|
|
||||||
async containsPoint(x, y) {
|
async containsPoint(x, y) {
|
||||||
for (let popup = this; popup !== null && popup.isVisibleSync(); popup = popup._child) {
|
for (let popup = this; popup !== null && popup.isVisibleSync(); popup = popup._child) {
|
||||||
const rect = popup._container.getBoundingClientRect();
|
const rect = popup._frame.getBoundingClientRect();
|
||||||
if (x >= rect.left && y >= rect.top && x < rect.right && y < rect.bottom) {
|
if (x >= rect.left && y >= rect.top && x < rect.right && y < rect.bottom) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -173,12 +175,12 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTheme() {
|
updateTheme() {
|
||||||
this._container.dataset.yomichanTheme = this._options.general.popupOuterTheme;
|
this._frame.dataset.yomichanTheme = this._options.general.popupOuterTheme;
|
||||||
this._container.dataset.yomichanSiteColor = this._getSiteColor();
|
this._frame.dataset.yomichanSiteColor = this._getSiteColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCustomOuterCss(css, useWebExtensionApi) {
|
async setCustomOuterCss(css, useWebExtensionApi) {
|
||||||
return await Popup._injectStylesheet(
|
return await this._injectStylesheet(
|
||||||
'yomichan-popup-outer-user-stylesheet',
|
'yomichan-popup-outer-user-stylesheet',
|
||||||
'code',
|
'code',
|
||||||
css,
|
css,
|
||||||
@ -190,12 +192,12 @@ class Popup {
|
|||||||
this._childrenSupported = value;
|
this._childrenSupported = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContainer() {
|
getFrame() {
|
||||||
return this._container;
|
return this._frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContainerRect() {
|
getFrameRect() {
|
||||||
return this._container.getBoundingClientRect();
|
return this._frame.getBoundingClientRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private functions
|
// Private functions
|
||||||
@ -220,11 +222,11 @@ class Popup {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const tokenMap = new Map();
|
const tokenMap = new Map();
|
||||||
let timer = null;
|
let timer = null;
|
||||||
let containerLoadedResolve = null;
|
let frameLoadedResolve = null;
|
||||||
let containerLoadedReject = null;
|
let frameLoadedReject = null;
|
||||||
const containerLoaded = new Promise((resolve2, reject2) => {
|
const frameLoaded = new Promise((resolve2, reject2) => {
|
||||||
containerLoadedResolve = resolve2;
|
frameLoadedResolve = resolve2;
|
||||||
containerLoadedReject = reject2;
|
frameLoadedReject = reject2;
|
||||||
});
|
});
|
||||||
|
|
||||||
const postMessage = (action, params) => {
|
const postMessage = (action, params) => {
|
||||||
@ -252,7 +254,7 @@ class Popup {
|
|||||||
if (!isObject(message)) { return; }
|
if (!isObject(message)) { return; }
|
||||||
const {action, params} = message;
|
const {action, params} = message;
|
||||||
if (!isObject(params)) { return; }
|
if (!isObject(params)) { return; }
|
||||||
await containerLoaded;
|
await frameLoaded;
|
||||||
if (timer === null) { return; } // Done
|
if (timer === null) { return; } // Done
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@ -282,7 +284,7 @@ class Popup {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onLoad = () => {
|
const onLoad = () => {
|
||||||
if (containerLoadedResolve === null) {
|
if (frameLoadedResolve === null) {
|
||||||
cleanup();
|
cleanup();
|
||||||
reject(new Error('Unexpected load event'));
|
reject(new Error('Unexpected load event'));
|
||||||
return;
|
return;
|
||||||
@ -292,9 +294,9 @@ class Popup {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
containerLoadedResolve();
|
frameLoadedResolve();
|
||||||
containerLoadedResolve = null;
|
frameLoadedResolve = null;
|
||||||
containerLoadedReject = null;
|
frameLoadedReject = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
@ -302,10 +304,10 @@ class Popup {
|
|||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = null;
|
timer = null;
|
||||||
|
|
||||||
containerLoadedResolve = null;
|
frameLoadedResolve = null;
|
||||||
if (containerLoadedReject !== null) {
|
if (frameLoadedReject !== null) {
|
||||||
containerLoadedReject(new Error('Terminated'));
|
frameLoadedReject(new Error('Terminated'));
|
||||||
containerLoadedReject = null;
|
frameLoadedReject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.runtime.onMessage.removeListener(onMessage);
|
chrome.runtime.onMessage.removeListener(onMessage);
|
||||||
@ -322,7 +324,7 @@ class Popup {
|
|||||||
frame.addEventListener('load', onLoad);
|
frame.addEventListener('load', onLoad);
|
||||||
|
|
||||||
// Prevent unhandled rejections
|
// Prevent unhandled rejections
|
||||||
containerLoaded.catch(() => {}); // NOP
|
frameLoaded.catch(() => {}); // NOP
|
||||||
|
|
||||||
setupFrame(frame);
|
setupFrame(frame);
|
||||||
});
|
});
|
||||||
@ -331,15 +333,15 @@ class Popup {
|
|||||||
async _createInjectPromise() {
|
async _createInjectPromise() {
|
||||||
this._injectStyles();
|
this._injectStyles();
|
||||||
|
|
||||||
const {secret, token} = await this._initializeFrame(this._container, this._targetOrigin, this._frameId, (frame) => {
|
const {secret, token} = await this._initializeFrame(this._frame, this._targetOrigin, this._frameId, (frame) => {
|
||||||
frame.removeAttribute('src');
|
frame.removeAttribute('src');
|
||||||
frame.removeAttribute('srcdoc');
|
frame.removeAttribute('srcdoc');
|
||||||
frame.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));
|
frame.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));
|
||||||
this._observeFullscreen(true);
|
this._observeFullscreen(true);
|
||||||
this._onFullscreenChanged();
|
this._onFullscreenChanged();
|
||||||
});
|
});
|
||||||
this._containerSecret = secret;
|
this._frameSecret = secret;
|
||||||
this._containerToken = token;
|
this._frameToken = token;
|
||||||
|
|
||||||
// Configure
|
// Configure
|
||||||
const messageId = yomichan.generateId(16);
|
const messageId = yomichan.generateId(16);
|
||||||
@ -374,22 +376,22 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_resetFrame() {
|
_resetFrame() {
|
||||||
const parent = this._container.parentNode;
|
const parent = this._frame.parentNode;
|
||||||
if (parent !== null) {
|
if (parent !== null) {
|
||||||
parent.removeChild(this._container);
|
parent.removeChild(this._frame);
|
||||||
}
|
}
|
||||||
this._container.removeAttribute('src');
|
this._frame.removeAttribute('src');
|
||||||
this._container.removeAttribute('srcdoc');
|
this._frame.removeAttribute('srcdoc');
|
||||||
|
|
||||||
this._containerSecret = null;
|
this._frameSecret = null;
|
||||||
this._containerToken = null;
|
this._frameToken = null;
|
||||||
this._injectPromise = null;
|
this._injectPromise = null;
|
||||||
this._injectPromiseComplete = false;
|
this._injectPromiseComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _injectStyles() {
|
async _injectStyles() {
|
||||||
try {
|
try {
|
||||||
await Popup._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true);
|
await this._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// NOP
|
// NOP
|
||||||
}
|
}
|
||||||
@ -426,8 +428,8 @@ class Popup {
|
|||||||
|
|
||||||
_onFullscreenChanged() {
|
_onFullscreenChanged() {
|
||||||
const parent = this._getFrameParentElement();
|
const parent = this._getFrameParentElement();
|
||||||
if (parent !== null && this._container.parentNode !== parent) {
|
if (parent !== null && this._frame.parentNode !== parent) {
|
||||||
parent.appendChild(this._container);
|
parent.appendChild(this._frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,31 +437,31 @@ class Popup {
|
|||||||
await this._inject();
|
await this._inject();
|
||||||
|
|
||||||
const optionsGeneral = this._options.general;
|
const optionsGeneral = this._options.general;
|
||||||
const container = this._container;
|
const frame = this._frame;
|
||||||
const containerRect = container.getBoundingClientRect();
|
const frameRect = frame.getBoundingClientRect();
|
||||||
const getPosition = (
|
|
||||||
writingMode === 'horizontal-tb' || optionsGeneral.popupVerticalTextPosition === 'default' ?
|
|
||||||
Popup._getPositionForHorizontalText :
|
|
||||||
Popup._getPositionForVerticalText
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewport = Popup._getViewport(optionsGeneral.popupScaleRelativeToVisualViewport);
|
const viewport = this._getViewport(optionsGeneral.popupScaleRelativeToVisualViewport);
|
||||||
const scale = this._contentScale;
|
const scale = this._contentScale;
|
||||||
const scaleRatio = this._containerSizeContentScale === null ? 1.0 : scale / this._containerSizeContentScale;
|
const scaleRatio = this._frameSizeContentScale === null ? 1.0 : scale / this._frameSizeContentScale;
|
||||||
this._containerSizeContentScale = scale;
|
this._frameSizeContentScale = scale;
|
||||||
let [x, y, width, height, below] = getPosition(
|
const getPositionArgs = [
|
||||||
elementRect,
|
elementRect,
|
||||||
Math.max(containerRect.width * scaleRatio, optionsGeneral.popupWidth * scale),
|
Math.max(frameRect.width * scaleRatio, optionsGeneral.popupWidth * scale),
|
||||||
Math.max(containerRect.height * scaleRatio, optionsGeneral.popupHeight * scale),
|
Math.max(frameRect.height * scaleRatio, optionsGeneral.popupHeight * scale),
|
||||||
viewport,
|
viewport,
|
||||||
scale,
|
scale,
|
||||||
optionsGeneral,
|
optionsGeneral,
|
||||||
writingMode
|
writingMode
|
||||||
|
];
|
||||||
|
let [x, y, width, height, below] = (
|
||||||
|
writingMode === 'horizontal-tb' || optionsGeneral.popupVerticalTextPosition === 'default' ?
|
||||||
|
this._getPositionForHorizontalText(...getPositionArgs) :
|
||||||
|
this._getPositionForVerticalText(...getPositionArgs)
|
||||||
);
|
);
|
||||||
|
|
||||||
const fullWidth = (optionsGeneral.popupDisplayMode === 'full-width');
|
const fullWidth = (optionsGeneral.popupDisplayMode === 'full-width');
|
||||||
container.classList.toggle('yomichan-float-full-width', fullWidth);
|
frame.classList.toggle('yomichan-float-full-width', fullWidth);
|
||||||
container.classList.toggle('yomichan-float-above', !below);
|
frame.classList.toggle('yomichan-float-above', !below);
|
||||||
|
|
||||||
if (optionsGeneral.popupDisplayMode === 'full-width') {
|
if (optionsGeneral.popupDisplayMode === 'full-width') {
|
||||||
x = viewport.left;
|
x = viewport.left;
|
||||||
@ -467,10 +469,10 @@ class Popup {
|
|||||||
width = viewport.right - viewport.left;
|
width = viewport.right - viewport.left;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.style.left = `${x}px`;
|
frame.style.left = `${x}px`;
|
||||||
container.style.top = `${y}px`;
|
frame.style.top = `${y}px`;
|
||||||
container.style.width = `${width}px`;
|
frame.style.width = `${width}px`;
|
||||||
container.style.height = `${height}px`;
|
frame.style.height = `${height}px`;
|
||||||
|
|
||||||
this._setVisible(true);
|
this._setVisible(true);
|
||||||
if (this._child !== null) {
|
if (this._child !== null) {
|
||||||
@ -484,20 +486,20 @@ class Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updateVisibility() {
|
_updateVisibility() {
|
||||||
this._container.style.setProperty('visibility', this.isVisibleSync() ? 'visible' : 'hidden', 'important');
|
this._frame.style.setProperty('visibility', this.isVisibleSync() ? 'visible' : 'hidden', 'important');
|
||||||
}
|
}
|
||||||
|
|
||||||
_focusParent() {
|
_focusParent() {
|
||||||
if (this._parent !== null) {
|
if (this._parent !== null) {
|
||||||
// Chrome doesn't like focusing iframe without contentWindow.
|
// Chrome doesn't like focusing iframe without contentWindow.
|
||||||
const contentWindow = this._parent._container.contentWindow;
|
const contentWindow = this._parent.getFrame().contentWindow;
|
||||||
if (contentWindow !== null) {
|
if (contentWindow !== null) {
|
||||||
contentWindow.focus();
|
contentWindow.focus();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Firefox doesn't like focusing window without first blurring the iframe.
|
// Firefox doesn't like focusing window without first blurring the iframe.
|
||||||
// this.container.contentWindow.blur() doesn't work on Firefox for some reason.
|
// this._frame.contentWindow.blur() doesn't work on Firefox for some reason.
|
||||||
this._container.blur();
|
this._frame.blur();
|
||||||
// This is needed for Chrome.
|
// This is needed for Chrome.
|
||||||
window.focus();
|
window.focus();
|
||||||
}
|
}
|
||||||
@ -507,19 +509,19 @@ class Popup {
|
|||||||
const color = [255, 255, 255];
|
const color = [255, 255, 255];
|
||||||
const {documentElement, body} = document;
|
const {documentElement, body} = document;
|
||||||
if (documentElement !== null) {
|
if (documentElement !== null) {
|
||||||
Popup._addColor(color, Popup._getColorInfo(window.getComputedStyle(documentElement).backgroundColor));
|
this._addColor(color, window.getComputedStyle(documentElement).backgroundColor);
|
||||||
}
|
}
|
||||||
if (body !== null) {
|
if (body !== null) {
|
||||||
Popup._addColor(color, Popup._getColorInfo(window.getComputedStyle(body).backgroundColor));
|
this._addColor(color, window.getComputedStyle(body).backgroundColor);
|
||||||
}
|
}
|
||||||
const dark = (color[0] < 128 && color[1] < 128 && color[2] < 128);
|
const dark = (color[0] < 128 && color[1] < 128 && color[2] < 128);
|
||||||
return dark ? 'dark' : 'light';
|
return dark ? 'dark' : 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
_invokeApi(action, params={}) {
|
_invokeApi(action, params={}) {
|
||||||
const secret = this._containerSecret;
|
const secret = this._frameSecret;
|
||||||
const token = this._containerToken;
|
const token = this._frameToken;
|
||||||
const contentWindow = this._container.contentWindow;
|
const contentWindow = this._frame.contentWindow;
|
||||||
if (secret === null || token === null || contentWindow === null) { return; }
|
if (secret === null || token === null || contentWindow === null) { return; }
|
||||||
|
|
||||||
contentWindow.postMessage({action, params, secret, token}, this._targetOrigin);
|
contentWindow.postMessage({action, params, secret, token}, this._targetOrigin);
|
||||||
@ -541,12 +543,12 @@ class Popup {
|
|||||||
return fullscreenElement;
|
return fullscreenElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) {
|
_getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) {
|
||||||
const preferBelow = (optionsGeneral.popupHorizontalTextPosition === 'below');
|
const preferBelow = (optionsGeneral.popupHorizontalTextPosition === 'below');
|
||||||
const horizontalOffset = optionsGeneral.popupHorizontalOffset * offsetScale;
|
const horizontalOffset = optionsGeneral.popupHorizontalOffset * offsetScale;
|
||||||
const verticalOffset = optionsGeneral.popupVerticalOffset * offsetScale;
|
const verticalOffset = optionsGeneral.popupVerticalOffset * offsetScale;
|
||||||
|
|
||||||
const [x, w] = Popup._getConstrainedPosition(
|
const [x, w] = this._getConstrainedPosition(
|
||||||
elementRect.right - horizontalOffset,
|
elementRect.right - horizontalOffset,
|
||||||
elementRect.left + horizontalOffset,
|
elementRect.left + horizontalOffset,
|
||||||
width,
|
width,
|
||||||
@ -554,7 +556,7 @@ class Popup {
|
|||||||
viewport.right,
|
viewport.right,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
const [y, h, below] = Popup._getConstrainedPositionBinary(
|
const [y, h, below] = this._getConstrainedPositionBinary(
|
||||||
elementRect.top - verticalOffset,
|
elementRect.top - verticalOffset,
|
||||||
elementRect.bottom + verticalOffset,
|
elementRect.bottom + verticalOffset,
|
||||||
height,
|
height,
|
||||||
@ -565,12 +567,12 @@ class Popup {
|
|||||||
return [x, y, w, h, below];
|
return [x, y, w, h, below];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getPositionForVerticalText(elementRect, width, height, viewport, offsetScale, optionsGeneral, writingMode) {
|
_getPositionForVerticalText(elementRect, width, height, viewport, offsetScale, optionsGeneral, writingMode) {
|
||||||
const preferRight = Popup._isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode);
|
const preferRight = this._isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode);
|
||||||
const horizontalOffset = optionsGeneral.popupHorizontalOffset2 * offsetScale;
|
const horizontalOffset = optionsGeneral.popupHorizontalOffset2 * offsetScale;
|
||||||
const verticalOffset = optionsGeneral.popupVerticalOffset2 * offsetScale;
|
const verticalOffset = optionsGeneral.popupVerticalOffset2 * offsetScale;
|
||||||
|
|
||||||
const [x, w] = Popup._getConstrainedPositionBinary(
|
const [x, w] = this._getConstrainedPositionBinary(
|
||||||
elementRect.left - horizontalOffset,
|
elementRect.left - horizontalOffset,
|
||||||
elementRect.right + horizontalOffset,
|
elementRect.right + horizontalOffset,
|
||||||
width,
|
width,
|
||||||
@ -578,7 +580,7 @@ class Popup {
|
|||||||
viewport.right,
|
viewport.right,
|
||||||
preferRight
|
preferRight
|
||||||
);
|
);
|
||||||
const [y, h, below] = Popup._getConstrainedPosition(
|
const [y, h, below] = this._getConstrainedPosition(
|
||||||
elementRect.bottom - verticalOffset,
|
elementRect.bottom - verticalOffset,
|
||||||
elementRect.top + verticalOffset,
|
elementRect.top + verticalOffset,
|
||||||
height,
|
height,
|
||||||
@ -589,20 +591,22 @@ class Popup {
|
|||||||
return [x, y, w, h, below];
|
return [x, y, w, h, below];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _isVerticalTextPopupOnRight(positionPreference, writingMode) {
|
_isVerticalTextPopupOnRight(positionPreference, writingMode) {
|
||||||
switch (positionPreference) {
|
switch (positionPreference) {
|
||||||
case 'before':
|
case 'before':
|
||||||
return !Popup._isWritingModeLeftToRight(writingMode);
|
return !this._isWritingModeLeftToRight(writingMode);
|
||||||
case 'after':
|
case 'after':
|
||||||
return Popup._isWritingModeLeftToRight(writingMode);
|
return this._isWritingModeLeftToRight(writingMode);
|
||||||
case 'left':
|
case 'left':
|
||||||
return false;
|
return false;
|
||||||
case 'right':
|
case 'right':
|
||||||
return true;
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _isWritingModeLeftToRight(writingMode) {
|
_isWritingModeLeftToRight(writingMode) {
|
||||||
switch (writingMode) {
|
switch (writingMode) {
|
||||||
case 'vertical-lr':
|
case 'vertical-lr':
|
||||||
case 'sideways-lr':
|
case 'sideways-lr':
|
||||||
@ -612,7 +616,7 @@ class Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getConstrainedPosition(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
|
_getConstrainedPosition(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
|
||||||
size = Math.min(size, maxLimit - minLimit);
|
size = Math.min(size, maxLimit - minLimit);
|
||||||
|
|
||||||
let position;
|
let position;
|
||||||
@ -627,7 +631,7 @@ class Popup {
|
|||||||
return [position, size, after];
|
return [position, size, after];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getConstrainedPositionBinary(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
|
_getConstrainedPositionBinary(positionBefore, positionAfter, size, minLimit, maxLimit, after) {
|
||||||
const overflowBefore = minLimit - (positionBefore - size);
|
const overflowBefore = minLimit - (positionBefore - size);
|
||||||
const overflowAfter = (positionAfter + size) - maxLimit;
|
const overflowAfter = (positionAfter + size) - maxLimit;
|
||||||
|
|
||||||
@ -647,7 +651,10 @@ class Popup {
|
|||||||
return [position, size, after];
|
return [position, size, after];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _addColor(target, color) {
|
_addColor(target, cssColor) {
|
||||||
|
if (typeof cssColor !== 'string') { return; }
|
||||||
|
|
||||||
|
const color = this._getColorInfo(cssColor);
|
||||||
if (color === null) { return; }
|
if (color === null) { return; }
|
||||||
|
|
||||||
const a = color[3];
|
const a = color[3];
|
||||||
@ -659,7 +666,7 @@ class Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getColorInfo(cssColor) {
|
_getColorInfo(cssColor) {
|
||||||
const m = /^\s*rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)\s*$/.exec(cssColor);
|
const m = /^\s*rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)\s*$/.exec(cssColor);
|
||||||
if (m === null) { return null; }
|
if (m === null) { return null; }
|
||||||
|
|
||||||
@ -672,7 +679,7 @@ class Popup {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static _getViewport(useVisualViewport) {
|
_getViewport(useVisualViewport) {
|
||||||
const visualViewport = window.visualViewport;
|
const visualViewport = window.visualViewport;
|
||||||
if (visualViewport !== null && typeof visualViewport === 'object') {
|
if (visualViewport !== null && typeof visualViewport === 'object') {
|
||||||
const left = visualViewport.offsetLeft;
|
const left = visualViewport.offsetLeft;
|
||||||
@ -706,7 +713,7 @@ class Popup {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static async _injectStylesheet(id, type, value, useWebExtensionApi) {
|
async _injectStylesheet(id, type, value, useWebExtensionApi) {
|
||||||
const injectedStylesheets = Popup._injectedStylesheets;
|
const injectedStylesheets = Popup._injectedStylesheets;
|
||||||
|
|
||||||
if (yomichan.isExtensionUrl(window.location.href)) {
|
if (yomichan.isExtensionUrl(window.location.href)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user