2023-01-07 18:57:47 +01:00
import { SiteSettings } from './../settings/SiteSettings' ;
2018-12-31 01:03:07 +01:00
import Debug from '../../conf/Debug' ;
2021-03-30 21:54:44 +02:00
import Scaler , { CropStrategy , VideoDimensions } from './Scaler' ;
2018-12-31 01:03:07 +01:00
import Stretcher from './Stretcher' ;
import Zoom from './Zoom' ;
import PlayerData from '../video-data/PlayerData' ;
2021-02-08 23:04:54 +01:00
import ExtensionMode from '../../../common/enums/ExtensionMode.enum' ;
import StretchType from '../../../common/enums/StretchType.enum' ;
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum' ;
import AspectRatioType from '../../../common/enums/AspectRatioType.enum' ;
import CropModePersistance from '../../../common/enums/CropModePersistence.enum' ;
2020-04-28 03:05:55 +02:00
import { sleep } from '../Util' ;
2021-02-08 22:45:51 +01:00
import Logger from '../Logger' ;
2023-01-07 18:57:47 +01:00
import siteSettings from '../Settings' ;
2021-02-08 22:45:51 +01:00
import VideoData from '../video-data/VideoData' ;
2021-10-26 22:19:41 +02:00
import EventBus from '../EventBus' ;
2023-01-07 18:57:47 +01:00
import { _cp } from '../../../common/js/utils' ;
import Settings from '../Settings' ;
import { Ar } from '../../../common/interfaces/ArInterface' ;
2024-05-07 19:05:10 +02:00
import { RunLevel } from '../../enum/run-level.enum' ;
2024-06-12 20:29:00 +02:00
import * as _ from 'lodash' ;
2025-01-02 04:33:00 +01:00
import getElementStyles from '../../util/getElementStyles' ;
2025-01-14 01:44:25 +01:00
import { Stretch } from '../../../common/interfaces/StretchInterface' ;
2018-12-31 01:03:07 +01:00
2019-02-21 21:21:25 +01:00
if ( Debug . debug ) {
2017-09-24 01:54:46 +02:00
console . log ( "Loading: Resizer.js" ) ;
2019-02-21 21:21:25 +01:00
}
2017-09-24 01:54:46 +02:00
2023-07-10 18:27:06 +02:00
/ * *
* Resizer is the top class and is responsible for figuring out which component needs to crop , which
* component needs to zoom , and which component needs to stretch .
*
* It also kinda does lots of the work that should prolly be moved to Scaler .
*
* /
2018-12-31 01:03:07 +01:00
class Resizer {
2021-02-08 22:45:51 +01:00
//#region flags
canPan : boolean = false ;
destroyed : boolean = false ;
2021-10-31 23:19:32 +01:00
manualZoom : boolean = false ;
2021-02-08 22:45:51 +01:00
//#endregion
//#region helper objects
logger : Logger ;
settings : Settings ;
2023-01-07 18:57:47 +01:00
siteSettings : SiteSettings ;
2021-02-08 22:45:51 +01:00
scaler : Scaler ;
stretcher : Stretcher ;
zoom : Zoom ;
2023-04-12 23:08:19 +02:00
videoData : VideoData ;
2021-10-26 22:19:41 +02:00
eventBus : EventBus ;
2021-02-08 22:45:51 +01:00
//#endregion
//#region HTML elements
2025-01-01 22:18:37 +01:00
video : HTMLVideoElement ;
2021-02-08 22:45:51 +01:00
//#endregion
//#region data
correctedVideoDimensions : any ;
currentCss : any ;
currentStyleString : string ;
currentPlayerStyleString : any ;
currentCssValidFor : any ;
currentVideoSettings : any ;
2022-03-29 01:53:16 +02:00
2023-01-07 18:57:47 +01:00
_lastAr : Ar = { type : AspectRatioType . Initial } ;
set lastAr ( x : Ar ) {
2024-12-30 23:02:55 +01:00
// emit updates for UI when setting lastAr, but only if AR really changed
if ( this . _lastAr ? . type !== x . type || this . _lastAr ? . ratio !== x . ratio ) {
this . eventBus . send ( 'uw-config-broadcast' , { type : 'ar' , config : x } ) ;
}
2022-03-29 01:53:16 +02:00
this . _lastAr = x ;
}
get lastAr() {
return this . _lastAr ;
}
2021-02-08 22:45:51 +01:00
resizerId : any ;
2021-10-26 22:19:41 +02:00
videoAlignment : { x : VideoAlignmentType , y : VideoAlignmentType } ;
2021-02-08 22:45:51 +01:00
userCss : string ;
userCssClassName : any ;
pan : any = null ;
//#endregion
2023-03-30 00:43:30 +02:00
cycleableAspectRatios : Ar [ ] ;
nextCycleOptionIndex = 0 ;
2021-10-26 22:19:41 +02:00
//#region event bus configuration
private eventBusCommands = {
'set-ar' : [ {
2021-10-31 23:19:32 +01:00
function : ( config : any ) = > {
this . manualZoom = false ; // this only gets called from UI or keyboard shortcuts, making this action safe.
2023-03-30 00:43:30 +02:00
if ( config . type !== AspectRatioType . Cycle ) {
this . setAr ( config ) ;
} else {
// if we manually switched to a different aspect ratio, cycle from that ratio forward
const lastArIndex = this . cycleableAspectRatios . findIndex ( x = > x . type === this . lastAr . type && x . ratio === this . lastAr . ratio ) ;
if ( lastArIndex !== - 1 ) {
this . nextCycleOptionIndex = ( lastArIndex + 1 ) % this . cycleableAspectRatios . length ;
}
this . setAr ( this . cycleableAspectRatios [ this . nextCycleOptionIndex ] ) ;
this . nextCycleOptionIndex = ( this . nextCycleOptionIndex + 1 ) % this . cycleableAspectRatios . length ;
}
2021-10-31 23:19:32 +01:00
}
2021-10-26 22:19:41 +02:00
} ] ,
'set-alignment' : [ {
function : ( config : any ) = > {
2021-11-03 00:08:34 +01:00
this . setVideoAlignment ( config . x , config . y ) ;
2021-10-26 22:19:41 +02:00
}
} ] ,
'set-stretch' : [ {
2021-10-31 23:19:32 +01:00
function : ( config : any ) = > {
this . manualZoom = false ; // we also need to unset manual aspect ratio when doing this
2025-01-14 01:44:25 +01:00
this . setStretchMode ( config )
2021-10-31 23:19:32 +01:00
}
2021-10-26 22:19:41 +02:00
} ] ,
'set-zoom' : [ {
2021-11-01 01:13:13 +01:00
function : ( config : any ) = > this . setZoom ( config . zoom , config . axis , config . noAnnounce )
2021-10-26 22:19:41 +02:00
} ] ,
'change-zoom' : [ {
function : ( config : any ) = > this . zoomStep ( config . step )
} ] ,
2022-03-29 01:53:16 +02:00
'get-ar' : [ {
function : ( ) = > this . eventBus . send ( 'uw-config-broadcast' , { type : 'ar' , config : this.lastAr } )
2024-05-07 19:05:10 +02:00
} ] ,
2024-06-10 23:40:49 +02:00
'get-resizer-config' : [ {
function : ( ) = > this . eventBus . send (
'uw-resizer-config-broadcast' ,
{
ar : this.lastAr ,
2025-01-14 01:44:25 +01:00
stretchMode : this.stretcher.stretch ,
2024-06-10 23:40:49 +02:00
videoAlignment : this.videoAlignment
}
)
} ] ,
2024-05-07 19:05:10 +02:00
'restore-ar' : [ {
function : ( ) = > this . restore ( )
2024-06-12 20:29:00 +02:00
} ] ,
'delayed-restore-ar' : [ {
function : ( ) = > {
_ . debounce (
this . restore ,
500 ,
{
leading : true ,
trailing : true
}
)
}
2022-03-29 01:53:16 +02:00
} ]
2021-10-26 22:19:41 +02:00
}
//#endregion
2019-09-03 22:42:38 +02:00
constructor ( videoData ) {
2021-02-08 22:45:51 +01:00
this . resizerId = ( Math . random ( ) * 100 ) . toFixed ( 0 ) ;
2023-04-12 23:08:19 +02:00
this . videoData = videoData ;
2019-09-03 23:01:23 +02:00
this . logger = videoData . logger ;
2018-05-08 23:35:16 +02:00
this . video = videoData . video ;
2018-08-05 23:48:56 +02:00
this . settings = videoData . settings ;
2023-01-07 18:57:47 +01:00
this . siteSettings = videoData . siteSettings ;
2021-10-26 23:13:11 +02:00
this . eventBus = videoData . eventBus ;
this . initEventBus ( ) ;
2019-07-18 21:25:58 +02:00
2023-04-12 23:08:19 +02:00
this . scaler = new Scaler ( this . videoData ) ;
this . stretcher = new Stretcher ( this . videoData ) ;
this . zoom = new Zoom ( this . videoData ) ;
2018-05-12 02:51:58 +02:00
2024-06-10 23:40:33 +02:00
const defaultCrop = this . siteSettings . getDefaultOption ( 'crop' ) as { type : AspectRatioType , ratio? : number } ;
if ( defaultCrop . type !== AspectRatioType . Reset ) {
this . lastAr = defaultCrop ;
}
2023-01-07 18:57:47 +01:00
this . videoAlignment = this . siteSettings . getDefaultOption ( 'alignment' ) as { x : VideoAlignmentType , y : VideoAlignmentType } // this is initial video alignment
2018-08-30 00:56:15 +02:00
2021-10-27 20:10:55 +02:00
this . destroyed = false ;
2018-09-13 23:47:20 +02:00
2023-01-07 18:57:47 +01:00
// if (this.siteSettings.active.pan) {
// this.canPan = this.siteSettings.active.miscSettings.mousePan.enabled;
// } else {
// this.canPan = false;
// }
2019-08-23 02:25:48 +02:00
2023-03-30 00:43:30 +02:00
this . cycleableAspectRatios =
( this . settings ? . active ? . commands ? . crop ? ? [ ] )
. filter ( x = > [ AspectRatioType . FitHeight , AspectRatioType . FitWidth , AspectRatioType . Fixed , AspectRatioType . Reset ] . includes ( x ? . arguments ? . type ) )
. map ( x = > x . arguments ) as Ar [ ] ;
this . nextCycleOptionIndex = 0 ;
2021-10-25 23:11:34 +02:00
this . userCssClassName = videoData . userCssClassName ;
2021-10-26 22:19:41 +02:00
}
initEventBus() {
for ( const action in this . eventBusCommands ) {
for ( const command of this . eventBusCommands [ action ] ) {
this . eventBus . subscribe ( action , command ) ;
}
}
2017-09-24 01:54:46 +02:00
}
2021-10-25 23:11:34 +02:00
2018-05-23 23:57:51 +02:00
2019-08-23 02:25:48 +02:00
prepareCss ( css ) {
return ` . ${ this . userCssClassName } { ${ css } } ` ;
2018-05-23 23:57:51 +02:00
}
2018-05-16 23:26:47 +02:00
destroy ( ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , [ 'debug' , 'init' ] , ` [Resizer::destroy] <rid: ${ this . resizerId } > received destroy command. ` ) ;
2018-05-16 23:26:47 +02:00
this . destroyed = true ;
}
2018-05-13 15:22:28 +02:00
2024-06-10 00:08:49 +02:00
getFileAr() {
return this . videoData . video . videoWidth / this . videoData . video . videoHeight ;
}
2019-03-10 23:27:50 +01:00
calculateRatioForLegacyOptions ( ar ) {
// also present as modeToAr in Scaler.js
2021-02-08 23:04:54 +01:00
if ( ar . type !== AspectRatioType . FitWidth && ar . type !== AspectRatioType . FitHeight && ar . ratio ) {
2019-04-25 22:02:10 +02:00
return ar ;
2019-03-10 23:27:50 +01:00
}
2021-02-08 23:04:54 +01:00
// handles "legacy" options, such as 'fit to widht', 'fit to height' and AspectRatioType.Reset. No zoom tho
2021-02-18 22:38:32 +01:00
let ratioOut ;
2019-03-10 23:27:50 +01:00
2023-04-12 23:08:19 +02:00
if ( ! this . videoData . video ) {
this . logger . log ( 'info' , 'debug' , "[Scaler.js::modeToAr] No video??" , this . videoData . video , "killing videoData" ) ;
this . videoData . destroy ( ) ;
2019-03-10 23:27:50 +01:00
return null ;
}
2021-10-25 23:11:34 +02:00
2023-04-12 23:08:19 +02:00
if ( ! this . videoData . player . dimensions ) {
2019-03-10 23:27:50 +01:00
ratioOut = screen . width / screen . height ;
2019-04-25 22:02:10 +02:00
} else {
2023-04-12 23:08:19 +02:00
this . logger . log ( 'info' , 'debug' , ` [Resizer::calculateRatioForLegacyOptions] <rid: ${ this . resizerId } > Player dimensions: ` , this . videoData . player . dimensions . width , 'x' , this . videoData . player . dimensions . height , 'aspect ratio:' , this . videoData . player . dimensions . width / this . videoData . player . dimensions . height )
ratioOut = this . videoData . player . dimensions . width / this . videoData . player . dimensions . height ;
2019-03-10 23:27:50 +01:00
}
2021-10-25 23:11:34 +02:00
2019-03-10 23:27:50 +01:00
// IMPORTANT NOTE: lastAr needs to be set after _res_setAr() is called, as _res_setAr() assumes we're
2021-10-25 23:11:34 +02:00
// setting a static aspect ratio (even if the function is called from here or ArDetect).
2024-06-10 00:08:49 +02:00
let fileAr = this . getFileAr ( ) ;
2021-10-25 23:11:34 +02:00
2021-02-08 23:04:54 +01:00
if ( ar . type === AspectRatioType . FitWidth ) {
2019-04-25 22:02:10 +02:00
ar . ratio = ratioOut > fileAr ? ratioOut : fileAr ;
2019-03-10 23:27:50 +01:00
}
2021-02-08 23:04:54 +01:00
else if ( ar . type === AspectRatioType . FitHeight ) {
2019-04-25 22:02:10 +02:00
ar . ratio = ratioOut < fileAr ? ratioOut : fileAr ;
2019-03-10 23:27:50 +01:00
}
2021-02-08 23:04:54 +01:00
else if ( ar . type === AspectRatioType . Reset ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'debug' , "[Scaler.js::modeToAr] Using original aspect ratio -" , fileAr ) ;
2019-04-25 22:02:10 +02:00
ar . ratio = fileAr ;
} else {
return null ;
2019-03-10 23:27:50 +01:00
}
2019-04-25 22:02:10 +02:00
return ar ;
2019-03-10 23:27:50 +01:00
}
2019-11-04 23:53:28 +01:00
updateAr ( ar ) {
2020-03-01 16:55:52 +01:00
if ( ! ar ) {
return ;
}
// Some options require a bit more testing re: whether they make sense
// if they don't, we refuse to update aspect ratio until they do
2021-11-25 00:13:21 +01:00
if ( ar . type === AspectRatioType . AutomaticUpdate || ar . type === AspectRatioType . Fixed ) {
2020-03-01 16:55:52 +01:00
if ( ! ar . ratio || isNaN ( ar . ratio ) ) {
return ;
}
}
// Only update aspect ratio if there's a difference between the old and the new state
2019-11-04 23:53:28 +01:00
if ( ! this . lastAr || ar . type !== this . lastAr . type || ar . ratio !== this . lastAr . ratio ) {
this . setAr ( ar ) ;
}
}
2023-01-07 18:57:47 +01:00
async setAr ( ar : Ar , lastAr? : Ar ) {
2018-08-30 00:56:15 +02:00
if ( this . destroyed ) {
return ;
}
2021-10-25 23:11:34 +02:00
2025-01-05 00:37:20 +01:00
// If no aspect ratio is applied AND if no stretch mode is active,
// we disable our CSS in order to prevent breaking websites by default,
// without any human interaction
if (
[ AspectRatioType . Reset , AspectRatioType . Initial ] . includes ( ar . type ) &&
2025-01-14 01:44:25 +01:00
[ StretchType . NoStretch , StretchType . Default ] . includes ( this . stretcher . stretch . type )
2025-01-05 00:37:20 +01:00
) {
2024-05-07 19:05:10 +02:00
this . eventBus . send ( 'set-run-level' , RunLevel . UIOnly ) ;
} else {
this . eventBus . send ( 'set-run-level' , RunLevel . CustomCSSActive ) ;
}
2021-11-25 00:31:38 +01:00
// handle autodetection stuff
if ( ar . type === AspectRatioType . Automatic ) {
2025-01-29 23:58:16 +01:00
this . videoData . aard ? . startCheck ( ) ;
2021-11-25 00:31:38 +01:00
return ;
} else if ( ar . type !== AspectRatioType . AutomaticUpdate ) {
2024-11-07 02:26:19 +01:00
this . videoData . aard ? . stop ( ) ;
2021-11-25 00:31:38 +01:00
}
if ( ar . type !== AspectRatioType . AutomaticUpdate ) {
2021-10-31 23:19:32 +01:00
this . manualZoom = false ;
}
2020-12-29 20:20:00 +01:00
if ( ! this . video . videoWidth || ! this . video . videoHeight ) {
2021-01-12 23:28:17 +01:00
this . logger . log ( 'warning' , 'debug' , '[Resizer::setAr] <rid:' + this . resizerId + '> Video has no width or no height. This is not allowed. Aspect ratio will not be set, and videoData will be uninitialized.' ) ;
2023-04-12 23:08:19 +02:00
this . videoData . videoUnloaded ( ) ;
2020-12-29 20:20:00 +01:00
}
2021-10-25 23:11:34 +02:00
2022-08-08 23:16:55 +02:00
this . logger . log ( 'info' , 'debug' , '%c[Resizer::setAr] <rid:' + this . resizerId + '> trying to set ar. New ar:' , 'background-color: #4c3a2f, color: #ffa349' , ar ) ;
2017-09-24 01:54:46 +02:00
2019-03-10 23:27:50 +01:00
if ( ar == null ) {
2019-02-21 21:21:25 +01:00
return ;
}
2024-12-30 03:05:47 +01:00
let stretchFactors : VideoDimensions | any ;
2019-10-24 00:45:11 +02:00
2019-11-02 02:45:24 +01:00
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
2021-02-08 23:04:54 +01:00
// AspectRatioType.Fixed when zooming, so let's keep that in mind
2020-10-21 19:48:04 +02:00
if (
2021-02-08 23:04:54 +01:00
( ar . type !== AspectRatioType . Fixed && ar . type !== AspectRatioType . Manual ) // anything not these two _always_ changes AR
2020-10-21 19:48:04 +02:00
|| ar . type !== this . lastAr . type // this also means aspect ratio has changed
|| ar . ratio !== this . lastAr . ratio // this also means aspect ratio has changed
2023-04-12 23:08:19 +02:00
) {
2019-11-02 02:45:24 +01:00
this . zoom . reset ( ) ;
this . resetPan ( ) ;
}
2019-10-24 00:45:11 +02:00
// most everything that could go wrong went wrong by this stage, and returns can happen afterwards
2019-10-26 02:38:47 +02:00
// this means here's the optimal place to set or forget aspect ratio. Saving of current crop ratio
// is handled in pageInfo.updateCurrentCrop(), which also makes sure to persist aspect ratio if ar
// is set to persist between videos / through current session / until manual reset.
2023-09-10 22:06:16 +02:00
// if (ar.type === AspectRatioType.Reset ||
// ar.type === AspectRatioType.Initial
// ) {
// // reset/undo default
// this.videoData.pageInfo.updateCurrentCrop(undefined);
// } else {
2023-04-12 23:08:19 +02:00
this . videoData . pageInfo . updateCurrentCrop ( ar ) ;
2023-09-10 22:06:16 +02:00
// }
2019-10-24 00:45:11 +02:00
2019-04-25 22:02:10 +02:00
if ( lastAr ) {
2019-05-07 23:40:13 +02:00
this . lastAr = this . calculateRatioForLegacyOptions ( lastAr ) ;
ar = this . calculateRatioForLegacyOptions ( ar ) ;
2018-05-12 02:51:58 +02:00
} else {
2019-04-25 22:02:10 +02:00
// NOTE: "fitw" "fith" and "reset" should ignore ar.ratio bit, but
// I'm not sure whether they do. Check that.
ar = this . calculateRatioForLegacyOptions ( ar ) ;
if ( ! ar ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'resizer' , ` [Resizer::setAr] < ${ this . resizerId } > Something wrong with ar or the player. Doing nothing. ` ) ;
2019-04-25 22:02:10 +02:00
return ;
2018-05-13 15:22:28 +02:00
}
2021-08-26 00:37:56 +02:00
this . lastAr = { type : ar . type , ratio : ar.ratio } ;
2018-05-12 02:51:58 +02:00
}
2018-05-01 23:09:58 +02:00
2018-05-08 23:35:16 +02:00
if ( ! this . video ) {
2023-04-12 23:08:19 +02:00
this . videoData . destroy ( ) ;
2018-11-02 21:19:34 +01:00
}
2020-10-21 19:48:56 +02:00
// pause AR on:
// * ar.type NOT automatic
// * ar.type is auto, but stretch is set to basic basic stretch
2021-10-25 23:11:34 +02:00
//
2020-10-21 19:48:56 +02:00
// unpause when using other modes
2025-01-14 01:44:25 +01:00
if ( ( ar . type !== AspectRatioType . Automatic && ar . type !== AspectRatioType . AutomaticUpdate ) || this . stretcher . stretch . type === StretchType . Basic ) {
2024-11-07 02:26:19 +01:00
this . videoData ? . aard ? . stop ( ) ;
2020-10-21 19:48:56 +02:00
} else {
2024-06-10 23:27:47 +02:00
if ( ar . type !== AspectRatioType . AutomaticUpdate ) {
if ( this . lastAr . type === AspectRatioType . Automatic || this . lastAr . type === AspectRatioType . AutomaticUpdate ) {
2024-11-07 02:26:19 +01:00
this . videoData ? . aard ? . stop ( ) ;
2024-06-10 23:27:47 +02:00
}
2019-02-15 20:40:56 +01:00
}
}
2018-05-16 20:26:55 +02:00
2018-07-11 23:13:40 +02:00
// do stretch thingy
2025-01-14 01:44:25 +01:00
if ( [ StretchType . NoStretch , StretchType . Conditional , StretchType . FixedSource ] . includes ( this . stretcher . stretch . type ) ) {
2021-02-08 22:45:51 +01:00
stretchFactors = this . scaler . calculateCrop ( ar ) ;
2018-05-24 23:29:30 +02:00
if ( ! stretchFactors || stretchFactors . error ) {
2020-10-21 19:48:56 +02:00
this . logger . log ( 'error' , 'debug' , ` [Resizer::setAr] <rid: ${ this . resizerId } > failed to set AR due to problem with calculating crop. Error: ` , stretchFactors ? . error ) ;
2020-04-28 03:05:55 +02:00
if ( stretchFactors ? . error === 'no_video' ) {
2023-04-12 23:08:19 +02:00
this . videoData . destroy ( ) ;
2020-04-28 03:05:55 +02:00
return ;
2018-05-24 23:29:30 +02:00
}
2020-04-28 03:05:55 +02:00
2020-06-04 21:51:22 +02:00
// we could have issued calculate crop too early. Let's tell VideoData that there's something wrong
// and exit this function. When <video> will receive onloadeddata or ontimeupdate (receiving either
// of the two means video is loaded or playing, and that means video has proper dimensions), it will
// try to reset or re-apply aspect ratio when the video is finally ready.
2020-04-28 03:05:55 +02:00
if ( stretchFactors ? . error === 'illegal_video_dimensions' ) {
2023-04-12 23:08:19 +02:00
this . videoData . videoDimensionsLoaded = false ;
2020-06-04 21:51:22 +02:00
return ;
2019-05-07 23:40:13 +02:00
}
2018-05-16 23:26:47 +02:00
}
2019-02-15 20:40:56 +01:00
2025-01-14 01:44:25 +01:00
if ( this . stretcher . stretch . type === StretchType . Conditional ) {
2019-12-06 00:17:09 +01:00
this . stretcher . applyConditionalStretch ( stretchFactors , ar . ratio ) ;
2025-01-14 01:44:25 +01:00
} else if ( this . stretcher . stretch . type === StretchType . FixedSource ) {
2019-12-06 00:17:09 +01:00
this . stretcher . applyStretchFixedSource ( stretchFactors ) ;
}
this . logger . log ( 'info' , 'debug' , "[Resizer::setAr] Processed stretch factors for " ,
2025-01-14 01:44:25 +01:00
this . stretcher . stretch . type === StretchType . NoStretch ? 'stretch-free crop.' :
this . stretcher . stretch . type === StretchType . Conditional ? 'crop with conditional StretchType.' : 'crop with fixed stretch' ,
2019-12-06 00:17:09 +01:00
'Stretch factors are:' , stretchFactors
) ;
2019-02-15 20:40:56 +01:00
2025-01-14 01:44:25 +01:00
} else if ( this . stretcher . stretch . type === StretchType . Hybrid ) {
2021-02-08 22:45:51 +01:00
stretchFactors = this . stretcher . calculateStretch ( ar . ratio ) ;
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'debug' , '[Resizer::setAr] Processed stretch factors for hybrid stretch/crop. Stretch factors are:' , stretchFactors ) ;
2025-01-14 01:44:25 +01:00
} else if ( this . stretcher . stretch . type === StretchType . Fixed ) {
2021-02-08 22:45:51 +01:00
stretchFactors = this . stretcher . calculateStretchFixed ( ar . ratio )
2025-01-14 01:44:25 +01:00
} else if ( this . stretcher . stretch . type === StretchType . Basic ) {
2021-02-08 22:45:51 +01:00
stretchFactors = this . stretcher . calculateBasicStretch ( ) ;
2021-02-08 23:04:54 +01:00
this . logger . log ( 'info' , 'debug' , '[Resizer::setAr] Processed stretch factors for basic StretchType. Stretch factors are:' , stretchFactors ) ;
2019-02-15 20:40:56 +01:00
} else {
2021-11-02 22:19:50 +01:00
stretchFactors = this . scaler . calculateCrop ( ar ) ;
this . logger . log (
'error' , 'debug' ,
'[Resizer::setAr] Okay wtf happened? If you see this, something has gone wrong. Pretending stretchMode is set tu NoStretch. Stretch factors are:' , stretchFactors ,
"\n------[ i n f o d u m p ]------\nstretcher:" , this . stretcher ,
'\nargs: ar (corrected for legacy):' , ar , 'last ar (optional argument):' , lastAr
) ;
2018-05-16 20:26:55 +02:00
}
2024-12-30 03:05:47 +01:00
this . applyScaling ( stretchFactors as VideoDimensions ) ;
2021-10-31 23:19:32 +01:00
}
2024-12-30 03:05:47 +01:00
applyScaling ( stretchFactors : VideoDimensions , options ? : { noAnnounce? : boolean , ar? : Ar } ) {
2025-01-29 23:58:16 +01:00
// this.stretcher.chromeBugMitigation(stretchFactors);
2021-01-30 12:16:37 +01:00
2021-11-01 01:13:13 +01:00
// let the UI know
if ( ! options ? . noAnnounce ) {
2023-04-12 23:08:19 +02:00
this . videoData . eventBus . send ( 'announce-zoom' , { x : stretchFactors.xFactor , y : stretchFactors.yFactor } ) ;
2021-11-01 01:13:13 +01:00
}
2021-02-08 22:45:51 +01:00
2024-12-30 03:05:47 +01:00
let translate = this . computeOffsets ( stretchFactors , options ? . ar ) ;
2018-05-25 21:37:09 +02:00
this . applyCss ( stretchFactors , translate ) ;
2018-07-10 20:36:12 +02:00
}
2018-06-15 00:33:10 +02:00
2019-11-02 02:45:24 +01:00
toFixedAr() {
2020-10-21 19:48:56 +02:00
// converting to fixed AR means we also turn off autoAR
this . setAr ( {
2021-02-08 22:45:51 +01:00
ratio : this.lastAr.ratio ,
2021-02-08 23:04:54 +01:00
type : AspectRatioType . Fixed
2020-10-21 19:48:56 +02:00
} ) ;
2019-11-02 02:45:24 +01:00
}
2018-07-10 20:36:12 +02:00
resetLastAr() {
2021-02-08 23:04:54 +01:00
this . lastAr = { type : AspectRatioType . Initial } ;
2018-05-12 02:51:58 +02:00
}
2018-05-02 17:52:25 +02:00
2018-05-15 21:40:53 +02:00
2025-01-14 01:44:25 +01:00
setStretchMode ( stretch : { type : StretchType , ratio? : number } ) {
this . stretcher . setStretchMode ( stretch ) ;
2018-05-27 21:41:08 +02:00
this . restore ( ) ;
2018-05-13 15:22:28 +02:00
}
2018-05-02 17:52:25 +02:00
2018-12-02 23:51:34 +01:00
panHandler ( event , forcePan ) {
if ( this . canPan || forcePan ) {
2023-04-12 23:08:19 +02:00
if ( ! this . videoData . player || ! this . videoData . player . element ) {
2018-09-13 23:47:20 +02:00
return ;
}
2024-12-30 03:05:47 +01:00
// don't allow weird floats
2021-10-26 22:19:41 +02:00
this . videoAlignment . x = VideoAlignmentType . Center ;
2018-12-02 23:51:34 +01:00
2019-11-02 02:45:24 +01:00
// because non-fixed aspect ratios reset panning:
2021-02-08 23:04:54 +01:00
if ( this . lastAr . type !== AspectRatioType . Fixed ) {
2020-10-21 19:48:56 +02:00
this . toFixedAr ( ) ;
}
2019-11-02 02:45:24 +01:00
2023-04-12 23:08:19 +02:00
const player = this . videoData . player . element ;
2018-09-13 23:47:20 +02:00
const relativeX = ( event . pageX - player . offsetLeft ) / player . offsetWidth ;
const relativeY = ( event . pageY - player . offsetTop ) / player . offsetHeight ;
2021-10-25 23:11:34 +02:00
2019-07-18 21:25:58 +02:00
this . logger . log ( 'info' , 'mousemove' , "[Resizer::panHandler] mousemove.pageX, pageY:" , event . pageX , event . pageY , "\nrelativeX/Y:" , relativeX , relativeY )
2018-12-02 23:51:34 +01:00
2018-09-13 23:47:20 +02:00
this . setPan ( relativeX , relativeY ) ;
}
}
2019-11-02 02:45:24 +01:00
resetPan() {
2021-02-08 22:45:51 +01:00
this . pan = { x : 0 , y : 0 } ;
2021-11-03 00:08:34 +01:00
// this.videoAlignment = {x: this.settings.getDefaultVideoAlignment(window.location.hostname), y: VideoAlignmentType.Center};
2019-11-02 02:45:24 +01:00
}
2018-05-13 15:22:28 +02:00
setPan ( relativeMousePosX , relativeMousePosY ) {
2021-10-25 23:11:34 +02:00
// relativeMousePos[X|Y] - on scale from 0 to 1, how close is the mouse to player edges.
2018-05-13 15:22:28 +02:00
// use these values: top, left: 0, bottom, right: 1
if ( ! this . pan ) {
2021-02-08 22:45:51 +01:00
this . pan = { x : 0 , y : 0 } ;
2018-05-13 15:22:28 +02:00
}
2018-02-04 17:39:26 +01:00
2018-12-03 00:31:28 +01:00
if ( this . settings . active . miscSettings . mousePanReverseMouse ) {
this . pan . relativeOffsetX = ( relativeMousePosX * 1.1 ) - 0.55 ;
this . pan . relativeOffsetY = ( relativeMousePosY * 1.1 ) - 0.55 ;
} else {
this . pan . relativeOffsetX = - ( relativeMousePosX * 1.1 ) + 0.55 ;
this . pan . relativeOffsetY = - ( relativeMousePosY * 1.1 ) + 0.55 ;
}
2018-09-13 23:47:20 +02:00
this . restore ( ) ;
2018-05-13 15:22:28 +02:00
}
2018-05-02 17:52:25 +02:00
2021-10-26 22:19:41 +02:00
setVideoAlignment ( videoAlignmentX : VideoAlignmentType , videoAlignmentY? : VideoAlignmentType ) {
2024-06-10 00:08:49 +02:00
// if aspect ratio is unset or initial, CSS fixes are inactive by design.
// because of that, we need to set a manual aspect ratio first.
2024-12-30 03:05:47 +01:00
if ( ! this . lastAr ? . ratio ) {
2024-06-10 00:08:49 +02:00
this . setAr ( {
2024-12-30 03:05:47 +01:00
type : AspectRatioType . AutomaticUpdate ,
2024-06-10 00:08:49 +02:00
ratio : this.getFileAr ( )
} ) ;
}
2024-12-30 03:05:47 +01:00
2024-06-10 00:08:49 +02:00
if ( [ AspectRatioType . Reset , AspectRatioType . Initial ] . includes ( this . lastAr . type ) ) {
if ( this . lastAr . ratio ) {
this . lastAr . type = AspectRatioType . Fixed ;
} else {
this . setAr ( {
type : AspectRatioType . Fixed ,
ratio : this.getFileAr ( )
} ) ;
}
}
2021-10-26 22:19:41 +02:00
this . videoAlignment = {
x : videoAlignmentX ? ? VideoAlignmentType . Default ,
y : videoAlignmentY ? ? VideoAlignmentType . Default
} ;
2018-09-23 02:39:27 +02:00
this . restore ( ) ;
}
2025-01-29 23:58:16 +01:00
/ * *
* Restores aspect ratio to last known aspect ratio
* @returns
* /
2018-06-15 00:33:10 +02:00
restore() {
2021-10-31 23:19:32 +01:00
if ( ! this . manualZoom ) {
this . logger . log ( 'info' , 'debug' , "[Resizer::restore] <rid:" + this . resizerId + "> attempting to restore aspect ratio" , { 'lastAr' : this . lastAr } ) ;
2021-10-25 23:11:34 +02:00
2021-10-31 23:19:32 +01:00
// this is true until we verify that css has actually been applied
if ( this . lastAr . type === AspectRatioType . Initial ) {
this . setAr ( { type : AspectRatioType . Reset } ) ;
}
else {
if ( this . lastAr ? . ratio === null ) {
// if this is the case, we do nothing as we have the correct aspect ratio
// throw "Last ar is null!"
return ;
}
this . setAr ( this . lastAr , this . lastAr )
2019-02-21 21:21:25 +01:00
}
2021-10-31 23:19:32 +01:00
} else {
this . applyScaling ( { xFactor : this.zoom.scale , yFactor : this.zoom.scaleY } ) ;
2018-05-13 15:22:28 +02:00
}
2021-10-31 23:19:32 +01:00
2018-05-13 15:22:28 +02:00
}
2018-05-02 17:52:25 +02:00
2018-05-13 15:22:28 +02:00
reset ( ) {
2025-01-14 01:44:25 +01:00
this . setStretchMode ( this . siteSettings . getDefaultOption ( 'stretch' ) as Stretch ) ;
2018-05-13 15:22:28 +02:00
this . zoom . setZoom ( 1 ) ;
this . resetPan ( ) ;
2021-02-08 23:04:54 +01:00
this . setAr ( { type : AspectRatioType . Reset } ) ;
2023-09-10 22:06:16 +02:00
this . unsetStyleString ( ) ;
2017-09-24 01:54:46 +02:00
}
2018-05-13 15:22:28 +02:00
2018-09-13 23:47:20 +02:00
setPanMode ( mode ) {
if ( mode === 'enable' ) {
this . canPan = true ;
} else if ( mode === 'disable' ) {
this . canPan = false ;
} else if ( mode === 'toggle' ) {
this . canPan = ! this . canPan ;
}
}
2021-11-01 01:13:13 +01:00
setZoom ( zoomLevel : number , axis ? : 'x' | 'y' , noAnnounce ? ) {
2021-10-31 23:19:32 +01:00
this . manualZoom = true ;
2021-11-01 01:13:13 +01:00
this . zoom . setZoom ( zoomLevel , axis , noAnnounce ) ;
2018-09-18 23:37:33 +02:00
}
2021-10-25 23:11:34 +02:00
2018-05-24 20:50:37 +02:00
zoomStep ( step ) {
2021-10-31 23:19:32 +01:00
this . manualZoom = true ;
2018-05-24 20:50:37 +02:00
this . zoom . zoomStep ( step ) ;
}
2018-05-13 15:22:28 +02:00
resetZoom ( ) {
this . zoom . setZoom ( 1 ) ;
2021-10-31 23:19:32 +01:00
this . manualZoom = false ;
2018-05-13 15:22:28 +02:00
this . restore ( ) ;
2017-09-24 01:54:46 +02:00
}
2018-05-13 15:22:28 +02:00
resetCrop ( ) {
2021-02-08 23:04:54 +01:00
this . setAr ( { type : AspectRatioType . Reset } ) ;
2017-09-24 01:54:46 +02:00
}
2018-05-13 15:22:28 +02:00
resetStretch ( ) {
2025-01-14 01:44:25 +01:00
this . stretcher . setStretchMode ( { type : StretchType . NoStretch } ) ;
2018-05-13 15:22:28 +02:00
this . restore ( ) ;
2017-09-24 01:54:46 +02:00
}
2018-01-02 03:36:29 +01:00
2018-05-13 15:22:28 +02:00
// mostly internal stuff
2020-09-23 00:23:24 +02:00
/ * *
* Returns the size of the video file _as displayed_ on the screen .
* Consider the following example :
2021-10-25 23:11:34 +02:00
*
2020-09-23 00:23:24 +02:00
* * player dimensions are 2560 x1080
* * < video > is child of player
* * < video > has the following css : { width : 100 % , height : 100 % }
* * video file dimensions are 1280 x720
2021-10-25 23:11:34 +02:00
*
2020-09-23 00:23:24 +02:00
* CSS will ensure that the dimensions of < video > tag are equal to the dimension of the
2021-10-25 23:11:34 +02:00
* player element — that is , 2560 x1080px . This is no bueno , because the browser will upscale
2020-09-23 00:23:24 +02:00
* the video file to take up as much space as it can ( without stretching it ) . This means
* we ' ll get a 1920 x1080 video ( as displayed ) and a letterbox .
2021-10-25 23:11:34 +02:00
*
2020-09-23 00:23:24 +02:00
* We can ' t get that number out of anywhere : video.videoWidth will return 1280 ( video file
* dimensions ) and . offsetWidth ( and the likes ) will return the < video > tag dimension . Neither
2021-10-25 23:11:34 +02:00
* will return the actual size of video as displayed , which we need in order to calculate the
2020-09-23 00:23:24 +02:00
* extra space to the left and right of the video .
2021-10-25 23:11:34 +02:00
*
* We make the assumption of the
2020-09-23 00:23:24 +02:00
* /
computeVideoDisplayedDimensions() {
2023-04-12 23:08:19 +02:00
const offsetWidth = this . videoData . video . offsetWidth ;
const offsetHeight = this . videoData . video . offsetHeight ;
2020-09-23 00:23:24 +02:00
2023-04-12 23:08:19 +02:00
const scaleX = offsetWidth / this . videoData . video . videoWidth ;
const scaleY = offsetHeight / this . videoData . video . videoHeight ;
2020-09-23 00:23:24 +02:00
2021-10-25 23:11:34 +02:00
// if differences between the scale factors are minimal, we presume offsetWidth and
2020-09-23 00:23:24 +02:00
// offsetHeight are the accurate enough for our needs
if ( Math . abs ( scaleX - scaleY ) < 0.02 ) {
return {
realVideoWidth : offsetWidth ,
realVideoHeight : offsetHeight ,
marginX : 0 ,
marginY : 0 ,
}
}
// if we're still here, we need to calculate real video dimensions
2023-04-12 23:08:19 +02:00
const diffX = Math . abs ( scaleY * this . videoData . video . videoWidth - offsetWidth ) ;
const diffY = Math . abs ( scaleX * this . videoData . video . videoHeight - offsetHeight ) ;
2020-09-23 00:23:24 +02:00
// in this case, we want to base our real dimensions off scaleX
// otherwise, we want to base it off scaleY
if ( diffX < diffY ) {
2023-04-12 23:08:19 +02:00
const realHeight = this . videoData . video . videoHeight * scaleX ;
2020-09-23 00:23:24 +02:00
return {
realVideoWidth : offsetWidth ,
realVideoHeight : realHeight ,
marginX : 0 ,
marginY : ( offsetHeight - realHeight ) * 0.5
}
} else {
2023-04-12 23:08:19 +02:00
const realWidth = this . videoData . video . videoWidth * scaleY ;
2020-09-23 00:23:24 +02:00
return {
realVideoWidth : realWidth ,
realVideoHeight : offsetHeight ,
marginX : ( offsetWidth - realWidth ) * 0.5 ,
marginY : 0
}
}
}
2021-03-30 21:54:44 +02:00
/ * *
* Sometimes , sites ( e . g . new reddit ) will guarantee that video fits width of its container
* and let the browser figure out the height through the magic of height :auto. This is bad ,
* because our addon generally relies of videos always being 100 % of the height of the
* container .
2021-10-25 23:11:34 +02:00
*
* This sometimes leads to a situation where realVideoHeight and realVideoWidth — at least
2021-03-30 21:54:44 +02:00
* one of which should be roughly equal to the player width or hight with the other one being
2021-10-25 23:11:34 +02:00
* either smaller or equal — are both smaller than player width or height ; and sometimes
2021-03-30 21:54:44 +02:00
* rather substantially . Fortunately for us , realVideo [ Width | Height ] and player dimensions
* never lie , which allows us to calculate the extra scale factor we need .
2021-10-25 23:11:34 +02:00
*
2021-03-30 21:54:44 +02:00
* Returned factor for this function should do fit :contain , not fit :cover.
* @param realVideoWidth real video width
* @param realVideoHeight real video height
* @param playerWidth player width
* @param playerHeight player height
2021-10-25 23:11:34 +02:00
* @param mode whether to
2021-03-30 21:54:44 +02:00
* /
2021-03-31 00:10:18 +02:00
computeAutoHeightCompensationFactor ( realVideoWidth : number , realVideoHeight : number , playerWidth : number , playerHeight : number , mode : 'height' | 'width' ) : number {
2021-03-30 21:54:44 +02:00
const widthFactor = playerWidth / realVideoWidth ;
const heightFactor = playerHeight / realVideoHeight ;
2021-03-31 00:10:18 +02:00
return mode === 'height' ? heightFactor : widthFactor ;
2021-03-30 21:54:44 +02:00
}
2021-04-04 23:19:43 +02:00
private _computeOffsetsRecursionGuard : boolean = false ;
2024-12-30 03:05:47 +01:00
computeOffsets ( stretchFactors : VideoDimensions , ar? : Ar ) {
2023-01-07 18:57:47 +01:00
this . logger . log ( 'info' , 'debug' , "[Resizer::computeOffsets] <rid:" + this . resizerId + "> video will be aligned to " , this . videoAlignment ) ;
2018-09-13 23:47:20 +02:00
2020-09-23 00:23:24 +02:00
const { realVideoWidth , realVideoHeight , marginX , marginY } = this . computeVideoDisplayedDimensions ( ) ;
2024-12-30 03:05:47 +01:00
// correct any remaining element size discrepancies (applicable only to certain crop strategies!)
2021-03-30 21:54:44 +02:00
// NOTE: it's possible that we might also need to apply a similar measure for CropPillarbox strategy
2021-03-31 00:10:18 +02:00
// (but we'll wait for bug reports before doing so).
// We also don't compensate for height:auto if height is provided via element style
2021-03-30 21:54:44 +02:00
let autoHeightCompensationFactor ;
2021-03-31 00:10:18 +02:00
if (
2021-10-25 23:11:34 +02:00
stretchFactors . cropStrategy === CropStrategy . CropLetterbox
2021-03-31 00:10:18 +02:00
&& ( ! stretchFactors . styleHeightCompensationFactor || stretchFactors . styleHeightCompensationFactor === 1 )
) {
2024-12-30 03:05:47 +01:00
autoHeightCompensationFactor = this . computeAutoHeightCompensationFactor (
realVideoWidth , realVideoHeight ,
this . videoData . player . dimensions . width , this . videoData . player . dimensions . height ,
'height'
) ;
2021-03-30 21:54:44 +02:00
stretchFactors . xFactor *= autoHeightCompensationFactor ;
stretchFactors . yFactor *= autoHeightCompensationFactor ;
}
2020-09-23 00:23:24 +02:00
2024-06-06 00:51:34 +02:00
// NOTE: transform: scale() is self-centering by default.
// 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.
2019-11-04 23:53:28 +01:00
const translate = {
2025-01-02 04:33:00 +01:00
x : 0 ,
y : 0
2018-11-16 23:02:56 +01:00
} ;
2018-04-22 14:35:40 +02:00
2025-01-02 06:32:43 +01:00
const problemStats = getElementStyles ( this . video , [ 'top' , 'left' , 'transform' ] , [ 'transform' ] ) ;
2025-01-02 04:33:00 +01:00
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 ;
}
2024-06-06 00:51:34 +02:00
// NOTE: manual panning is probably broken now.
// TODO: FIXME:
// (argument could be made that manual panning was also broken before)
const alignXOffset = ( realVideoWidth * stretchFactors . xFactor - this . videoData . player . dimensions . width ) * 0.5 ;
const alignYOffset = ( realVideoHeight * stretchFactors . yFactor - this . videoData . player . dimensions . height ) * 0.5 ;
2019-11-04 23:53:28 +01:00
2024-06-12 20:29:00 +02:00
if ( this . pan ? . relativeOffsetX || this . pan ? . relativeOffsetY ) {
2018-09-13 23:47:20 +02:00
// don't offset when video is smaller than player
2024-06-06 00:51:34 +02:00
if ( alignXOffset >= 0 || alignYOffset >= 0 ) {
translate . x += alignXOffset * this . pan . relativeOffsetX * this . zoom . scale ;
translate . y += alignYOffset * this . pan . relativeOffsetY * this . zoom . scale ;
2018-09-13 23:47:20 +02:00
}
2018-05-13 15:22:28 +02:00
} else {
2021-11-03 00:08:34 +01:00
// correct horizontal alignment according to the settings
2024-12-30 03:05:47 +01:00
if ( ! stretchFactors . preventAlignment ? . x ) {
if ( this . videoAlignment . x == VideoAlignmentType . Left ) {
translate . x += stretchFactors ? . relativeCropLimits ? . left ? ( this . videoData . player . dimensions . width * stretchFactors . relativeCropLimits . left ) : alignXOffset ;
} else if ( this . videoAlignment . x == VideoAlignmentType . Right ) {
translate . x -= stretchFactors ? . relativeCropLimits ? . left ? ( this . videoData . player . dimensions . width * stretchFactors . relativeCropLimits . left ) : alignXOffset
}
2018-05-13 15:22:28 +02:00
}
2021-11-03 00:08:34 +01:00
// correct vertical alignment according to the settings
2024-12-30 03:05:47 +01:00
if ( ! stretchFactors . preventAlignment ? . y ) {
if ( this . videoAlignment . y == VideoAlignmentType . Top ) {
translate . y += stretchFactors ? . relativeCropLimits ? . top ? ( this . videoData . player . dimensions . height * stretchFactors ? . relativeCropLimits ? . top ) : alignYOffset ;
} else if ( this . videoAlignment . y == VideoAlignmentType . Bottom ) {
translate . y -= stretchFactors ? . relativeCropLimits ? . top ? ( this . videoData . player . dimensions . height * stretchFactors ? . relativeCropLimits ? . top ) : alignYOffset ;
}
2021-11-03 00:08:34 +01:00
}
2018-05-13 15:22:28 +02:00
}
2021-10-25 23:11:34 +02:00
2021-03-29 23:40:34 +02:00
this . logger . log (
2021-03-30 01:11:39 +02:00
'info' , [ 'debug' , 'resizer' ] , "[Resizer::_res_computeOffsets] <rid:" + this . resizerId + "> calculated offsets:" ,
'\n\n---- elements ----' ,
2023-04-12 23:08:19 +02:00
'\nplayer element: ' , this . videoData . player . element ,
'\nvideo element: ' , this . videoData . video ,
2021-03-30 01:11:39 +02:00
'\n\n---- data in ----' ,
2023-04-12 23:08:19 +02:00
'\nplayer dimensions: ' , { w : this.videoData.player.dimensions.width , h : this.videoData.player.dimensions.height } ,
'\nvideo dimensions: ' , { w : this.videoData.video.offsetWidth , h : this.videoData.video.offsetHeight } ,
2021-03-29 23:40:34 +02:00
'\nreal video dimensions:' , { w : realVideoWidth , h : realVideoHeight } ,
2024-06-06 00:51:34 +02:00
'\nalign. base offset: ' , { alignXOffset , alignYOffset } ,
2021-03-31 00:10:18 +02:00
'\nauto compensation: ' , 'x' , autoHeightCompensationFactor ,
2021-03-29 23:40:34 +02:00
'\nstretch factors: ' , stretchFactors ,
'\npan & zoom: ' , this . pan , this . zoom . scale ,
2024-06-06 00:51:34 +02:00
// '\nwdiff, hdiff: ', wdiff, 'x', hdiff,
// '\nwdiff, hdiffAfterZoom:', wdiffAfterZoom, 'x', hdiffAfterZoom,
2021-03-29 23:40:34 +02:00
'\n\n---- data out ----\n' ,
'translate:' , translate
) ;
2020-06-01 23:54:42 +02:00
// by the way, let's do a quick sanity check whether video player is doing any fuckies wuckies
2021-10-25 23:11:34 +02:00
// fucky wucky examples:
2020-06-01 23:54:42 +02:00
//
// * video width is bigger than player width AND video height is bigger than player height
// * video width is smaller than player width AND video height is smaller than player height
//
2021-10-25 23:11:34 +02:00
// In both examples, at most one of the two conditions can be true at the same time. If both
2020-06-01 23:54:42 +02:00
// conditions are true at the same time, we need to go 'chiny reckon' and recheck our player
// element. Chances are our video is not getting aligned correctly
2021-10-25 23:11:34 +02:00
if (
2023-04-12 23:08:19 +02:00
( this . videoData . video . offsetWidth > this . videoData . player . dimensions . width && this . videoData . video . offsetHeight > this . videoData . player . dimensions . height ) ||
( this . videoData . video . offsetWidth < this . videoData . player . dimensions . width && this . videoData . video . offsetHeight < this . videoData . player . dimensions . height )
2020-06-01 23:54:42 +02:00
) {
this . logger . log ( 'warn' , [ 'debugger' , 'resizer' ] , ` [Resizer::_res_computeOffsets] <rid: ${ this . resizerId } > We are getting some incredibly funny results here. \ n \ n ` ,
` Video seems to be both wider and taller (or shorter and narrower) than player element at the same time. This is super duper not supposed to happen. \ n \ n ` ,
` Player element needs to be checked. `
)
2021-04-04 23:19:43 +02:00
// sometimes this appears to randomly recurse.
// There seems to be no way to reproduce it.
if ( ! this . _computeOffsetsRecursionGuard ) {
this . _computeOffsetsRecursionGuard = true ;
2023-04-12 23:08:19 +02:00
this . videoData . player . trackDimensionChanges ( ) ;
2021-04-04 23:19:43 +02:00
this . _computeOffsetsRecursionGuard = false ;
2020-10-21 21:26:00 +02:00
}
2020-06-01 23:54:42 +02:00
}
2020-10-21 21:26:00 +02:00
2021-10-25 23:11:34 +02:00
return translate ;
2018-05-13 15:22:28 +02:00
}
2021-10-25 23:11:34 +02:00
2021-03-30 21:55:17 +02:00
//#region css handling
2019-06-12 23:55:15 +02:00
buildStyleArray ( existingStyleString , extraStyleString ) {
if ( existingStyleString ) {
const styleArray = existingStyleString . split ( ";" ) ;
2018-05-16 23:26:47 +02:00
2019-06-12 23:55:15 +02:00
if ( extraStyleString ) {
const extraCss = extraStyleString . split ( ';' ) ;
2019-06-11 01:34:02 +02:00
let dup = false ;
for ( const ecss of extraCss ) {
2019-06-12 23:55:15 +02:00
for ( let i in styleArray ) {
if ( ecss . split ( ':' ) [ 0 ] . trim ( ) === styleArray [ i ] . split ( ':' ) [ 0 ] . trim ( ) ) {
2019-06-11 01:34:02 +02:00
dup = true ;
2019-06-12 23:55:15 +02:00
styleArray [ i ] = ecss ;
2019-06-11 01:34:02 +02:00
}
if ( dup ) {
dup = false ;
continue ;
}
2019-06-12 23:55:15 +02:00
styleArray . push ( ecss ) ;
2019-06-11 01:34:02 +02:00
}
}
}
2021-10-25 23:11:34 +02:00
2021-02-18 22:38:32 +01:00
for ( let i in styleArray ) {
2021-10-25 23:11:34 +02:00
styleArray [ i ] = styleArray [ i ] . trim ( ) ;
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
2018-11-02 23:10:42 +01:00
// we dont wanna, because we already center videos on our own
2020-10-21 19:49:26 +02:00
if ( styleArray [ i ] . startsWith ( "transform:" )
2021-10-25 23:11:34 +02:00
|| styleArray [ i ] . startsWith ( "top:" )
|| styleArray [ i ] . startsWith ( "left:" )
|| styleArray [ i ] . startsWith ( "right:" )
2020-10-21 19:49:26 +02:00
|| styleArray [ i ] . startsWith ( "bottom:" )
|| styleArray [ i ] . startsWith ( "margin" )
) {
2018-05-25 21:37:09 +02:00
delete styleArray [ i ] ;
2018-05-13 15:22:28 +02:00
}
2018-01-02 03:36:29 +01:00
}
2019-06-12 23:55:15 +02:00
return styleArray ;
}
return [ ] ;
}
buildStyleString ( styleArray ) {
let styleString = '' ;
2021-02-18 22:38:32 +01:00
for ( let i in styleArray ) {
2019-06-12 23:55:15 +02:00
if ( styleArray [ i ] ) {
2019-08-23 02:25:48 +02:00
styleString += styleArray [ i ] + " !important; " ;
2019-06-12 23:55:15 +02:00
}
}
return styleString ;
}
applyCss ( stretchFactors , translate ) {
2021-10-25 23:11:34 +02:00
// apply extra CSS here. In case of duplicated properties, extraCss overrides
2019-06-12 23:55:15 +02:00
// default styleString
if ( ! this . video ) {
2019-07-18 21:25:58 +02:00
this . logger . log ( 'warn' , 'debug' , "[Resizer::applyCss] <rid:" + this . resizerId + "> Video went missing, doing nothing." ) ;
2019-06-12 23:55:15 +02:00
2023-04-12 23:08:19 +02:00
this . videoData . destroy ( ) ;
2019-06-12 23:55:15 +02:00
return ;
}
2019-11-04 23:52:37 +01:00
this . logger . log ( 'info' , [ 'debug' , 'resizer' ] , "[Resizer::applyCss] <rid:" + this . resizerId + "> will apply css." , { stretchFactors , translate } ) ;
2021-10-25 23:11:34 +02:00
2019-06-12 23:55:15 +02:00
// save stuff for quick tests (before we turn numbers into css values):
this . currentVideoSettings = {
2023-04-12 23:08:19 +02:00
validFor : this.videoData.player.dimensions ,
2019-06-12 23:55:15 +02:00
// videoWidth: dimensions.width,
// videoHeight: dimensions.height
}
let extraStyleString ;
try {
2023-01-07 18:57:47 +01:00
extraStyleString = this . siteSettings . data . currentDOMConfig . customCss ;
2019-06-12 23:55:15 +02:00
} catch ( e ) {
// do nothing. It's ok if no special settings are defined for this site, we'll just do defaults
}
2019-08-23 02:25:48 +02:00
const styleArray = this . buildStyleArray ( '' , extraStyleString )
2019-06-12 23:55:15 +02:00
2018-05-13 15:22:28 +02:00
// add remaining elements
2019-06-11 01:34:02 +02:00
if ( stretchFactors ) {
2024-05-07 21:02:22 +02:00
styleArray . push ( ` transform: translate( ${ Math . round ( translate . x ) } px, ${ Math . round ( translate . y ) } px) scale( ${ stretchFactors . xFactor } , ${ stretchFactors . yFactor } ) !important; ` ) ;
2018-01-02 03:36:29 +01:00
}
2025-01-02 04:33:00 +01:00
2019-08-23 02:25:48 +02:00
const styleString = ` ${ this . buildStyleString ( styleArray ) } ${ extraStyleString || '' } ` ; // string returned by buildStyleString() should end with ; anyway
2018-04-22 14:35:40 +02:00
2019-06-12 23:55:15 +02:00
// build style string back
2019-06-14 02:15:24 +02:00
this . setStyleString ( styleString ) ;
2018-04-22 14:35:40 +02:00
}
2018-05-13 15:22:28 +02:00
2019-06-14 02:15:24 +02:00
setStyleString ( styleString ) {
2023-04-12 23:08:19 +02:00
this . currentCssValidFor = this . videoData . player . dimensions ;
2019-08-24 00:28:08 +02:00
const newCssString = this . prepareCss ( styleString ) ;
2019-08-23 02:25:48 +02:00
2019-08-24 00:28:08 +02:00
// inject new CSS or replace existing one
if ( ! this . userCss ) {
2020-04-28 03:05:55 +02:00
this . logger . log ( 'info' , [ 'debug' , 'resizer' ] , "[Resizer::setStyleString] <rid:" + this . resizerId + "> Setting new css: " , newCssString ) ;
2023-09-10 22:06:16 +02:00
this . eventBus . send ( 'inject-css' , { cssString : newCssString } ) ;
2019-08-24 00:28:08 +02:00
this . userCss = newCssString ;
2019-08-24 17:04:53 +02:00
} else if ( newCssString !== this . userCss ) {
2020-04-28 03:05:55 +02:00
this . logger . log ( 'info' , [ 'debug' , 'resizer' ] , "[Resizer::setStyleString] <rid:" + this . resizerId + "> Replacing css.\nOld string:" , this . userCss , "\nNew string:" , newCssString ) ;
2019-08-24 17:04:53 +02:00
// we only replace css if it
2023-09-10 22:06:16 +02:00
this . eventBus . send ( 'replace-css' , { oldCssString : this.userCss , newCssString } ) ;
2019-08-24 00:28:08 +02:00
this . userCss = newCssString ;
2020-04-28 03:05:55 +02:00
} else {
this . logger . log ( 'info' , [ 'debug' , 'resizer' ] , "[Resizer::setStyleString] <rid:" + this . resizerId + "> Existing css is still valid, doing nothing." ) ;
2018-02-05 22:46:38 +01:00
}
}
2023-09-10 22:06:16 +02:00
/ * *
* If no adjustments to crop or stretch are being made , we remove all CSS
* that we have previously injected .
* @param options :
* - options.force : remove our CSS regardless of current crop and stretch options
* /
unsetStyleString ( options ? : { force : boolean } ) {
// check whether it's safe to remove CSS.
if ( ! options ? . force ) {
if (
[ AspectRatioType . Reset , AspectRatioType . Initial ] . includes ( this . lastAr . type )
) {
return ;
}
}
}
2021-03-30 21:55:17 +02:00
//#endregion
2018-02-05 22:46:38 +01:00
}
2018-12-31 01:03:07 +01:00
export default Resizer ;