From 9f10c0dcf429e8379022db007ed300c240692186 Mon Sep 17 00:00:00 2001 From: Tamius Han Date: Thu, 2 Jan 2025 04:33:00 +0100 Subject: [PATCH] FOR REALS account for offset applied by sites that use top:50%/transformY:-50% to center elements --- src/ext/lib/video-transform/Resizer.ts | 13 +++- src/ext/util/getElementStyles.ts | 98 ++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 src/ext/util/getElementStyles.ts diff --git a/src/ext/lib/video-transform/Resizer.ts b/src/ext/lib/video-transform/Resizer.ts index c8d4126..7655b5e 100644 --- a/src/ext/lib/video-transform/Resizer.ts +++ b/src/ext/lib/video-transform/Resizer.ts @@ -19,6 +19,7 @@ import Settings from '../Settings'; import { Ar } from '../../../common/interfaces/ArInterface'; import { RunLevel } from '../../enum/run-level.enum'; import * as _ from 'lodash'; +import getElementStyles from '../../util/getElementStyles'; if(Debug.debug) { console.log("Loading: Resizer.js"); @@ -692,7 +693,6 @@ class Resizer { this.logger.log('info', 'debug', "[Resizer::computeOffsets] video will be aligned to ", this.videoAlignment); const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions(); - const computedStyles = getComputedStyle(this.video); // correct any remaining element size discrepancies (applicable only to certain crop strategies!) // NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy @@ -716,10 +716,16 @@ class Resizer { // we only need to compensate if alignment is set to anything other than center center // compensation is equal to half the difference between (zoomed) video size and player size. const translate = { - x: -Math.round(+ (computedStyles.left.replace('px', ''))), - y: -Math.round(+ (computedStyles.top.replace('px', ''))) + x: 0, + y: 0 }; + const problemStats = getElementStyles(this.video, ['top', 'left', 'transform']); + if (problemStats.left?.css && problemStats.top?.css && problemStats.transform?.css?.includes(`translate(-${problemStats.left.css}, -${problemStats.top.css})`)) { + translate.x -= ~~problemStats.left.pxValue; + translate.y -= ~~problemStats.top.pxValue; + } + // NOTE: manual panning is probably broken now. // TODO: FIXME: // (argument could be made that manual panning was also broken before) @@ -889,6 +895,7 @@ class Resizer { if (stretchFactors) { styleArray.push(`transform: translate(${Math.round(translate.x)}px, ${Math.round(translate.y)}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor}) !important;`); } + const styleString = `${this.buildStyleString(styleArray)}${extraStyleString || ''}`; // string returned by buildStyleString() should end with ; anyway // build style string back diff --git a/src/ext/util/getElementStyles.ts b/src/ext/util/getElementStyles.ts new file mode 100644 index 0000000..3df7e4a --- /dev/null +++ b/src/ext/util/getElementStyles.ts @@ -0,0 +1,98 @@ +export type ProcessedElementStyles = { + [x: string]: { + css: string, + pxValue: number + } +}; + +/** + * Note that this function is written _very_ dangerously + * and includes absolutely no error handling + */ +function getPixelValue(value: string, element?: HTMLElement, prop?: string) { + if (value === undefined || value === null) { + return null; + } + + if (value.endsWith('px')) { + // console.log('value ends in px:', value) + return parseFloat(value); + } + if (value.endsWith('%')) { + // console.log('value ends in %:', value) + // This allegedly doesn't work for certain types of properties, allegedly. + const parent = element?.parentElement; + if (parent && prop) { + const parentDimensions = parent.getBoundingClientRect(); + return (parseFloat(value) * 0.01) * (prop.includes('height') || ['top', 'bottom'].includes(prop) ? parentDimensions.height : parentDimensions.width); + } else { + return null; + } + } + + if (value.endsWith("vw")) { + const viewportWidth = window.innerWidth; + return (parseFloat(value) / 100) * viewportWidth; + } + if (value.endsWith("vh")) { + const viewportHeight = window.innerHeight; + return (parseFloat(value) / 100) * viewportHeight; + } + + if (value.endsWith("rem")) { + const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize); + return rootFontSize * parseFloat(value); + } + if (value.endsWith("em")) { + const fontSize = parseFloat(getComputedStyle(element).fontSize); + return fontSize * parseFloat(value); + } +}; + +export default function getElementStyles(element: HTMLElement, props: string[]): ProcessedElementStyles { + const stylesheets = document.styleSheets; + const computedStyles = getComputedStyle(element); + const stylesOut = {}; + + for (const stylesheet of stylesheets) { + // console.log('——————————————— processing stylesheet:', stylesheet); + try { + for (const rule of stylesheet?.cssRules) { + if (rule instanceof CSSStyleRule) { + if (element.matches(rule.selectorText)) { + // console.log('element matches rule:', rule); + + for (const property in rule.style) { + if (!props.includes(property)) { + continue; + } + + const cssValue = rule.style.getPropertyValue(property); + const actualValue = computedStyles.getPropertyValue(property); + + const theory = getPixelValue(cssValue, element, property); + const practice = getPixelValue(actualValue, element, property); + + if (theory === practice) { + stylesOut[property] = { + css: cssValue, + pxValue: theory + } + } + } + } + } + } + + } catch (e) { + // Cross-origin styles amy cause problems + } + } + + return stylesOut; +} + + + + +