494 lines
14 KiB
JavaScript
494 lines
14 KiB
JavaScript
if(Debug.debug)
|
|
console.log("Loading: Resizer.js");
|
|
|
|
class Resizer {
|
|
|
|
constructor(videoData) {
|
|
this.conf = videoData;
|
|
this.video = videoData.video;
|
|
this.settings = videoData.settings;
|
|
this.extensionMode = videoData.extensionMode;
|
|
|
|
this.scaler = new Scaler(this.conf);
|
|
this.stretcher = new Stretcher(this.conf);
|
|
this.zoom = new Zoom(this.conf);
|
|
|
|
// load up default values
|
|
this.correctedVideoDimensions = {};
|
|
this.currentCss = {};
|
|
this.currentStyleString = "";
|
|
this.currentCssValidFor = {};
|
|
|
|
// restore watchdog. While true, applyCss() tries to re-apply new css until this value becomes false again
|
|
// value becomes false when width and height of <video> tag match with what we want to set. Only necessary when
|
|
// calling _res_restore() for some weird reason.
|
|
this.restore_wd = false;
|
|
|
|
// CSS watcher will trigger _very_ often for this many iterations
|
|
this.cssWatcherIncreasedFrequencyCounter = 0;
|
|
|
|
|
|
this.lastAr = this.settings.getDefaultAr(); // this is the aspect ratio we start with
|
|
this.videoFloat = this.settings.getDefaultVideoAlignment(); // this is initial video alignment
|
|
this.destroyed = false;
|
|
|
|
this.resizerId = (Math.random(99)*100).toFixed(0);
|
|
|
|
if (this.settings.active.pan) {
|
|
console.log("can pan:", this.settings.active.miscSettings.mousePan.enabled, "(default:", this.settings.active.miscSettings.mousePan.enabled, ")")
|
|
this.canPan = this.settings.active.miscSettings.mousePan.enabled;
|
|
} else {
|
|
this.canPan = false;
|
|
}
|
|
}
|
|
|
|
start(){
|
|
if(!this.destroyed) {
|
|
this.startCssWatcher();
|
|
}
|
|
}
|
|
|
|
stop(){
|
|
this.stopCssWatcher();
|
|
}
|
|
|
|
destroy(){
|
|
if(Debug.debug || Debug.init){
|
|
console.log(`[Resizer::destroy] <rid:${this.resizerId}> received destroy command.`);
|
|
}
|
|
this.destroyed = true;
|
|
this.stopCssWatcher();
|
|
}
|
|
|
|
|
|
setAr(ar, lastAr){
|
|
if (this.destroyed) {
|
|
return;
|
|
}
|
|
|
|
if(Debug.debug){
|
|
console.log('[Resizer::setAr] <rid:'+this.resizerId+'> trying to set ar. New ar:', ar)
|
|
}
|
|
|
|
if(lastAr) {
|
|
this.lastAr = lastAr;
|
|
} else {
|
|
if(isNaN(ar)){
|
|
this.lastAr = {type: 'legacy', ar: ar}
|
|
} else {
|
|
this.lastAr = {type: 'static', ar: ar};
|
|
}
|
|
}
|
|
|
|
if (this.extensionMode !== ExtensionMode.Full && !PlayerData.isFullScreen() && ar !== 'reset') {
|
|
// don't actually apply or calculate css when using basic mode if not in fullscreen
|
|
// ... unless we're resetting the aspect ratio to original
|
|
return;
|
|
}
|
|
|
|
if (! this.video) {
|
|
// console.log("No video detected.")
|
|
this.videoData.destroy();
|
|
}
|
|
|
|
if (this.extensionMode !== ExtensionMode.Full || PlayerData.isFullScreen()) {
|
|
this.startCssWatcher();
|
|
}
|
|
this.cssWatcherIncreasedFrequencyCounter = 20;
|
|
|
|
// // pause AR on basic stretch, unpause when using other mdoes
|
|
// fir sine reason unpause doesn't unpause. investigate that later
|
|
// if (this.stretcher.mode === StretchMode.BASIC) {
|
|
// this.conf.arDetector.pause();
|
|
// } else {
|
|
// this.conf.arDetector.unpause();
|
|
// }
|
|
|
|
// do stretch thingy
|
|
if (this.stretcher.mode === StretchMode.NO_STRETCH || this.stretcher.mode === StretchMode.CONDITIONAL){
|
|
var stretchFactors = this.scaler.calculateCrop(ar);
|
|
|
|
if(! stretchFactors || stretchFactors.error){
|
|
if(Debug.debug){
|
|
console.log("[Resizer::setAr] <rid:"+this.resizerId+"> failed to set AR due to problem with calculating crop. Error:", (stretchFactors ? stretchFactors.error : stretchFactors));
|
|
}
|
|
if(stretchFactors.error === 'no_video'){
|
|
this.conf.destroy();
|
|
}
|
|
return;
|
|
}
|
|
if(this.stretcher.mode === StretchMode.CONDITIONAL){
|
|
this.stretcher.applyConditionalStretch(stretchFactors, ar);
|
|
}
|
|
} else if (this.stretcher.mode === StretchMode.HYBRID) {
|
|
var stretchFactors = this.stretcher.calculateStretch(ar);
|
|
} else if (this.stretcher.mode === StretchMode.BASIC) {
|
|
var stretchFactors = this.stretcher.calculateBasicStretch();
|
|
}
|
|
|
|
this.zoom.applyZoom(stretchFactors);
|
|
|
|
//TODO: correct these two
|
|
var translate = this.computeOffsets(stretchFactors);
|
|
this.applyCss(stretchFactors, translate);
|
|
|
|
}
|
|
|
|
resetLastAr() {
|
|
this.lastAr = {type: 'original'};
|
|
}
|
|
|
|
setLastAr(override){
|
|
this.lastAr = override;
|
|
}
|
|
|
|
getLastAr(){
|
|
return this.lastAr;
|
|
}
|
|
|
|
setStretchMode(stretchMode){
|
|
this.stretcher.mode = stretchMode;
|
|
this.restore();
|
|
}
|
|
|
|
panHandler(event, forcePan) {
|
|
// console.log("this.conf.canPan:", this.conf.canPan)
|
|
if (this.canPan || forcePan) {
|
|
if(!this.conf.player || !this.conf.player.element) {
|
|
return;
|
|
}
|
|
// dont allow weird floats
|
|
this.videoFloat = 'center';
|
|
|
|
const player = this.conf.player.element;
|
|
|
|
const relativeX = (event.pageX - player.offsetLeft) / player.offsetWidth;
|
|
const relativeY = (event.pageY - player.offsetTop) / player.offsetHeight;
|
|
|
|
if (Debug.debug && Debug.mousemove) {
|
|
console.log("[Resizer::panHandler] mousemove.pageX, pageY:", event.pageX, event.pageY,
|
|
"\nrelativeX/Y:", relativeX, relativeY)
|
|
}
|
|
|
|
this.setPan(relativeX, relativeY);
|
|
}
|
|
}
|
|
|
|
setPan(relativeMousePosX, relativeMousePosY){
|
|
// relativeMousePos[X|Y] - on scale from 0 to 1, how close is the mouse to player edges.
|
|
// use these values: top, left: 0, bottom, right: 1
|
|
if(! this.pan){
|
|
this.pan = {};
|
|
}
|
|
|
|
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;
|
|
}
|
|
// if(Debug.debug){
|
|
// console.log("[Resizer::setPan] relative cursor pos:", relativeMousePosX, ",",relativeMousePosY, " | new pan obj:", this.pan)
|
|
// }
|
|
this.restore();
|
|
}
|
|
|
|
setVideoFloat(videoFloat) {
|
|
this.videoFloat = videoFloat;
|
|
this.restore();
|
|
}
|
|
|
|
startCssWatcher(){
|
|
if(Debug.debug) {
|
|
console.log("[Resizer.js::startCssWatcher] starting css watcher. Is resizer destroyed?", this.destroyed);
|
|
}
|
|
if (this.destroyed) {
|
|
return;
|
|
}
|
|
|
|
// this.haltCssWatcher = false;
|
|
if(!this.cssWatcherTimer){
|
|
this.scheduleCssWatcher(1);
|
|
} else {
|
|
clearTimeout(this.cssWatcherTimer);
|
|
this.scheduleCssWatcher(1);
|
|
}
|
|
}
|
|
|
|
scheduleCssWatcher(timeout, force_reset) {
|
|
if (this.destroyed || (this.extensionMode !== ExtensionMode.Full && !PlayerData.isFullScreen())) {
|
|
return;
|
|
}
|
|
|
|
if(timeout === undefined) {
|
|
console.log("?")
|
|
this.cssCheck(); // no timeout = one-off
|
|
return;
|
|
}
|
|
|
|
if(this.cssWatcherTimeout) {
|
|
clearTimeout(this.cssWatcherTimer);
|
|
}
|
|
|
|
var ths = this;
|
|
this.cssWatcherTimer = setTimeout(function () {
|
|
ths.cssWatcherTimer = null;
|
|
try {
|
|
ths.cssCheck();
|
|
} catch (e) {
|
|
if(Debug.debug) {
|
|
console.log("[Resizer.js::scheduleCssWatcher] Css check failed. Error:", e);
|
|
}
|
|
}
|
|
}, timeout);
|
|
}
|
|
|
|
stopCssWatcher() {
|
|
if(Debug.debug) console.log("[Resizer.js] STOPPING CSS WATCHER!")
|
|
|
|
clearInterval(this.cssWatcherTimeout);
|
|
}
|
|
|
|
restore() {
|
|
if(Debug.debug){
|
|
console.log("[Resizer::restore] <rid:"+this.resizerId+"> attempting to restore aspect ratio. this & settings:", {'this': this, "settings": this.settings} );
|
|
}
|
|
|
|
// this is true until we verify that css has actually been applied
|
|
this.restore_wd = true;
|
|
|
|
if(this.lastAr.type === 'original'){
|
|
this.setAr('reset');
|
|
}
|
|
else {
|
|
this.setAr(this.lastAr.ar, this.lastAr)
|
|
}
|
|
}
|
|
|
|
reset(){
|
|
this.setStretchMode(StretchMode.NO_STRETCH);
|
|
this.zoom.setZoom(1);
|
|
this.resetPan();
|
|
this.setAr('reset');
|
|
}
|
|
|
|
setPanMode(mode) {
|
|
if (mode === 'enable') {
|
|
this.canPan = true;
|
|
} else if (mode === 'disable') {
|
|
this.canPan = false;
|
|
} else if (mode === 'toggle') {
|
|
this.canPan = !this.canPan;
|
|
}
|
|
}
|
|
|
|
resetPan(){
|
|
this.pan = undefined;
|
|
}
|
|
|
|
setZoom(zoomLevel, no_announce) {
|
|
this.zoom.setZoom(zoomLevel, no_announce);
|
|
}
|
|
|
|
zoomStep(step){
|
|
this.zoom.zoomStep(step);
|
|
}
|
|
|
|
resetZoom(){
|
|
this.zoom.setZoom(1);
|
|
this.restore();
|
|
}
|
|
|
|
resetCrop(){
|
|
this.setAr('reset');
|
|
}
|
|
|
|
resetStretch(){
|
|
this.stretcher.mode = StretchMode.NO_STRETCH;
|
|
this.restore();
|
|
}
|
|
|
|
|
|
// mostly internal stuff
|
|
|
|
computeOffsets(stretchFactors){
|
|
|
|
if (Debug.debug) {
|
|
console.log("[Resizer::_res_computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.settings.active.miscSettings.videoFloat);
|
|
}
|
|
|
|
const wdiff = this.conf.player.dimensions.width - this.conf.video.offsetWidth;
|
|
const hdiff = this.conf.player.dimensions.height - this.conf.video.offsetHeight;
|
|
|
|
const wdiffAfterZoom = this.conf.video.offsetWidth * stretchFactors.xFactor - this.conf.player.dimensions.width;
|
|
const hdiffAfterZoom = this.conf.video.offsetHeight * stretchFactors.yFactor - this.conf.player.dimensions.height;
|
|
|
|
var translate = {
|
|
x: wdiff * 0.5,
|
|
y: hdiff * 0.5,
|
|
};
|
|
|
|
if (this.pan) {
|
|
// don't offset when video is smaller than player
|
|
if(wdiffAfterZoom < 0 && hdiffAfterZoom < 0) {
|
|
return translate;
|
|
}
|
|
translate.x += wdiffAfterZoom * this.pan.relativeOffsetX * this.zoom.scale;
|
|
translate.y += hdiffAfterZoom * this.pan.relativeOffsetY * this.zoom.scale;
|
|
} else {
|
|
if (this.videoFloat == "left") {
|
|
translate.x += wdiffAfterZoom * 0.5;
|
|
}
|
|
else if (this.videoFloat == "right") {
|
|
translate.x -= wdiffAfterZoom * 0.5;
|
|
}
|
|
}
|
|
|
|
if(Debug.debug) {
|
|
console.log("[Resizer::_res_computeOffsets] <rid:"+this.resizerId+"> calculated offsets:", translate);
|
|
}
|
|
|
|
return translate;
|
|
}
|
|
|
|
applyCss(stretchFactors, translate){
|
|
|
|
if (! this.video) {
|
|
if(Debug.debug) {
|
|
console.log("[Resizer::_res_applyCss] <rid:"+this.resizerId+"> Video went missing, doing nothing.");
|
|
}
|
|
|
|
this.conf.destroy();
|
|
return;
|
|
}
|
|
|
|
// save stuff for quick tests (before we turn numbers into css values):
|
|
this.currentVideoSettings = {
|
|
validFor: this.conf.player.dimensions,
|
|
// videoWidth: dimensions.width,
|
|
// videoHeight: dimensions.height
|
|
}
|
|
|
|
var styleArrayStr = this.video.getAttribute('style');
|
|
var styleArrayObj = window.getComputedStyle(this.video);
|
|
|
|
|
|
if (styleArrayStr) {
|
|
var styleArray = styleArrayStr.split(";");
|
|
for(var i in styleArray){
|
|
|
|
styleArray[i] = styleArray[i].trim();
|
|
|
|
// some sites do 'top: 50%; left: 50%; transform: <transform>' to center videos.
|
|
// we dont wanna, because we already center videos on our own
|
|
if (styleArray[i].startsWith("transform:") ||
|
|
styleArray[i].startsWith("top:") ||
|
|
styleArray[i].startsWith("left:") ||
|
|
styleArray[i].startsWith("right:") ||
|
|
styleArray[i].startsWith("bottom") ){
|
|
delete styleArray[i];
|
|
}
|
|
}
|
|
}else {
|
|
var styleArray = [];
|
|
}
|
|
|
|
|
|
// add remaining elements
|
|
|
|
if(stretchFactors){
|
|
styleArray.push(`transform: translate(${translate.x}px, ${translate.y}px) scale(${stretchFactors.xFactor}, ${stretchFactors.yFactor})`);
|
|
styleArray.push("top: 0px; left: 0px; bottom: 0px; right: 0px");
|
|
}
|
|
|
|
// build style string back
|
|
var styleString = "";
|
|
for(var i in styleArray)
|
|
if(styleArray[i])
|
|
styleString += styleArray[i] + "; ";
|
|
|
|
this.setStyleString(styleString);
|
|
}
|
|
|
|
setStyleString (styleString, count = 0) {
|
|
this.video.setAttribute("style", styleString);
|
|
|
|
this.currentStyleString = this.video.getAttribute('style');
|
|
this.currentCssValidFor = this.conf.player.dimensions;
|
|
|
|
if(this.restore_wd){
|
|
|
|
if(! this.video){
|
|
if(Debug.debug)
|
|
console.log("[Resizer::_res_setStyleString] <rid:"+this.resizerId+"> Video element went missing, nothing to do here.")
|
|
return;
|
|
}
|
|
|
|
// if(
|
|
// styleString.indexOf("width: " + this.video.style.width) == -1 ||
|
|
// styleString.indexOf("height: " + this.video.style.height) == -1) {
|
|
// // css ni nastavljen?
|
|
// // css not set?
|
|
// if(Debug.debug)
|
|
// console.log("[Resizer::_res_setStyleString] Style string not set ???");
|
|
|
|
// if(count < settings.active.resizer.setStyleString.maxRetries){
|
|
// setTimeout( this.setStyleString, settings.active.resizer.setStyleString.retryTimeout, count + 1);
|
|
// }
|
|
// else if(Debug.debug){
|
|
// console.log("[Resizer::_res_setStyleString] we give up. css string won't be set");
|
|
// }
|
|
// }
|
|
// else{
|
|
this.restore_wd = false;
|
|
// }
|
|
}
|
|
else{
|
|
if(Debug.debug)
|
|
console.log("[Resizer::_res_setStyleString] <rid:"+this.resizerId+"> css applied. Style string:", styleString);
|
|
}
|
|
}
|
|
|
|
cssCheck(){
|
|
// this means we haven't set our CSS yet, or that we changed video.
|
|
// if(! this.currentCss.tranform) {
|
|
// this.scheduleCssWatcher(200);
|
|
// return;
|
|
// }
|
|
|
|
// this means video went missing. videoData will be re-initialized when the next video is found
|
|
if(! this.video){
|
|
if(Debug.debug) {
|
|
console.log("[Resizer::cssCheck] <rid:"+this.resizerId+"> no video detecting, issuing destroy command");
|
|
}
|
|
this.conf.destroy();
|
|
return;
|
|
}
|
|
|
|
if(this.destroyed) {
|
|
if(Debug.debug) {
|
|
console.log("[Resizer::cssCheck] <rid:"+this.resizerId+"> destroyed flag is set, we shouldnt be running");
|
|
}
|
|
this.stopCssWatcher();
|
|
return;
|
|
}
|
|
|
|
var styleString = this.video.getAttribute('style');
|
|
|
|
// first, a quick test:
|
|
// if (this.currentVideoSettings.validFor == this.conf.player.dimensions ){
|
|
if (this.currentStyleString !== styleString){
|
|
this.restore();
|
|
this.scheduleCssWatcher(10);
|
|
return;
|
|
}
|
|
if (this.cssWatcherIncreasedFrequencyCounter > 0) {
|
|
--this.cssWatcherIncreasedFrequencyCounter;
|
|
this.scheduleCssWatcher(20);
|
|
} else {
|
|
this.scheduleCssWatcher(1000);
|
|
}
|
|
}
|
|
}
|