jassub 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/jassub.es.js CHANGED
@@ -53,11 +53,16 @@ const _JASSUB = class extends EventTarget {
53
53
  this._onDemandRender = "requestVideoFrameCallback" in HTMLVideoElement.prototype && ((_c = options.onDemandRender) != null ? _c : true);
54
54
  this.timeOffset = options.timeOffset || 0;
55
55
  this._video = options.video;
56
- this._canvasParent = null;
57
- if (this._video) {
56
+ this._canvas = options.canvas;
57
+ if (this._video && !this._canvas) {
58
58
  this._canvasParent = document.createElement("div");
59
59
  this._canvasParent.className = "JASSUB";
60
60
  this._canvasParent.style.position = "relative";
61
+ this._canvas = document.createElement("canvas");
62
+ this._canvas.style.display = "block";
63
+ this._canvas.style.position = "absolute";
64
+ this._canvas.style.pointerEvents = "none";
65
+ this._canvasParent.appendChild(this._canvas);
61
66
  if (this._video.nextSibling) {
62
67
  this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling);
63
68
  } else {
@@ -66,11 +71,6 @@ const _JASSUB = class extends EventTarget {
66
71
  } else if (!this._canvas) {
67
72
  this.destroy("Don't know where to render: you should give video or canvas in options.");
68
73
  }
69
- this._canvas = options.canvas || document.createElement("canvas");
70
- this._canvas.style.display = "block";
71
- this._canvas.style.position = "absolute";
72
- this._canvas.style.pointerEvents = "none";
73
- this._canvasParent.appendChild(this._canvas);
74
74
  this._bufferCanvas = document.createElement("canvas");
75
75
  this._bufferCtx = this._bufferCanvas.getContext("2d", { desynchronized: true, willReadFrequently: true });
76
76
  this._canvasctrl = offscreenRender ? this._canvas.transferControlToOffscreen() : this._canvas;
@@ -109,7 +109,8 @@ const _JASSUB = class extends EventTarget {
109
109
  this._boundResize = this.resize.bind(this);
110
110
  this._boundTimeUpdate = this._timeupdate.bind(this);
111
111
  this._boundSetRate = this.setRate.bind(this);
112
- this.setVideo(options.video);
112
+ if (this._video)
113
+ this.setVideo(options.video);
113
114
  if (this._onDemandRender) {
114
115
  this.busy = false;
115
116
  this._lastDemandTime = null;
@@ -126,7 +127,7 @@ const _JASSUB = class extends EventTarget {
126
127
  new ImageData(new Uint8ClampedArray([0, 0, 0, 0]), 1, 1);
127
128
  } catch (e) {
128
129
  console.log("detected that ImageData is not constructable despite browser saying so");
129
- window.ImageData = function(data, width, height) {
130
+ self.ImageData = function(data, width, height) {
130
131
  const imageData = ctx1.createImageData(width, height);
131
132
  if (data)
132
133
  imageData.data.set(data);
@@ -156,17 +157,20 @@ const _JASSUB = class extends EventTarget {
156
157
  _JASSUB._hasAlphaBug = prePut[1] !== postPut[1];
157
158
  if (_JASSUB._hasAlphaBug)
158
159
  console.log("Detected a browser having issue with transparent pixels, applying workaround");
160
+ canvas1.remove();
159
161
  canvas2.remove();
160
162
  }
161
163
  resize(width = 0, height = 0, top = 0, left = 0) {
162
164
  let videoSize = null;
163
165
  if ((!width || !height) && this._video) {
164
166
  videoSize = this._getVideoPosition();
165
- const newsize = this._computeCanvasSize((videoSize.width || 0) * (window.devicePixelRatio || 1), (videoSize.height || 0) * (window.devicePixelRatio || 1));
167
+ const newsize = this._computeCanvasSize((videoSize.width || 0) * (self.devicePixelRatio || 1), (videoSize.height || 0) * (self.devicePixelRatio || 1));
166
168
  width = newsize.width;
167
169
  height = newsize.height;
168
- top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
169
- left = videoSize.x;
170
+ if (this._canvasParent) {
171
+ top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top);
172
+ left = videoSize.x;
173
+ }
170
174
  }
171
175
  if (videoSize != null) {
172
176
  this._canvas.style.top = top + "px";
@@ -322,12 +326,12 @@ const _JASSUB = class extends EventTarget {
322
326
  addFont(font) {
323
327
  this.sendMessage("addFont", { font });
324
328
  }
325
- _sendLocalFont(font) {
329
+ _sendLocalFont(name) {
326
330
  try {
327
331
  queryLocalFonts().then((fontData) => {
328
- const filtered = fontData && fontData.filter((obj) => obj.fullName.toLowerCase() === font);
329
- if (filtered && filtered.length) {
330
- filtered[0].blob().then((blob) => {
332
+ const font = fontData == null ? void 0 : fontData.find((obj) => obj.fullName.toLowerCase() === name);
333
+ if (font) {
334
+ font.blob().then((blob) => {
331
335
  blob.arrayBuffer().then((buffer) => {
332
336
  this.addFont(new Uint8Array(buffer));
333
337
  });
@@ -480,7 +484,7 @@ const _JASSUB = class extends EventTarget {
480
484
  destroy(err) {
481
485
  if (err)
482
486
  this._error(err);
483
- if (this._video)
487
+ if (this._video && this._canvasParent)
484
488
  this._video.parentNode.removeChild(this._canvasParent);
485
489
  this._destroyed = true;
486
490
  this._removeListeners();
@@ -1 +1 @@
1
- (function(h,n){typeof exports=="object"&&typeof module!="undefined"?module.exports=n():typeof define=="function"&&define.amd?define(n):(h=typeof globalThis!="undefined"?globalThis:h||self,h.JASSUB=n())})(this,function(){"use strict";var _=Object.defineProperty;var v=(h,n,d)=>n in h?_(h,n,{enumerable:!0,configurable:!0,writable:!0,value:d}):h[n]=d;var f=(h,n,d)=>(v(h,typeof n!="symbol"?n+"":n,d),d);!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(d){const e=this.getVideoPlaybackQuality(),t=this.mozPresentedFrames||this.mozPaintedFrames||e.totalVideoFrames-e.droppedVideoFrames,s=(r,o)=>{const l=this.getVideoPlaybackQuality(),m=this.mozPresentedFrames||this.mozPaintedFrames||l.totalVideoFrames-l.droppedVideoFrames;if(m>t){const c=this.mozFrameDelay||l.totalFrameDelay-e.totalFrameDelay||0,u=o-r;d(o,{presentationTime:o+c*1e3,expectedDisplayTime:o+u,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+u/1e3,presentedFrames:m,processingDuration:c}),delete this._rvfcpolyfillmap[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(c=>s(o,c))},a=Date.now(),i=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(r=>s(i,r)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(d){cancelAnimationFrame(this._rvfcpolyfillmap[d]),delete this._rvfcpolyfillmap[d]});const n=class extends EventTarget{constructor(e={}){var i,r,o,l,m;super(),globalThis.Worker||this.destroy("Worker not supported"),n._test();const t=e.blendMode||"js",s=typeof createImageBitmap!="undefined"&&((i=e.asyncRender)!=null?i:!0),a=typeof OffscreenCanvas!="undefined"&&((r=e.offscreenRender)!=null?r:!0);this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&((o=e.onDemandRender)!=null?o:!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._canvasParent=null,this._video?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._video.nextSibling?this._video.parentNode.insertBefore(this._canvasParent,this._video.nextSibling):this._video.parentNode.appendChild(this._canvasParent)):this._canvas||this.destroy("Don't know where to render: you should give video or canvas in options."),this._canvas=e.canvas||document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d",{desynchronized:!0,willReadFrequently:!0}),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&this._canvasctrl.getContext("2d",{desynchronized:!0}),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._worker=new Worker(n._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=c=>this._onmessage(c),this._worker.onerror=c=>this._error(c),this._worker.postMessage({target:"init",asyncRender:s,onDemandRender:this._onDemandRender,width:this._canvas.width,height:this._canvas.height,preMain:!0,blendMode:t,subUrl:e.subUrl,subContent:e.subContent||null,fonts:e.fonts||[],availableFonts:e.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:e.fallbackFont||"liberation sans",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:n._hasAlphaBug,useLocalFonts:"queryLocalFonts"in self&&((l=e.useLocalFonts)!=null?l:!0)}),a===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null,(m=this._video)==null||m.requestVideoFrameCallback(this._handleRVFC.bind(this)))}static _test(){if(n._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});if(typeof ImageData.prototype.constructor=="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("detected that ImageData is not constructable despite browser saying so"),window.ImageData=function(l,m,c){const u=t.createImageData(m,c);return l&&u.data.set(l),u}}try{if(typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"){const o=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));o instanceof WebAssembly.Module&&(n._supportsWebAssembly=new WebAssembly.Instance(o)instanceof WebAssembly.Instance)}}catch{n._supportsWebAssembly=!1}const s=document.createElement("canvas"),a=s.getContext("2d",{willReadFrequently:!0});e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const i=a.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(e,0,0);const r=a.getImageData(0,0,1,1).data;n._hasAlphaBug=i[1]!==r[1],n._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),s.remove()}resize(e=0,t=0,s=0,a=0){let i=null;if((!e||!t)&&this._video){i=this._getVideoPosition();const r=this._computeCanvasSize((i.width||0)*(window.devicePixelRatio||1),(i.height||0)*(window.devicePixelRatio||1));e=r.width,t=r.height,s=i.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=i.x}i!=null&&(this._canvas.style.top=s+"px",this._canvas.style.left=a+"px",this._canvas.style.width=i.width+"px",this._canvas.style.height=i.height+"px"),this._canvasctrl.width===e&&this._canvasctrl.height===t||(this._resizeTimeoutBuffer?(clearTimeout(this._resizeTimeoutBuffer),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0,this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t})},100)):(this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t}),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0},100)))}_getVideoPosition(){const e=this._video.videoWidth/this._video.videoHeight,{offsetWidth:t,offsetHeight:s}=this._video,a=t/s;let i=t,r=s;a>e?i=Math.floor(s*e):r=Math.floor(t/e);const o=(t-i)/2,l=(s-r)/2;return{width:i,height:r,x:o,y:l}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let i=t;a*i*s<=a*this.prescaleHeightLimit?i*=s:a*i<a*this.prescaleHeightLimit&&(i=this.prescaleHeightLimit),this.maxRenderHeight>0&&i>this.maxRenderHeight&&(i=this.maxRenderHeight),e*=i/t,t=i}return{width:e,height:t}}_timeupdate({type:e}){const s={seeking:!0,waiting:!0,playing:!1}[e];s!=null&&(this._playstate=s),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1)),e.videoWidth>0&&this.resize(),e.addEventListener("resize",this._boundResize),typeof ResizeObserver!="undefined"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize())),this._ro.observe(e))):this._error("Video element invalid!")}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(e){this.sendMessage("setTrackByUrl",{url:e})}setTrack(e){this.sendMessage("setTrack",{content:e})}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s})}createEvent(e){this.sendMessage("createEvent",{event:e})}setEvent(e,t){this.sendMessage("setEvent",{event:e,index:t})}removeEvent(e){this.sendMessage("removeEvent",{index:e})}getEvents(e){this._fetchFromWorker({target:"getEvents"},(t,{events:s})=>{e(t,s)})}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{event:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t&&t.filter(a=>a.fullName.toLowerCase()===e);s&&s.length&&s[0].blob().then(a=>{a.arrayBuffer().then(i=>{this.addFont(new Uint8Array(i))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(t=navigator==null?void 0:navigator.permissions)!=null&&t.query?navigator.permissions.query({name:"local-fonts"}).then(s=>{s.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t}){if(this._destroyed)return null;this.busy?this._lastDemandTime=t:(this.busy=!0,this._demandRender(t)),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender(e){this._lastDemandTime=null,this.sendMessage("demand",{time:e+this.timeOffset})}_render({images:e,async:t,times:s}){const a=Date.now();this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const i of e)i.image&&(t?(this._ctx.drawImage(i.image,i.x,i.y),i.image.close()):(this._bufferCanvas.width=i.w,this._bufferCanvas.height=i.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(i.image)),i.w,i.h),0,0),this._ctx.drawImage(this._bufferCanvas,i.x,i.y)));if(this.debug){s.drawTime=Date.now()-a;let i=0;for(const r in s)i+=s[r];console.log("Bitmaps: "+e.length+" Total: "+Math.round(i)+"ms",s)}}_fixAlpha(e){if(n._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e,t={},s){s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,a=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),i=({data:o})=>{o.target===s&&(t(null,o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a))},r=o=>{t(o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a)};this._worker.addEventListener("message",i),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){throw e instanceof ErrorEvent||this.dispatchEvent(new ErrorEvent("error",{message:e instanceof Error?e.cause:e})),e instanceof Error?e:new Error(e instanceof ErrorEvent?e.message:"error",{cause:e})}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize))}destroy(e){e&&this._error(e),this._video&&this._video.parentNode.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker.terminate()}};let h=n;return f(h,"_supportsWebAssembly",null),f(h,"_hasAlphaBug",null),h});
1
+ (function(h,n){typeof exports=="object"&&typeof module!="undefined"?module.exports=n():typeof define=="function"&&define.amd?define(n):(h=typeof globalThis!="undefined"?globalThis:h||self,h.JASSUB=n())})(this,function(){"use strict";var _=Object.defineProperty;var v=(h,n,d)=>n in h?_(h,n,{enumerable:!0,configurable:!0,writable:!0,value:d}):h[n]=d;var f=(h,n,d)=>(v(h,typeof n!="symbol"?n+"":n,d),d);!("requestVideoFrameCallback"in HTMLVideoElement.prototype)&&"getVideoPlaybackQuality"in HTMLVideoElement.prototype&&(HTMLVideoElement.prototype._rvfcpolyfillmap={},HTMLVideoElement.prototype.requestVideoFrameCallback=function(d){const e=this.getVideoPlaybackQuality(),t=this.mozPresentedFrames||this.mozPaintedFrames||e.totalVideoFrames-e.droppedVideoFrames,s=(r,o)=>{const l=this.getVideoPlaybackQuality(),m=this.mozPresentedFrames||this.mozPaintedFrames||l.totalVideoFrames-l.droppedVideoFrames;if(m>t){const c=this.mozFrameDelay||l.totalFrameDelay-e.totalFrameDelay||0,u=o-r;d(o,{presentationTime:o+c*1e3,expectedDisplayTime:o+u,width:this.videoWidth,height:this.videoHeight,mediaTime:Math.max(0,this.currentTime||0)+u/1e3,presentedFrames:m,processingDuration:c}),delete this._rvfcpolyfillmap[a]}else this._rvfcpolyfillmap[a]=requestAnimationFrame(c=>s(o,c))},a=Date.now(),i=performance.now();return this._rvfcpolyfillmap[a]=requestAnimationFrame(r=>s(i,r)),a},HTMLVideoElement.prototype.cancelVideoFrameCallback=function(d){cancelAnimationFrame(this._rvfcpolyfillmap[d]),delete this._rvfcpolyfillmap[d]});const n=class extends EventTarget{constructor(e={}){var i,r,o,l,m;super(),globalThis.Worker||this.destroy("Worker not supported"),n._test();const t=e.blendMode||"js",s=typeof createImageBitmap!="undefined"&&((i=e.asyncRender)!=null?i:!0),a=typeof OffscreenCanvas!="undefined"&&((r=e.offscreenRender)!=null?r:!0);this._onDemandRender="requestVideoFrameCallback"in HTMLVideoElement.prototype&&((o=e.onDemandRender)!=null?o:!0),this.timeOffset=e.timeOffset||0,this._video=e.video,this._canvas=e.canvas,this._video&&!this._canvas?(this._canvasParent=document.createElement("div"),this._canvasParent.className="JASSUB",this._canvasParent.style.position="relative",this._canvas=document.createElement("canvas"),this._canvas.style.display="block",this._canvas.style.position="absolute",this._canvas.style.pointerEvents="none",this._canvasParent.appendChild(this._canvas),this._video.nextSibling?this._video.parentNode.insertBefore(this._canvasParent,this._video.nextSibling):this._video.parentNode.appendChild(this._canvasParent)):this._canvas||this.destroy("Don't know where to render: you should give video or canvas in options."),this._bufferCanvas=document.createElement("canvas"),this._bufferCtx=this._bufferCanvas.getContext("2d",{desynchronized:!0,willReadFrequently:!0}),this._canvasctrl=a?this._canvas.transferControlToOffscreen():this._canvas,this._ctx=!a&&this._canvasctrl.getContext("2d",{desynchronized:!0}),this._lastRenderTime=0,this.debug=!!e.debug,this.prescaleFactor=e.prescaleFactor||1,this.prescaleHeightLimit=e.prescaleHeightLimit||1080,this.maxRenderHeight=e.maxRenderHeight||0,this._worker=new Worker(n._supportsWebAssembly?e.workerUrl||"jassub-worker.js":e.legacyWorkerUrl||"jassub-worker-legacy.js"),this._worker.onmessage=c=>this._onmessage(c),this._worker.onerror=c=>this._error(c),this._worker.postMessage({target:"init",asyncRender:s,onDemandRender:this._onDemandRender,width:this._canvas.width,height:this._canvas.height,preMain:!0,blendMode:t,subUrl:e.subUrl,subContent:e.subContent||null,fonts:e.fonts||[],availableFonts:e.availableFonts||{"liberation sans":"./default.woff2"},fallbackFont:e.fallbackFont||"liberation sans",debug:this.debug,targetFps:e.targetFps||24,dropAllAnimations:e.dropAllAnimations,libassMemoryLimit:e.libassMemoryLimit||0,libassGlyphLimit:e.libassGlyphLimit||0,hasAlphaBug:n._hasAlphaBug,useLocalFonts:"queryLocalFonts"in self&&((l=e.useLocalFonts)!=null?l:!0)}),a===!0&&this.sendMessage("offscreenCanvas",null,[this._canvasctrl]),this._boundResize=this.resize.bind(this),this._boundTimeUpdate=this._timeupdate.bind(this),this._boundSetRate=this.setRate.bind(this),this._video&&this.setVideo(e.video),this._onDemandRender&&(this.busy=!1,this._lastDemandTime=null,(m=this._video)==null||m.requestVideoFrameCallback(this._handleRVFC.bind(this)))}static _test(){if(n._supportsWebAssembly!==null)return null;const e=document.createElement("canvas"),t=e.getContext("2d",{willReadFrequently:!0});if(typeof ImageData.prototype.constructor=="function")try{new ImageData(new Uint8ClampedArray([0,0,0,0]),1,1)}catch{console.log("detected that ImageData is not constructable despite browser saying so"),self.ImageData=function(l,m,c){const u=t.createImageData(m,c);return l&&u.data.set(l),u}}try{if(typeof WebAssembly=="object"&&typeof WebAssembly.instantiate=="function"){const o=new WebAssembly.Module(Uint8Array.of(0,97,115,109,1,0,0,0));o instanceof WebAssembly.Module&&(n._supportsWebAssembly=new WebAssembly.Instance(o)instanceof WebAssembly.Instance)}}catch{n._supportsWebAssembly=!1}const s=document.createElement("canvas"),a=s.getContext("2d",{willReadFrequently:!0});e.width=s.width=1,e.height=s.height=1,t.clearRect(0,0,1,1),a.clearRect(0,0,1,1);const i=a.getImageData(0,0,1,1).data;t.putImageData(new ImageData(new Uint8ClampedArray([0,255,0,0]),1,1),0,0),a.drawImage(e,0,0);const r=a.getImageData(0,0,1,1).data;n._hasAlphaBug=i[1]!==r[1],n._hasAlphaBug&&console.log("Detected a browser having issue with transparent pixels, applying workaround"),e.remove(),s.remove()}resize(e=0,t=0,s=0,a=0){let i=null;if((!e||!t)&&this._video){i=this._getVideoPosition();const r=this._computeCanvasSize((i.width||0)*(self.devicePixelRatio||1),(i.height||0)*(self.devicePixelRatio||1));e=r.width,t=r.height,this._canvasParent&&(s=i.y-(this._canvasParent.getBoundingClientRect().top-this._video.getBoundingClientRect().top),a=i.x)}i!=null&&(this._canvas.style.top=s+"px",this._canvas.style.left=a+"px",this._canvas.style.width=i.width+"px",this._canvas.style.height=i.height+"px"),this._canvasctrl.width===e&&this._canvasctrl.height===t||(this._resizeTimeoutBuffer?(clearTimeout(this._resizeTimeoutBuffer),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0,this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t})},100)):(this._canvasctrl.width=e,this._canvasctrl.height=t,this.sendMessage("canvas",{width:e,height:t}),this._resizeTimeoutBuffer=setTimeout(()=>{this._resizeTimeoutBuffer=void 0},100)))}_getVideoPosition(){const e=this._video.videoWidth/this._video.videoHeight,{offsetWidth:t,offsetHeight:s}=this._video,a=t/s;let i=t,r=s;a>e?i=Math.floor(s*e):r=Math.floor(t/e);const o=(t-i)/2,l=(s-r)/2;return{width:i,height:r,x:o,y:l}}_computeCanvasSize(e=0,t=0){const s=this.prescaleFactor<=0?1:this.prescaleFactor;if(t<=0||e<=0)e=0,t=0;else{const a=s<1?-1:1;let i=t;a*i*s<=a*this.prescaleHeightLimit?i*=s:a*i<a*this.prescaleHeightLimit&&(i=this.prescaleHeightLimit),this.maxRenderHeight>0&&i>this.maxRenderHeight&&(i=this.maxRenderHeight),e*=i/t,t=i}return{width:e,height:t}}_timeupdate({type:e}){const s={seeking:!0,waiting:!0,playing:!1}[e];s!=null&&(this._playstate=s),this.setCurrentTime(this._video.paused||this._playstate,this._video.currentTime+this.timeOffset)}setVideo(e){e instanceof HTMLVideoElement?(this._removeListeners(),this._video=e,this._onDemandRender?this._video.requestVideoFrameCallback(this._handleRVFC.bind(this)):(this._playstate=e.paused,e.addEventListener("timeupdate",this._boundTimeUpdate,!1),e.addEventListener("progress",this._boundTimeUpdate,!1),e.addEventListener("waiting",this._boundTimeUpdate,!1),e.addEventListener("seeking",this._boundTimeUpdate,!1),e.addEventListener("playing",this._boundTimeUpdate,!1),e.addEventListener("ratechange",this._boundSetRate,!1)),e.videoWidth>0&&this.resize(),e.addEventListener("resize",this._boundResize),typeof ResizeObserver!="undefined"&&(this._ro||(this._ro=new ResizeObserver(()=>this.resize())),this._ro.observe(e))):this._error("Video element invalid!")}runBenchmark(){this.sendMessage("runBenchmark")}setTrackByUrl(e){this.sendMessage("setTrackByUrl",{url:e})}setTrack(e){this.sendMessage("setTrack",{content:e})}freeTrack(){this.sendMessage("freeTrack")}setIsPaused(e){this.sendMessage("video",{isPaused:e})}setRate(e){this.sendMessage("video",{rate:e})}setCurrentTime(e,t,s){this.sendMessage("video",{isPaused:e,currentTime:t,rate:s})}createEvent(e){this.sendMessage("createEvent",{event:e})}setEvent(e,t){this.sendMessage("setEvent",{event:e,index:t})}removeEvent(e){this.sendMessage("removeEvent",{index:e})}getEvents(e){this._fetchFromWorker({target:"getEvents"},(t,{events:s})=>{e(t,s)})}createStyle(e){this.sendMessage("createStyle",{style:e})}setStyle(e,t){this.sendMessage("setStyle",{event:e,index:t})}removeStyle(e){this.sendMessage("removeStyle",{index:e})}getStyles(e){this._fetchFromWorker({target:"getStyles"},(t,{styles:s})=>{e(t,s)})}addFont(e){this.sendMessage("addFont",{font:e})}_sendLocalFont(e){try{queryLocalFonts().then(t=>{const s=t==null?void 0:t.find(a=>a.fullName.toLowerCase()===e);s&&s.blob().then(a=>{a.arrayBuffer().then(i=>{this.addFont(new Uint8Array(i))})})})}catch(t){console.warn("Local fonts API:",t)}}_getLocalFont({font:e}){var t;try{(t=navigator==null?void 0:navigator.permissions)!=null&&t.query?navigator.permissions.query({name:"local-fonts"}).then(s=>{s.state==="granted"&&this._sendLocalFont(e)}):this._sendLocalFont(e)}catch(s){console.warn("Local fonts API:",s)}}_unbusy(){this._lastDemandTime?this._demandRender(this._lastDemandTime):this.busy=!1}_handleRVFC(e,{mediaTime:t}){if(this._destroyed)return null;this.busy?this._lastDemandTime=t:(this.busy=!0,this._demandRender(t)),this._video.requestVideoFrameCallback(this._handleRVFC.bind(this))}_demandRender(e){this._lastDemandTime=null,this.sendMessage("demand",{time:e+this.timeOffset})}_render({images:e,async:t,times:s}){const a=Date.now();this._ctx.clearRect(0,0,this._canvasctrl.width,this._canvasctrl.height);for(const i of e)i.image&&(t?(this._ctx.drawImage(i.image,i.x,i.y),i.image.close()):(this._bufferCanvas.width=i.w,this._bufferCanvas.height=i.h,this._bufferCtx.putImageData(new ImageData(this._fixAlpha(new Uint8ClampedArray(i.image)),i.w,i.h),0,0),this._ctx.drawImage(this._bufferCanvas,i.x,i.y)));if(this.debug){s.drawTime=Date.now()-a;let i=0;for(const r in s)i+=s[r];console.log("Bitmaps: "+e.length+" Total: "+Math.round(i)+"ms",s)}}_fixAlpha(e){if(n._hasAlphaBug)for(let t=3;t<e.length;t+=4)e[t]=e[t]>1?e[t]:1;return e}_ready(){this.dispatchEvent(new CustomEvent("ready"))}sendMessage(e,t={},s){s?this._worker.postMessage({target:e,transferable:s,...t},[...s]):this._worker.postMessage({target:e,...t})}_fetchFromWorker(e,t){try{const s=e.target,a=setTimeout(()=>{r(new Error("Error: Timeout while try to fetch "+s))},5e3),i=({data:o})=>{o.target===s&&(t(null,o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a))},r=o=>{t(o),this._worker.removeEventListener("message",i),this._worker.removeEventListener("error",r),clearTimeout(a)};this._worker.addEventListener("message",i),this._worker.addEventListener("error",r),this._worker.postMessage(e)}catch(s){this._error(s)}}_console({content:e,command:t}){console[t].apply(console,JSON.parse(e))}_onmessage({data:e}){this["_"+e.target]&&this["_"+e.target](e)}_error(e){throw e instanceof ErrorEvent||this.dispatchEvent(new ErrorEvent("error",{message:e instanceof Error?e.cause:e})),e instanceof Error?e:new Error(e instanceof ErrorEvent?e.message:"error",{cause:e})}_removeListeners(){this._video&&(this._ro&&this._ro.unobserve(this._video),this._video.removeEventListener("timeupdate",this._boundTimeUpdate),this._video.removeEventListener("progress",this._boundTimeUpdate),this._video.removeEventListener("waiting",this._boundTimeUpdate),this._video.removeEventListener("seeking",this._boundTimeUpdate),this._video.removeEventListener("playing",this._boundTimeUpdate),this._video.removeEventListener("ratechange",this._boundSetRate),this._video.removeEventListener("resize",this._boundResize))}destroy(e){e&&this._error(e),this._video&&this._canvasParent&&this._video.parentNode.removeChild(this._canvasParent),this._destroyed=!0,this._removeListeners(),this.sendMessage("destroy"),this._worker.terminate()}};let h=n;return f(h,"_supportsWebAssembly",null),f(h,"_hasAlphaBug",null),h});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jassub",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "libass Subtitle Renderer and Parser library for browsers",
5
5
  "main": "src/jassub.js",
6
6
  "files": [
package/src/jassub.js CHANGED
@@ -44,12 +44,18 @@ export default class JASSUB extends EventTarget {
44
44
 
45
45
  this.timeOffset = options.timeOffset || 0
46
46
  this._video = options.video
47
- this._canvasParent = null
48
- if (this._video) {
47
+ this._canvas = options.canvas
48
+ if (this._video && !this._canvas) {
49
49
  this._canvasParent = document.createElement('div')
50
50
  this._canvasParent.className = 'JASSUB'
51
51
  this._canvasParent.style.position = 'relative'
52
52
 
53
+ this._canvas = document.createElement('canvas')
54
+ this._canvas.style.display = 'block'
55
+ this._canvas.style.position = 'absolute'
56
+ this._canvas.style.pointerEvents = 'none'
57
+ this._canvasParent.appendChild(this._canvas)
58
+
53
59
  if (this._video.nextSibling) {
54
60
  this._video.parentNode.insertBefore(this._canvasParent, this._video.nextSibling)
55
61
  } else {
@@ -59,12 +65,6 @@ export default class JASSUB extends EventTarget {
59
65
  this.destroy('Don\'t know where to render: you should give video or canvas in options.')
60
66
  }
61
67
 
62
- this._canvas = options.canvas || document.createElement('canvas')
63
- this._canvas.style.display = 'block'
64
- this._canvas.style.position = 'absolute'
65
- this._canvas.style.pointerEvents = 'none'
66
- this._canvasParent.appendChild(this._canvas)
67
-
68
68
  this._bufferCanvas = document.createElement('canvas')
69
69
  this._bufferCtx = this._bufferCanvas.getContext('2d', { desynchronized: true, willReadFrequently: true })
70
70
 
@@ -108,7 +108,7 @@ export default class JASSUB extends EventTarget {
108
108
  this._boundResize = this.resize.bind(this)
109
109
  this._boundTimeUpdate = this._timeupdate.bind(this)
110
110
  this._boundSetRate = this.setRate.bind(this)
111
- this.setVideo(options.video)
111
+ if (this._video) this.setVideo(options.video)
112
112
 
113
113
  if (this._onDemandRender) {
114
114
  this.busy = false
@@ -137,7 +137,7 @@ export default class JASSUB extends EventTarget {
137
137
  } catch (e) {
138
138
  console.log('detected that ImageData is not constructable despite browser saying so')
139
139
 
140
- window.ImageData = function (data, width, height) {
140
+ self.ImageData = function (data, width, height) {
141
141
  const imageData = ctx1.createImageData(width, height)
142
142
  if (data) imageData.data.set(data)
143
143
  return imageData
@@ -169,6 +169,7 @@ export default class JASSUB extends EventTarget {
169
169
  const postPut = ctx2.getImageData(0, 0, 1, 1).data
170
170
  JASSUB._hasAlphaBug = prePut[1] !== postPut[1]
171
171
  if (JASSUB._hasAlphaBug) console.log('Detected a browser having issue with transparent pixels, applying workaround')
172
+ canvas1.remove()
172
173
  canvas2.remove()
173
174
  }
174
175
 
@@ -183,11 +184,13 @@ export default class JASSUB extends EventTarget {
183
184
  let videoSize = null
184
185
  if ((!width || !height) && this._video) {
185
186
  videoSize = this._getVideoPosition()
186
- const newsize = this._computeCanvasSize((videoSize.width || 0) * (window.devicePixelRatio || 1), (videoSize.height || 0) * (window.devicePixelRatio || 1))
187
+ const newsize = this._computeCanvasSize((videoSize.width || 0) * (self.devicePixelRatio || 1), (videoSize.height || 0) * (self.devicePixelRatio || 1))
187
188
  width = newsize.width
188
189
  height = newsize.height
189
- top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top)
190
- left = videoSize.x
190
+ if (this._canvasParent) {
191
+ top = videoSize.y - (this._canvasParent.getBoundingClientRect().top - this._video.getBoundingClientRect().top)
192
+ left = videoSize.x
193
+ }
191
194
  }
192
195
 
193
196
  if (videoSize != null) {
@@ -484,12 +487,12 @@ export default class JASSUB extends EventTarget {
484
487
  this.sendMessage('addFont', { font })
485
488
  }
486
489
 
487
- _sendLocalFont (font) {
490
+ _sendLocalFont (name) {
488
491
  try {
489
492
  queryLocalFonts().then(fontData => {
490
- const filtered = fontData && fontData.filter(obj => obj.fullName.toLowerCase() === font)
491
- if (filtered && filtered.length) {
492
- filtered[0].blob().then(blob => {
493
+ const font = fontData?.find(obj => obj.fullName.toLowerCase() === name)
494
+ if (font) {
495
+ font.blob().then(blob => {
493
496
  blob.arrayBuffer().then(buffer => {
494
497
  this.addFont(new Uint8Array(buffer))
495
498
  })
@@ -667,7 +670,7 @@ export default class JASSUB extends EventTarget {
667
670
  */
668
671
  destroy (err) {
669
672
  if (err) this._error(err)
670
- if (this._video) this._video.parentNode.removeChild(this._canvasParent)
673
+ if (this._video && this._canvasParent) this._video.parentNode.removeChild(this._canvasParent)
671
674
  this._destroyed = true
672
675
  this._removeListeners()
673
676
  this.sendMessage('destroy')