web-annotation-renderer 0.1.0
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/CHANGELOG.md +34 -0
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/index10.cjs +2 -0
- package/dist/index10.cjs.map +1 -0
- package/dist/index10.js +23 -0
- package/dist/index10.js.map +1 -0
- package/dist/index11.cjs +2 -0
- package/dist/index11.cjs.map +1 -0
- package/dist/index11.js +23 -0
- package/dist/index11.js.map +1 -0
- package/dist/index12.cjs +2 -0
- package/dist/index12.cjs.map +1 -0
- package/dist/index12.js +203 -0
- package/dist/index12.js.map +1 -0
- package/dist/index13.cjs +2 -0
- package/dist/index13.cjs.map +1 -0
- package/dist/index13.js +18 -0
- package/dist/index13.js.map +1 -0
- package/dist/index14.cjs +2 -0
- package/dist/index14.cjs.map +1 -0
- package/dist/index14.js +116 -0
- package/dist/index14.js.map +1 -0
- package/dist/index15.cjs +2 -0
- package/dist/index15.cjs.map +1 -0
- package/dist/index15.js +35 -0
- package/dist/index15.js.map +1 -0
- package/dist/index2.cjs +2 -0
- package/dist/index2.cjs.map +1 -0
- package/dist/index2.js +182 -0
- package/dist/index2.js.map +1 -0
- package/dist/index3.cjs +2 -0
- package/dist/index3.cjs.map +1 -0
- package/dist/index3.js +121 -0
- package/dist/index3.js.map +1 -0
- package/dist/index4.cjs +2 -0
- package/dist/index4.cjs.map +1 -0
- package/dist/index4.js +104 -0
- package/dist/index4.js.map +1 -0
- package/dist/index5.cjs +2 -0
- package/dist/index5.cjs.map +1 -0
- package/dist/index5.js +105 -0
- package/dist/index5.js.map +1 -0
- package/dist/index6.cjs +2 -0
- package/dist/index6.cjs.map +1 -0
- package/dist/index6.js +119 -0
- package/dist/index6.js.map +1 -0
- package/dist/index7.cjs +2 -0
- package/dist/index7.cjs.map +1 -0
- package/dist/index7.js +100 -0
- package/dist/index7.js.map +1 -0
- package/dist/index8.cjs +2 -0
- package/dist/index8.cjs.map +1 -0
- package/dist/index8.js +109 -0
- package/dist/index8.js.map +1 -0
- package/dist/index9.cjs +2 -0
- package/dist/index9.cjs.map +1 -0
- package/dist/index9.js +112 -0
- package/dist/index9.js.map +1 -0
- package/package.json +84 -0
package/dist/index5.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
class e {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.currentTime = 0, this.subscribers = /* @__PURE__ */ new Set(), this.animationFrameId = null, this.isRunning = !1;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Set timeline position and notify subscribers if changed
|
|
7
|
+
*
|
|
8
|
+
* @param {number} timestamp - Timeline position in seconds
|
|
9
|
+
* @returns {void}
|
|
10
|
+
*/
|
|
11
|
+
setTime(n) {
|
|
12
|
+
n !== this.currentTime && (this.currentTime = n, this.notifySubscribers());
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get current timeline position
|
|
16
|
+
*
|
|
17
|
+
* @returns {number} Current timeline position in seconds
|
|
18
|
+
*/
|
|
19
|
+
getCurrentTime() {
|
|
20
|
+
return this.currentTime;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to timeline updates
|
|
24
|
+
*
|
|
25
|
+
* @param {Function} callback - Function to call on timeline updates
|
|
26
|
+
* @returns {Function} Unsubscribe function
|
|
27
|
+
* @throws {Error} If callback is not a function
|
|
28
|
+
*/
|
|
29
|
+
subscribe(n) {
|
|
30
|
+
if (typeof n != "function")
|
|
31
|
+
throw new Error("TimelineSync.subscribe: callback must be a function");
|
|
32
|
+
return this.subscribers.add(n), () => this.unsubscribe(n);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Unsubscribe from timeline updates
|
|
36
|
+
*
|
|
37
|
+
* @param {Function} callback - Callback function to remove
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
unsubscribe(n) {
|
|
41
|
+
this.subscribers.delete(n);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Start continuous timeline synchronization
|
|
45
|
+
*
|
|
46
|
+
* @param {Function} getTimeFunction - Function that returns current time
|
|
47
|
+
* @returns {void}
|
|
48
|
+
* @throws {Error} If getTimeFunction is not a function
|
|
49
|
+
*/
|
|
50
|
+
startContinuousSync(n) {
|
|
51
|
+
if (typeof n != "function")
|
|
52
|
+
throw new Error("TimelineSync.startContinuousSync: getTimeFunction must be a function");
|
|
53
|
+
if (this.isRunning) {
|
|
54
|
+
console.warn("TimelineSync: Continuous sync already running");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.isRunning = !0;
|
|
58
|
+
const i = () => {
|
|
59
|
+
if (this.isRunning) {
|
|
60
|
+
try {
|
|
61
|
+
const r = n();
|
|
62
|
+
this.setTime(r);
|
|
63
|
+
} catch (r) {
|
|
64
|
+
console.error("TimelineSync: Error in continuous sync:", r);
|
|
65
|
+
}
|
|
66
|
+
this.animationFrameId = requestAnimationFrame(i);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
i();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Stop continuous timeline synchronization
|
|
73
|
+
*
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*/
|
|
76
|
+
stopContinuousSync() {
|
|
77
|
+
this.isRunning = !1, this.animationFrameId !== null && (cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Clean up resources and release references
|
|
81
|
+
*
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
84
|
+
destroy() {
|
|
85
|
+
this.stopContinuousSync(), this.subscribers.clear(), this.currentTime = 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Notify all subscribers of current time
|
|
89
|
+
*
|
|
90
|
+
* @private
|
|
91
|
+
* @returns {void}
|
|
92
|
+
*/
|
|
93
|
+
notifySubscribers() {
|
|
94
|
+
for (const n of this.subscribers)
|
|
95
|
+
try {
|
|
96
|
+
n(this.currentTime);
|
|
97
|
+
} catch (i) {
|
|
98
|
+
console.error("TimelineSync: Subscriber callback error:", i);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
e as TimelineSync
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=index5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index5.js","sources":["../src/core/TimelineSync.js"],"sourcesContent":["/**\n * TimelineSync - Framework-agnostic timeline synchronization subsystem\n *\n * This module manages timeline position and provides a subscriber notification\n * system for timeline updates. Supports both discrete updates (manual setTime)\n * and continuous synchronization via requestAnimationFrame for audio/video.\n *\n * @module core/TimelineSync\n */\n\n/**\n * TimelineSync class\n *\n * Provides timeline state management and pub-sub notification system.\n * Zero dependencies - pure JavaScript implementation.\n *\n * @class\n * @example\n * // Discrete mode\n * const sync = new TimelineSync();\n * sync.subscribe((time) => console.log('Time:', time));\n * sync.setTime(5.0);\n *\n * @example\n * // Continuous mode with audio\n * const audio = document.getElementById('audio');\n * sync.startContinuousSync(() => audio.currentTime);\n */\nexport class TimelineSync {\n constructor() {\n /**\n * @private\n * @type {number}\n */\n this.currentTime = 0;\n\n /**\n * @private\n * @type {Set<Function>}\n */\n this.subscribers = new Set();\n\n /**\n * @private\n * @type {number|null}\n */\n this.animationFrameId = null;\n\n /**\n * @private\n * @type {boolean}\n */\n this.isRunning = false;\n }\n\n /**\n * Set timeline position and notify subscribers if changed\n *\n * @param {number} timestamp - Timeline position in seconds\n * @returns {void}\n */\n setTime(timestamp) {\n if (timestamp === this.currentTime) {\n return;\n }\n\n this.currentTime = timestamp;\n this.notifySubscribers();\n }\n\n /**\n * Get current timeline position\n *\n * @returns {number} Current timeline position in seconds\n */\n getCurrentTime() {\n return this.currentTime;\n }\n\n /**\n * Subscribe to timeline updates\n *\n * @param {Function} callback - Function to call on timeline updates\n * @returns {Function} Unsubscribe function\n * @throws {Error} If callback is not a function\n */\n subscribe(callback) {\n if (typeof callback !== 'function') {\n throw new Error('TimelineSync.subscribe: callback must be a function');\n }\n\n this.subscribers.add(callback);\n\n return () => this.unsubscribe(callback);\n }\n\n /**\n * Unsubscribe from timeline updates\n *\n * @param {Function} callback - Callback function to remove\n * @returns {void}\n */\n unsubscribe(callback) {\n this.subscribers.delete(callback);\n }\n\n /**\n * Start continuous timeline synchronization\n *\n * @param {Function} getTimeFunction - Function that returns current time\n * @returns {void}\n * @throws {Error} If getTimeFunction is not a function\n */\n startContinuousSync(getTimeFunction) {\n if (typeof getTimeFunction !== 'function') {\n throw new Error('TimelineSync.startContinuousSync: getTimeFunction must be a function');\n }\n\n if (this.isRunning) {\n console.warn('TimelineSync: Continuous sync already running');\n return;\n }\n\n this.isRunning = true;\n\n const syncLoop = () => {\n if (!this.isRunning) {\n return;\n }\n\n try {\n const newTime = getTimeFunction();\n this.setTime(newTime);\n } catch (err) {\n console.error('TimelineSync: Error in continuous sync:', err);\n }\n\n this.animationFrameId = requestAnimationFrame(syncLoop);\n };\n\n syncLoop();\n }\n\n /**\n * Stop continuous timeline synchronization\n *\n * @returns {void}\n */\n stopContinuousSync() {\n this.isRunning = false;\n\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n /**\n * Clean up resources and release references\n *\n * @returns {void}\n */\n destroy() {\n this.stopContinuousSync();\n this.subscribers.clear();\n this.currentTime = 0;\n }\n\n /**\n * Notify all subscribers of current time\n *\n * @private\n * @returns {void}\n */\n notifySubscribers() {\n for (const callback of this.subscribers) {\n try {\n callback(this.currentTime);\n } catch (err) {\n console.error('TimelineSync: Subscriber callback error:', err);\n }\n }\n }\n}\n"],"names":["TimelineSync","timestamp","callback","getTimeFunction","syncLoop","newTime","err"],"mappings":"AA4BO,MAAMA,EAAa;AAAA,EACxB,cAAc;AAKZ,SAAK,cAAc,GAMnB,KAAK,cAAc,oBAAI,IAAG,GAM1B,KAAK,mBAAmB,MAMxB,KAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQC,GAAW;AACjB,IAAIA,MAAc,KAAK,gBAIvB,KAAK,cAAcA,GACnB,KAAK,kBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAUC,GAAU;AAClB,QAAI,OAAOA,KAAa;AACtB,YAAM,IAAI,MAAM,qDAAqD;AAGvE,gBAAK,YAAY,IAAIA,CAAQ,GAEtB,MAAM,KAAK,YAAYA,CAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAYA,GAAU;AACpB,SAAK,YAAY,OAAOA,CAAQ;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,oBAAoBC,GAAiB;AACnC,QAAI,OAAOA,KAAoB;AAC7B,YAAM,IAAI,MAAM,sEAAsE;AAGxF,QAAI,KAAK,WAAW;AAClB,cAAQ,KAAK,+CAA+C;AAC5D;AAAA,IACF;AAEA,SAAK,YAAY;AAEjB,UAAMC,IAAW,MAAM;AACrB,UAAK,KAAK,WAIV;AAAA,YAAI;AACF,gBAAMC,IAAUF,EAAe;AAC/B,eAAK,QAAQE,CAAO;AAAA,QACtB,SAASC,GAAK;AACZ,kBAAQ,MAAM,2CAA2CA,CAAG;AAAA,QAC9D;AAEA,aAAK,mBAAmB,sBAAsBF,CAAQ;AAAA;AAAA,IACxD;AAEA,IAAAA,EAAQ;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAAqB;AACnB,SAAK,YAAY,IAEb,KAAK,qBAAqB,SAC5B,qBAAqB,KAAK,gBAAgB,GAC1C,KAAK,mBAAmB;AAAA,EAE5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU;AACR,SAAK,mBAAkB,GACvB,KAAK,YAAY,MAAK,GACtB,KAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,oBAAoB;AAClB,eAAWF,KAAY,KAAK;AAC1B,UAAI;AACF,QAAAA,EAAS,KAAK,WAAW;AAAA,MAC3B,SAASI,GAAK;AACZ,gBAAQ,MAAM,4CAA4CA,CAAG;AAAA,MAC/D;AAAA,EAEJ;AACF;"}
|
package/dist/index6.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});class t{constructor(e,r){if(this._validateContainer(e),this._validateViewport(r),this.container=e,this.viewport={...r},this.annotations=[],this.currentTime=0,this.isDestroyed=!1,new.target===t)throw new Error("BaseLayer is an abstract class and cannot be instantiated directly. Extend it with a concrete implementation.")}setAnnotations(e){this._checkDestroyed("setAnnotations"),this.annotations=e||[]}setViewport(e){this._checkDestroyed("setViewport"),this._validateViewport(e),this.viewport={...e}}updateTime(e){this._checkDestroyed("updateTime"),this.currentTime=e}destroy(){this.isDestroyed||(this.annotations=null,this.viewport=null,this.container=null,this.isDestroyed=!0)}render(){throw new Error("render() must be implemented by subclass")}update(){throw new Error("update() must be implemented by subclass")}_validateContainer(e){if(!e||!(e instanceof HTMLElement))throw new Error("BaseLayer: container must be a valid HTMLElement")}_validateViewport(e){if(!e||typeof e!="object")throw new Error("BaseLayer: viewport must be an object");if(typeof e.width!="number"||e.width<=0)throw new Error("BaseLayer: viewport.width must be a positive number");if(typeof e.height!="number"||e.height<=0)throw new Error("BaseLayer: viewport.height must be a positive number");if(typeof e.scale!="number"||e.scale<=0)throw new Error("BaseLayer: viewport.scale must be a positive number")}_checkDestroyed(e){if(this.isDestroyed)throw new Error(`BaseLayer: Cannot call ${e}() on destroyed layer`)}}exports.default=t;
|
|
2
|
+
//# sourceMappingURL=index6.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index6.cjs","sources":["../src/layers/BaseLayer.js"],"sourcesContent":["/**\n * BaseLayer - Abstract base class for annotation layers\n *\n * Provides common interface and lifecycle management for all annotation layer types.\n * Subclasses must implement render() and update() abstract methods.\n *\n * @abstract\n */\nclass BaseLayer {\n /**\n * Creates a new BaseLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n * @throws {Error} If container is not a valid HTMLElement\n * @throws {Error} If viewport is missing required properties\n * @throws {Error} If instantiated directly (abstract class)\n */\n constructor(container, viewport) {\n // Validate parameters\n this._validateContainer(container);\n this._validateViewport(viewport);\n\n // Initialize core properties\n this.container = container;\n this.viewport = { ...viewport };\n this.annotations = [];\n this.currentTime = 0;\n this.isDestroyed = false;\n\n // Prevent direct instantiation\n if (new.target === BaseLayer) {\n throw new Error('BaseLayer is an abstract class and cannot be instantiated directly. Extend it with a concrete implementation.');\n }\n }\n\n /**\n * Sets the annotation data for this layer\n *\n * @param {Array} annotations - Array of annotation objects\n * @throws {Error} If called after layer is destroyed\n */\n setAnnotations(annotations) {\n this._checkDestroyed('setAnnotations');\n this.annotations = annotations || [];\n }\n\n /**\n * Updates the viewport dimensions\n *\n * @param {Object} viewport - New viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n * @throws {Error} If viewport is missing required properties\n * @throws {Error} If called after layer is destroyed\n */\n setViewport(viewport) {\n this._checkDestroyed('setViewport');\n this._validateViewport(viewport);\n this.viewport = { ...viewport };\n }\n\n /**\n * Updates the current timeline position\n *\n * @param {number} nowSec - Current timeline position in seconds\n * @throws {Error} If called after layer is destroyed\n */\n updateTime(nowSec) {\n this._checkDestroyed('updateTime');\n this.currentTime = nowSec;\n }\n\n /**\n * Destroys the layer and releases resources\n *\n * Safe to call multiple times (idempotent).\n * Subclasses must call super.destroy() after their own cleanup.\n */\n destroy() {\n if (this.isDestroyed) {\n return;\n }\n\n this.annotations = null;\n this.viewport = null;\n this.container = null;\n this.isDestroyed = true;\n }\n\n /**\n * Renders the layer content\n *\n * @abstract\n * @throws {Error} If not implemented by subclass\n */\n render() {\n throw new Error('render() must be implemented by subclass');\n }\n\n /**\n * Updates the visual state of the layer\n *\n * @abstract\n * @throws {Error} If not implemented by subclass\n */\n update() {\n throw new Error('update() must be implemented by subclass');\n }\n\n /**\n * Validates that container is a valid HTMLElement\n *\n * @private\n * @param {*} container - Value to validate\n * @throws {Error} If container is not a valid HTMLElement\n */\n _validateContainer(container) {\n if (!container || !(container instanceof HTMLElement)) {\n throw new Error('BaseLayer: container must be a valid HTMLElement');\n }\n }\n\n /**\n * Validates that viewport has required properties\n *\n * @private\n * @param {*} viewport - Value to validate\n * @throws {Error} If viewport is missing required properties\n */\n _validateViewport(viewport) {\n if (!viewport || typeof viewport !== 'object') {\n throw new Error('BaseLayer: viewport must be an object');\n }\n\n if (typeof viewport.width !== 'number' || viewport.width <= 0) {\n throw new Error('BaseLayer: viewport.width must be a positive number');\n }\n\n if (typeof viewport.height !== 'number' || viewport.height <= 0) {\n throw new Error('BaseLayer: viewport.height must be a positive number');\n }\n\n if (typeof viewport.scale !== 'number' || viewport.scale <= 0) {\n throw new Error('BaseLayer: viewport.scale must be a positive number');\n }\n }\n\n /**\n * Checks if layer is destroyed and throws error if so\n *\n * @private\n * @param {string} methodName - Name of method being called\n * @throws {Error} If layer is destroyed\n */\n _checkDestroyed(methodName) {\n if (this.isDestroyed) {\n throw new Error(`BaseLayer: Cannot call ${methodName}() on destroyed layer`);\n }\n }\n}\n\nexport default BaseLayer;\n"],"names":["BaseLayer","container","viewport","annotations","nowSec","methodName"],"mappings":"4GAQA,MAAMA,CAAU,CAad,YAAYC,EAAWC,EAAU,CAa/B,GAXA,KAAK,mBAAmBD,CAAS,EACjC,KAAK,kBAAkBC,CAAQ,EAG/B,KAAK,UAAYD,EACjB,KAAK,SAAW,CAAE,GAAGC,CAAQ,EAC7B,KAAK,YAAc,CAAA,EACnB,KAAK,YAAc,EACnB,KAAK,YAAc,GAGf,aAAeF,EACjB,MAAM,IAAI,MAAM,+GAA+G,CAEnI,CAQA,eAAeG,EAAa,CAC1B,KAAK,gBAAgB,gBAAgB,EACrC,KAAK,YAAcA,GAAe,CAAA,CACpC,CAYA,YAAYD,EAAU,CACpB,KAAK,gBAAgB,aAAa,EAClC,KAAK,kBAAkBA,CAAQ,EAC/B,KAAK,SAAW,CAAE,GAAGA,CAAQ,CAC/B,CAQA,WAAWE,EAAQ,CACjB,KAAK,gBAAgB,YAAY,EACjC,KAAK,YAAcA,CACrB,CAQA,SAAU,CACJ,KAAK,cAIT,KAAK,YAAc,KACnB,KAAK,SAAW,KAChB,KAAK,UAAY,KACjB,KAAK,YAAc,GACrB,CAQA,QAAS,CACP,MAAM,IAAI,MAAM,0CAA0C,CAC5D,CAQA,QAAS,CACP,MAAM,IAAI,MAAM,0CAA0C,CAC5D,CASA,mBAAmBH,EAAW,CAC5B,GAAI,CAACA,GAAa,EAAEA,aAAqB,aACvC,MAAM,IAAI,MAAM,kDAAkD,CAEtE,CASA,kBAAkBC,EAAU,CAC1B,GAAI,CAACA,GAAY,OAAOA,GAAa,SACnC,MAAM,IAAI,MAAM,uCAAuC,EAGzD,GAAI,OAAOA,EAAS,OAAU,UAAYA,EAAS,OAAS,EAC1D,MAAM,IAAI,MAAM,qDAAqD,EAGvE,GAAI,OAAOA,EAAS,QAAW,UAAYA,EAAS,QAAU,EAC5D,MAAM,IAAI,MAAM,sDAAsD,EAGxE,GAAI,OAAOA,EAAS,OAAU,UAAYA,EAAS,OAAS,EAC1D,MAAM,IAAI,MAAM,qDAAqD,CAEzE,CASA,gBAAgBG,EAAY,CAC1B,GAAI,KAAK,YACP,MAAM,IAAI,MAAM,0BAA0BA,CAAU,uBAAuB,CAE/E,CACF"}
|
package/dist/index6.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
class r {
|
|
2
|
+
/**
|
|
3
|
+
* Creates a new BaseLayer instance
|
|
4
|
+
*
|
|
5
|
+
* @param {HTMLElement} container - Parent DOM element for layer content
|
|
6
|
+
* @param {Object} viewport - Initial viewport dimensions
|
|
7
|
+
* @param {number} viewport.width - Viewport width in pixels
|
|
8
|
+
* @param {number} viewport.height - Viewport height in pixels
|
|
9
|
+
* @param {number} viewport.scale - PDF scale/zoom level
|
|
10
|
+
* @throws {Error} If container is not a valid HTMLElement
|
|
11
|
+
* @throws {Error} If viewport is missing required properties
|
|
12
|
+
* @throws {Error} If instantiated directly (abstract class)
|
|
13
|
+
*/
|
|
14
|
+
constructor(e, t) {
|
|
15
|
+
if (this._validateContainer(e), this._validateViewport(t), this.container = e, this.viewport = { ...t }, this.annotations = [], this.currentTime = 0, this.isDestroyed = !1, new.target === r)
|
|
16
|
+
throw new Error("BaseLayer is an abstract class and cannot be instantiated directly. Extend it with a concrete implementation.");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sets the annotation data for this layer
|
|
20
|
+
*
|
|
21
|
+
* @param {Array} annotations - Array of annotation objects
|
|
22
|
+
* @throws {Error} If called after layer is destroyed
|
|
23
|
+
*/
|
|
24
|
+
setAnnotations(e) {
|
|
25
|
+
this._checkDestroyed("setAnnotations"), this.annotations = e || [];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Updates the viewport dimensions
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} viewport - New viewport dimensions
|
|
31
|
+
* @param {number} viewport.width - Viewport width in pixels
|
|
32
|
+
* @param {number} viewport.height - Viewport height in pixels
|
|
33
|
+
* @param {number} viewport.scale - PDF scale/zoom level
|
|
34
|
+
* @throws {Error} If viewport is missing required properties
|
|
35
|
+
* @throws {Error} If called after layer is destroyed
|
|
36
|
+
*/
|
|
37
|
+
setViewport(e) {
|
|
38
|
+
this._checkDestroyed("setViewport"), this._validateViewport(e), this.viewport = { ...e };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Updates the current timeline position
|
|
42
|
+
*
|
|
43
|
+
* @param {number} nowSec - Current timeline position in seconds
|
|
44
|
+
* @throws {Error} If called after layer is destroyed
|
|
45
|
+
*/
|
|
46
|
+
updateTime(e) {
|
|
47
|
+
this._checkDestroyed("updateTime"), this.currentTime = e;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Destroys the layer and releases resources
|
|
51
|
+
*
|
|
52
|
+
* Safe to call multiple times (idempotent).
|
|
53
|
+
* Subclasses must call super.destroy() after their own cleanup.
|
|
54
|
+
*/
|
|
55
|
+
destroy() {
|
|
56
|
+
this.isDestroyed || (this.annotations = null, this.viewport = null, this.container = null, this.isDestroyed = !0);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Renders the layer content
|
|
60
|
+
*
|
|
61
|
+
* @abstract
|
|
62
|
+
* @throws {Error} If not implemented by subclass
|
|
63
|
+
*/
|
|
64
|
+
render() {
|
|
65
|
+
throw new Error("render() must be implemented by subclass");
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Updates the visual state of the layer
|
|
69
|
+
*
|
|
70
|
+
* @abstract
|
|
71
|
+
* @throws {Error} If not implemented by subclass
|
|
72
|
+
*/
|
|
73
|
+
update() {
|
|
74
|
+
throw new Error("update() must be implemented by subclass");
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validates that container is a valid HTMLElement
|
|
78
|
+
*
|
|
79
|
+
* @private
|
|
80
|
+
* @param {*} container - Value to validate
|
|
81
|
+
* @throws {Error} If container is not a valid HTMLElement
|
|
82
|
+
*/
|
|
83
|
+
_validateContainer(e) {
|
|
84
|
+
if (!e || !(e instanceof HTMLElement))
|
|
85
|
+
throw new Error("BaseLayer: container must be a valid HTMLElement");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validates that viewport has required properties
|
|
89
|
+
*
|
|
90
|
+
* @private
|
|
91
|
+
* @param {*} viewport - Value to validate
|
|
92
|
+
* @throws {Error} If viewport is missing required properties
|
|
93
|
+
*/
|
|
94
|
+
_validateViewport(e) {
|
|
95
|
+
if (!e || typeof e != "object")
|
|
96
|
+
throw new Error("BaseLayer: viewport must be an object");
|
|
97
|
+
if (typeof e.width != "number" || e.width <= 0)
|
|
98
|
+
throw new Error("BaseLayer: viewport.width must be a positive number");
|
|
99
|
+
if (typeof e.height != "number" || e.height <= 0)
|
|
100
|
+
throw new Error("BaseLayer: viewport.height must be a positive number");
|
|
101
|
+
if (typeof e.scale != "number" || e.scale <= 0)
|
|
102
|
+
throw new Error("BaseLayer: viewport.scale must be a positive number");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Checks if layer is destroyed and throws error if so
|
|
106
|
+
*
|
|
107
|
+
* @private
|
|
108
|
+
* @param {string} methodName - Name of method being called
|
|
109
|
+
* @throws {Error} If layer is destroyed
|
|
110
|
+
*/
|
|
111
|
+
_checkDestroyed(e) {
|
|
112
|
+
if (this.isDestroyed)
|
|
113
|
+
throw new Error(`BaseLayer: Cannot call ${e}() on destroyed layer`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
r as default
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=index6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index6.js","sources":["../src/layers/BaseLayer.js"],"sourcesContent":["/**\n * BaseLayer - Abstract base class for annotation layers\n *\n * Provides common interface and lifecycle management for all annotation layer types.\n * Subclasses must implement render() and update() abstract methods.\n *\n * @abstract\n */\nclass BaseLayer {\n /**\n * Creates a new BaseLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n * @throws {Error} If container is not a valid HTMLElement\n * @throws {Error} If viewport is missing required properties\n * @throws {Error} If instantiated directly (abstract class)\n */\n constructor(container, viewport) {\n // Validate parameters\n this._validateContainer(container);\n this._validateViewport(viewport);\n\n // Initialize core properties\n this.container = container;\n this.viewport = { ...viewport };\n this.annotations = [];\n this.currentTime = 0;\n this.isDestroyed = false;\n\n // Prevent direct instantiation\n if (new.target === BaseLayer) {\n throw new Error('BaseLayer is an abstract class and cannot be instantiated directly. Extend it with a concrete implementation.');\n }\n }\n\n /**\n * Sets the annotation data for this layer\n *\n * @param {Array} annotations - Array of annotation objects\n * @throws {Error} If called after layer is destroyed\n */\n setAnnotations(annotations) {\n this._checkDestroyed('setAnnotations');\n this.annotations = annotations || [];\n }\n\n /**\n * Updates the viewport dimensions\n *\n * @param {Object} viewport - New viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n * @throws {Error} If viewport is missing required properties\n * @throws {Error} If called after layer is destroyed\n */\n setViewport(viewport) {\n this._checkDestroyed('setViewport');\n this._validateViewport(viewport);\n this.viewport = { ...viewport };\n }\n\n /**\n * Updates the current timeline position\n *\n * @param {number} nowSec - Current timeline position in seconds\n * @throws {Error} If called after layer is destroyed\n */\n updateTime(nowSec) {\n this._checkDestroyed('updateTime');\n this.currentTime = nowSec;\n }\n\n /**\n * Destroys the layer and releases resources\n *\n * Safe to call multiple times (idempotent).\n * Subclasses must call super.destroy() after their own cleanup.\n */\n destroy() {\n if (this.isDestroyed) {\n return;\n }\n\n this.annotations = null;\n this.viewport = null;\n this.container = null;\n this.isDestroyed = true;\n }\n\n /**\n * Renders the layer content\n *\n * @abstract\n * @throws {Error} If not implemented by subclass\n */\n render() {\n throw new Error('render() must be implemented by subclass');\n }\n\n /**\n * Updates the visual state of the layer\n *\n * @abstract\n * @throws {Error} If not implemented by subclass\n */\n update() {\n throw new Error('update() must be implemented by subclass');\n }\n\n /**\n * Validates that container is a valid HTMLElement\n *\n * @private\n * @param {*} container - Value to validate\n * @throws {Error} If container is not a valid HTMLElement\n */\n _validateContainer(container) {\n if (!container || !(container instanceof HTMLElement)) {\n throw new Error('BaseLayer: container must be a valid HTMLElement');\n }\n }\n\n /**\n * Validates that viewport has required properties\n *\n * @private\n * @param {*} viewport - Value to validate\n * @throws {Error} If viewport is missing required properties\n */\n _validateViewport(viewport) {\n if (!viewport || typeof viewport !== 'object') {\n throw new Error('BaseLayer: viewport must be an object');\n }\n\n if (typeof viewport.width !== 'number' || viewport.width <= 0) {\n throw new Error('BaseLayer: viewport.width must be a positive number');\n }\n\n if (typeof viewport.height !== 'number' || viewport.height <= 0) {\n throw new Error('BaseLayer: viewport.height must be a positive number');\n }\n\n if (typeof viewport.scale !== 'number' || viewport.scale <= 0) {\n throw new Error('BaseLayer: viewport.scale must be a positive number');\n }\n }\n\n /**\n * Checks if layer is destroyed and throws error if so\n *\n * @private\n * @param {string} methodName - Name of method being called\n * @throws {Error} If layer is destroyed\n */\n _checkDestroyed(methodName) {\n if (this.isDestroyed) {\n throw new Error(`BaseLayer: Cannot call ${methodName}() on destroyed layer`);\n }\n }\n}\n\nexport default BaseLayer;\n"],"names":["BaseLayer","container","viewport","annotations","nowSec","methodName"],"mappings":"AAQA,MAAMA,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad,YAAYC,GAAWC,GAAU;AAa/B,QAXA,KAAK,mBAAmBD,CAAS,GACjC,KAAK,kBAAkBC,CAAQ,GAG/B,KAAK,YAAYD,GACjB,KAAK,WAAW,EAAE,GAAGC,EAAQ,GAC7B,KAAK,cAAc,CAAA,GACnB,KAAK,cAAc,GACnB,KAAK,cAAc,IAGf,eAAeF;AACjB,YAAM,IAAI,MAAM,+GAA+G;AAAA,EAEnI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAeG,GAAa;AAC1B,SAAK,gBAAgB,gBAAgB,GACrC,KAAK,cAAcA,KAAe,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAYD,GAAU;AACpB,SAAK,gBAAgB,aAAa,GAClC,KAAK,kBAAkBA,CAAQ,GAC/B,KAAK,WAAW,EAAE,GAAGA,EAAQ;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,WAAWE,GAAQ;AACjB,SAAK,gBAAgB,YAAY,GACjC,KAAK,cAAcA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU;AACR,IAAI,KAAK,gBAIT,KAAK,cAAc,MACnB,KAAK,WAAW,MAChB,KAAK,YAAY,MACjB,KAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS;AACP,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS;AACP,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,mBAAmBH,GAAW;AAC5B,QAAI,CAACA,KAAa,EAAEA,aAAqB;AACvC,YAAM,IAAI,MAAM,kDAAkD;AAAA,EAEtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,kBAAkBC,GAAU;AAC1B,QAAI,CAACA,KAAY,OAAOA,KAAa;AACnC,YAAM,IAAI,MAAM,uCAAuC;AAGzD,QAAI,OAAOA,EAAS,SAAU,YAAYA,EAAS,SAAS;AAC1D,YAAM,IAAI,MAAM,qDAAqD;AAGvE,QAAI,OAAOA,EAAS,UAAW,YAAYA,EAAS,UAAU;AAC5D,YAAM,IAAI,MAAM,sDAAsD;AAGxE,QAAI,OAAOA,EAAS,SAAU,YAAYA,EAAS,SAAS;AAC1D,YAAM,IAAI,MAAM,qDAAqD;AAAA,EAEzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,gBAAgBG,GAAY;AAC1B,QAAI,KAAK;AACP,YAAM,IAAI,MAAM,0BAA0BA,CAAU,uBAAuB;AAAA,EAE/E;AACF;"}
|
package/dist/index7.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const y=require("./index6.cjs"),u=require("./index10.cjs");class p extends y.default{constructor(e,r){super(e,r),this.layerElement=document.createElement("div"),this.layerElement.style.position="absolute",this.layerElement.style.inset="0",this.layerElement.style.pointerEvents="none",this.layerElement.style.zIndex="25",this.container.appendChild(this.layerElement),this.elements=new Map,this.rafId=null}render(){this.layerElement.innerHTML="",this.elements.clear(),this.annotations.forEach(e=>{if(e.mode!=="quads"||!e.quads?.length)return;const r=e.quads.reduce((a,i)=>a+i.w,0);e.quads.forEach((a,i)=>{const s=u.rectNormToAbs(a,this.viewport),n=e.quads.slice(0,i).reduce((m,c)=>m+c.w,0),h=n/r,d=(n+a.w)/r,t=document.createElement("div");t.style.position="absolute",t.style.left=`${s.left}px`,t.style.top=`${s.top}px`,t.style.width=`${s.width}px`,t.style.height=`${s.height}px`,t.style.overflow="hidden",t.style.borderRadius="2px";const l=document.createElement("div");l.style.width="100%",l.style.height="100%",l.style.background=e?.style?.color??"rgba(255,230,100,0.35)",l.style.outline="1px solid rgba(255,200,0,0.6)",l.style.transformOrigin="left center",l.style.transform="scaleX(0)",l.style.willChange="transform",t.appendChild(l),this.layerElement.appendChild(t);const o=`${e.id}-${i}`;this.elements.set(o,{element:l,wrapper:t,annotation:e,segStart:h,segEnd:d})})})}updateTime(e){super.updateTime(e),this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null);const r=()=>{this.isDestroyed||(this.elements.forEach(({element:a,wrapper:i,annotation:s,segStart:n,segEnd:h})=>{if(e<s.start)i.style.display="none";else{i.style.display="block";const d=Math.max(0,Math.min(1,(e-s.start)/Math.max(1e-6,s.end-s.start))),t=Math.max(0,Math.min(1,(d-n)/Math.max(1e-6,h-n)));a.style.transform=`scaleX(${t})`}}),this.rafId=requestAnimationFrame(r))};r()}update(){}destroy(){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.elements.clear(),this.elements=null,this.layerElement&&this.layerElement.parentNode&&this.layerElement.parentNode.removeChild(this.layerElement),this.layerElement=null,super.destroy()}}exports.default=p;
|
|
2
|
+
//# sourceMappingURL=index7.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index7.cjs","sources":["../src/layers/HighlightLayer.js"],"sourcesContent":["import BaseLayer from './BaseLayer.js';\nimport { rectNormToAbs } from '../utils/coordinateUtils.js';\n\n/**\n * HighlightLayer - Renders highlight annotations with progressive reveal\n *\n * Extends BaseLayer to render rectangular highlight regions (quads) with\n * progressive left-to-right scaleX animation based on timeline position.\n * Supports multi-line highlights with per-quad timing segments.\n *\n * @extends BaseLayer\n */\nclass HighlightLayer extends BaseLayer {\n /**\n * Creates a new HighlightLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n */\n constructor(container, viewport) {\n super(container, viewport);\n\n // Create layer container element\n this.layerElement = document.createElement('div');\n this.layerElement.style.position = 'absolute';\n this.layerElement.style.inset = '0';\n this.layerElement.style.pointerEvents = 'none';\n this.layerElement.style.zIndex = '25';\n\n this.container.appendChild(this.layerElement);\n\n // Initialize element storage\n this.elements = new Map();\n\n // Initialize RAF ID\n this.rafId = null;\n }\n\n /**\n * Renders highlight elements for all annotations\n *\n * Creates DOM structure for each quad in each annotation. Calculates\n * timing segments for progressive animation. Clears and recreates all\n * elements when called.\n */\n render() {\n // Clear existing elements\n this.layerElement.innerHTML = '';\n this.elements.clear();\n\n // Process each annotation\n this.annotations.forEach((annotation) => {\n // Skip if not quad mode or no quads\n if (annotation.mode !== 'quads' || !annotation.quads?.length) {\n return;\n }\n\n // Calculate total width across all quads\n const totalW = annotation.quads.reduce((sum, quad) => sum + quad.w, 0);\n\n // Process each quad\n annotation.quads.forEach((quad, idx) => {\n // Convert normalized coordinates to absolute pixels\n const abs = rectNormToAbs(quad, this.viewport);\n\n // Calculate timing segment for this quad\n const prevW = annotation.quads.slice(0, idx).reduce((sum, q) => sum + q.w, 0);\n const segStart = prevW / totalW;\n const segEnd = (prevW + quad.w) / totalW;\n\n // Create wrapper div\n const wrapper = document.createElement('div');\n wrapper.style.position = 'absolute';\n wrapper.style.left = `${abs.left}px`;\n wrapper.style.top = `${abs.top}px`;\n wrapper.style.width = `${abs.width}px`;\n wrapper.style.height = `${abs.height}px`;\n wrapper.style.overflow = 'hidden';\n wrapper.style.borderRadius = '2px';\n\n // Create highlight div\n const highlight = document.createElement('div');\n highlight.style.width = '100%';\n highlight.style.height = '100%';\n highlight.style.background = annotation?.style?.color ?? 'rgba(255,230,100,0.35)';\n highlight.style.outline = '1px solid rgba(255,200,0,0.6)';\n highlight.style.transformOrigin = 'left center';\n highlight.style.transform = 'scaleX(0)';\n highlight.style.willChange = 'transform';\n\n // Assemble DOM structure\n wrapper.appendChild(highlight);\n this.layerElement.appendChild(wrapper);\n\n // Store reference for animation\n const key = `${annotation.id}-${idx}`;\n this.elements.set(key, {\n element: highlight,\n wrapper: wrapper,\n annotation: annotation,\n segStart: segStart,\n segEnd: segEnd\n });\n });\n });\n }\n\n /**\n * Updates highlight animations based on current timeline position\n *\n * Starts requestAnimationFrame loop to animate scaleX transform for\n * each highlight element. Calculates progress for each quad segment\n * and updates visibility.\n *\n * @param {number} nowSec - Current timeline position in seconds\n */\n updateTime(nowSec) {\n super.updateTime(nowSec);\n\n // Cancel existing RAF if running\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Start animation loop\n const animate = () => {\n if (this.isDestroyed) {\n return;\n }\n\n // Update each highlight element\n this.elements.forEach(({ element, wrapper, annotation, segStart, segEnd }) => {\n // Hide wrapper if time hasn't reached annotation start\n if (nowSec < annotation.start) {\n wrapper.style.display = 'none';\n } else {\n // Show wrapper\n wrapper.style.display = 'block';\n\n // Calculate global progress (0 to 1)\n const globalProgress = Math.max(\n 0,\n Math.min(\n 1,\n (nowSec - annotation.start) / Math.max(1e-6, annotation.end - annotation.start)\n )\n );\n\n // Calculate local progress for this quad segment (0 to 1)\n const localProgress = Math.max(\n 0,\n Math.min(\n 1,\n (globalProgress - segStart) / Math.max(1e-6, segEnd - segStart)\n )\n );\n\n // Apply scaleX transform\n element.style.transform = `scaleX(${localProgress})`;\n }\n });\n\n // Schedule next frame\n this.rafId = requestAnimationFrame(animate);\n };\n\n animate();\n }\n\n /**\n * Updates the visual state of the layer\n *\n * Not used by HighlightLayer - animation handled in updateTime()\n */\n update() {\n // Not used - updateTime handles animation directly\n }\n\n /**\n * Destroys the layer and releases all resources\n *\n * Cancels animation loop, clears element storage, removes DOM elements,\n * and calls parent cleanup.\n */\n destroy() {\n // Cancel RAF if running\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Clear element storage\n this.elements.clear();\n this.elements = null;\n\n // Remove layer element from DOM\n if (this.layerElement && this.layerElement.parentNode) {\n this.layerElement.parentNode.removeChild(this.layerElement);\n }\n this.layerElement = null;\n\n // Call parent destroy\n super.destroy();\n }\n}\n\nexport default HighlightLayer;\n"],"names":["HighlightLayer","BaseLayer","container","viewport","annotation","totalW","sum","quad","idx","abs","rectNormToAbs","prevW","q","segStart","segEnd","wrapper","highlight","key","nowSec","animate","element","globalProgress","localProgress"],"mappings":"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"}
|
package/dist/index7.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import c from "./index6.js";
|
|
2
|
+
import { rectNormToAbs as p } from "./index10.js";
|
|
3
|
+
class g extends c {
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new HighlightLayer instance
|
|
6
|
+
*
|
|
7
|
+
* @param {HTMLElement} container - Parent DOM element for layer content
|
|
8
|
+
* @param {Object} viewport - Initial viewport dimensions
|
|
9
|
+
* @param {number} viewport.width - Viewport width in pixels
|
|
10
|
+
* @param {number} viewport.height - Viewport height in pixels
|
|
11
|
+
* @param {number} viewport.scale - PDF scale/zoom level
|
|
12
|
+
*/
|
|
13
|
+
constructor(e, r) {
|
|
14
|
+
super(e, r), this.layerElement = document.createElement("div"), this.layerElement.style.position = "absolute", this.layerElement.style.inset = "0", this.layerElement.style.pointerEvents = "none", this.layerElement.style.zIndex = "25", this.container.appendChild(this.layerElement), this.elements = /* @__PURE__ */ new Map(), this.rafId = null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Renders highlight elements for all annotations
|
|
18
|
+
*
|
|
19
|
+
* Creates DOM structure for each quad in each annotation. Calculates
|
|
20
|
+
* timing segments for progressive animation. Clears and recreates all
|
|
21
|
+
* elements when called.
|
|
22
|
+
*/
|
|
23
|
+
render() {
|
|
24
|
+
this.layerElement.innerHTML = "", this.elements.clear(), this.annotations.forEach((e) => {
|
|
25
|
+
if (e.mode !== "quads" || !e.quads?.length)
|
|
26
|
+
return;
|
|
27
|
+
const r = e.quads.reduce((a, i) => a + i.w, 0);
|
|
28
|
+
e.quads.forEach((a, i) => {
|
|
29
|
+
const s = p(a, this.viewport), n = e.quads.slice(0, i).reduce((o, y) => o + y.w, 0), h = n / r, d = (n + a.w) / r, t = document.createElement("div");
|
|
30
|
+
t.style.position = "absolute", t.style.left = `${s.left}px`, t.style.top = `${s.top}px`, t.style.width = `${s.width}px`, t.style.height = `${s.height}px`, t.style.overflow = "hidden", t.style.borderRadius = "2px";
|
|
31
|
+
const l = document.createElement("div");
|
|
32
|
+
l.style.width = "100%", l.style.height = "100%", l.style.background = e?.style?.color ?? "rgba(255,230,100,0.35)", l.style.outline = "1px solid rgba(255,200,0,0.6)", l.style.transformOrigin = "left center", l.style.transform = "scaleX(0)", l.style.willChange = "transform", t.appendChild(l), this.layerElement.appendChild(t);
|
|
33
|
+
const m = `${e.id}-${i}`;
|
|
34
|
+
this.elements.set(m, {
|
|
35
|
+
element: l,
|
|
36
|
+
wrapper: t,
|
|
37
|
+
annotation: e,
|
|
38
|
+
segStart: h,
|
|
39
|
+
segEnd: d
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Updates highlight animations based on current timeline position
|
|
46
|
+
*
|
|
47
|
+
* Starts requestAnimationFrame loop to animate scaleX transform for
|
|
48
|
+
* each highlight element. Calculates progress for each quad segment
|
|
49
|
+
* and updates visibility.
|
|
50
|
+
*
|
|
51
|
+
* @param {number} nowSec - Current timeline position in seconds
|
|
52
|
+
*/
|
|
53
|
+
updateTime(e) {
|
|
54
|
+
super.updateTime(e), this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null);
|
|
55
|
+
const r = () => {
|
|
56
|
+
this.isDestroyed || (this.elements.forEach(({ element: a, wrapper: i, annotation: s, segStart: n, segEnd: h }) => {
|
|
57
|
+
if (e < s.start)
|
|
58
|
+
i.style.display = "none";
|
|
59
|
+
else {
|
|
60
|
+
i.style.display = "block";
|
|
61
|
+
const d = Math.max(
|
|
62
|
+
0,
|
|
63
|
+
Math.min(
|
|
64
|
+
1,
|
|
65
|
+
(e - s.start) / Math.max(1e-6, s.end - s.start)
|
|
66
|
+
)
|
|
67
|
+
), t = Math.max(
|
|
68
|
+
0,
|
|
69
|
+
Math.min(
|
|
70
|
+
1,
|
|
71
|
+
(d - n) / Math.max(1e-6, h - n)
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
a.style.transform = `scaleX(${t})`;
|
|
75
|
+
}
|
|
76
|
+
}), this.rafId = requestAnimationFrame(r));
|
|
77
|
+
};
|
|
78
|
+
r();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Updates the visual state of the layer
|
|
82
|
+
*
|
|
83
|
+
* Not used by HighlightLayer - animation handled in updateTime()
|
|
84
|
+
*/
|
|
85
|
+
update() {
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Destroys the layer and releases all resources
|
|
89
|
+
*
|
|
90
|
+
* Cancels animation loop, clears element storage, removes DOM elements,
|
|
91
|
+
* and calls parent cleanup.
|
|
92
|
+
*/
|
|
93
|
+
destroy() {
|
|
94
|
+
this.rafId && (cancelAnimationFrame(this.rafId), this.rafId = null), this.elements.clear(), this.elements = null, this.layerElement && this.layerElement.parentNode && this.layerElement.parentNode.removeChild(this.layerElement), this.layerElement = null, super.destroy();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
g as default
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=index7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index7.js","sources":["../src/layers/HighlightLayer.js"],"sourcesContent":["import BaseLayer from './BaseLayer.js';\nimport { rectNormToAbs } from '../utils/coordinateUtils.js';\n\n/**\n * HighlightLayer - Renders highlight annotations with progressive reveal\n *\n * Extends BaseLayer to render rectangular highlight regions (quads) with\n * progressive left-to-right scaleX animation based on timeline position.\n * Supports multi-line highlights with per-quad timing segments.\n *\n * @extends BaseLayer\n */\nclass HighlightLayer extends BaseLayer {\n /**\n * Creates a new HighlightLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n */\n constructor(container, viewport) {\n super(container, viewport);\n\n // Create layer container element\n this.layerElement = document.createElement('div');\n this.layerElement.style.position = 'absolute';\n this.layerElement.style.inset = '0';\n this.layerElement.style.pointerEvents = 'none';\n this.layerElement.style.zIndex = '25';\n\n this.container.appendChild(this.layerElement);\n\n // Initialize element storage\n this.elements = new Map();\n\n // Initialize RAF ID\n this.rafId = null;\n }\n\n /**\n * Renders highlight elements for all annotations\n *\n * Creates DOM structure for each quad in each annotation. Calculates\n * timing segments for progressive animation. Clears and recreates all\n * elements when called.\n */\n render() {\n // Clear existing elements\n this.layerElement.innerHTML = '';\n this.elements.clear();\n\n // Process each annotation\n this.annotations.forEach((annotation) => {\n // Skip if not quad mode or no quads\n if (annotation.mode !== 'quads' || !annotation.quads?.length) {\n return;\n }\n\n // Calculate total width across all quads\n const totalW = annotation.quads.reduce((sum, quad) => sum + quad.w, 0);\n\n // Process each quad\n annotation.quads.forEach((quad, idx) => {\n // Convert normalized coordinates to absolute pixels\n const abs = rectNormToAbs(quad, this.viewport);\n\n // Calculate timing segment for this quad\n const prevW = annotation.quads.slice(0, idx).reduce((sum, q) => sum + q.w, 0);\n const segStart = prevW / totalW;\n const segEnd = (prevW + quad.w) / totalW;\n\n // Create wrapper div\n const wrapper = document.createElement('div');\n wrapper.style.position = 'absolute';\n wrapper.style.left = `${abs.left}px`;\n wrapper.style.top = `${abs.top}px`;\n wrapper.style.width = `${abs.width}px`;\n wrapper.style.height = `${abs.height}px`;\n wrapper.style.overflow = 'hidden';\n wrapper.style.borderRadius = '2px';\n\n // Create highlight div\n const highlight = document.createElement('div');\n highlight.style.width = '100%';\n highlight.style.height = '100%';\n highlight.style.background = annotation?.style?.color ?? 'rgba(255,230,100,0.35)';\n highlight.style.outline = '1px solid rgba(255,200,0,0.6)';\n highlight.style.transformOrigin = 'left center';\n highlight.style.transform = 'scaleX(0)';\n highlight.style.willChange = 'transform';\n\n // Assemble DOM structure\n wrapper.appendChild(highlight);\n this.layerElement.appendChild(wrapper);\n\n // Store reference for animation\n const key = `${annotation.id}-${idx}`;\n this.elements.set(key, {\n element: highlight,\n wrapper: wrapper,\n annotation: annotation,\n segStart: segStart,\n segEnd: segEnd\n });\n });\n });\n }\n\n /**\n * Updates highlight animations based on current timeline position\n *\n * Starts requestAnimationFrame loop to animate scaleX transform for\n * each highlight element. Calculates progress for each quad segment\n * and updates visibility.\n *\n * @param {number} nowSec - Current timeline position in seconds\n */\n updateTime(nowSec) {\n super.updateTime(nowSec);\n\n // Cancel existing RAF if running\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Start animation loop\n const animate = () => {\n if (this.isDestroyed) {\n return;\n }\n\n // Update each highlight element\n this.elements.forEach(({ element, wrapper, annotation, segStart, segEnd }) => {\n // Hide wrapper if time hasn't reached annotation start\n if (nowSec < annotation.start) {\n wrapper.style.display = 'none';\n } else {\n // Show wrapper\n wrapper.style.display = 'block';\n\n // Calculate global progress (0 to 1)\n const globalProgress = Math.max(\n 0,\n Math.min(\n 1,\n (nowSec - annotation.start) / Math.max(1e-6, annotation.end - annotation.start)\n )\n );\n\n // Calculate local progress for this quad segment (0 to 1)\n const localProgress = Math.max(\n 0,\n Math.min(\n 1,\n (globalProgress - segStart) / Math.max(1e-6, segEnd - segStart)\n )\n );\n\n // Apply scaleX transform\n element.style.transform = `scaleX(${localProgress})`;\n }\n });\n\n // Schedule next frame\n this.rafId = requestAnimationFrame(animate);\n };\n\n animate();\n }\n\n /**\n * Updates the visual state of the layer\n *\n * Not used by HighlightLayer - animation handled in updateTime()\n */\n update() {\n // Not used - updateTime handles animation directly\n }\n\n /**\n * Destroys the layer and releases all resources\n *\n * Cancels animation loop, clears element storage, removes DOM elements,\n * and calls parent cleanup.\n */\n destroy() {\n // Cancel RAF if running\n if (this.rafId) {\n cancelAnimationFrame(this.rafId);\n this.rafId = null;\n }\n\n // Clear element storage\n this.elements.clear();\n this.elements = null;\n\n // Remove layer element from DOM\n if (this.layerElement && this.layerElement.parentNode) {\n this.layerElement.parentNode.removeChild(this.layerElement);\n }\n this.layerElement = null;\n\n // Call parent destroy\n super.destroy();\n }\n}\n\nexport default HighlightLayer;\n"],"names":["HighlightLayer","BaseLayer","container","viewport","annotation","totalW","sum","quad","idx","abs","rectNormToAbs","prevW","q","segStart","segEnd","wrapper","highlight","key","nowSec","animate","element","globalProgress","localProgress"],"mappings":"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"}
|
package/dist/index8.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const h=require("./index6.cjs"),p=require("./index10.cjs");class u extends h.default{constructor(t,s){super(t,s),this.layerElement=document.createElement("div"),this.layerElement.style.position="absolute",this.layerElement.style.inset="0",this.layerElement.style.pointerEvents="none",this.layerElement.style.zIndex="30",this.container.appendChild(this.layerElement),this.textElements=new Map}render(){this.layerElement.innerHTML="",this.textElements.clear(),this.annotations.forEach(t=>{const s=p.rectNormToAbs(t,this.viewport),e=document.createElement("div");e.style.position="absolute",e.style.left=`${s.left}px`,e.style.top=`${s.top}px`,e.style.width=`${s.width}px`,e.style.height=`${s.height}px`,e.style.backgroundColor=t.style?.bg||"rgba(255,255,255,0.9)",e.style.borderRadius="4px",e.style.padding="8px",e.style.fontSize="14px",e.style.lineHeight="1.4",e.style.color=t.style?.color||"#1f2937",e.style.fontFamily="system-ui, -apple-system, sans-serif",e.style.display="flex",e.style.alignItems="center",e.style.justifyContent="flex-start",e.style.overflow="hidden",e.style.wordWrap="break-word",e.style.display="none",this.layerElement.appendChild(e),this.textElements.set(t.id,{element:e,annotation:t})})}updateTime(t){super.updateTime(t),this.textElements.forEach(({element:s,annotation:e})=>{if(t<e.start)s.style.display="none";else{s.style.display="flex";const l=this._getVisibleText(e.content,e.start,e.end,t);s.textContent=l}})}_getVisibleText(t,s,e,l){if(l<s)return"";if(l>=e)return t;const n=(l-s)/(e-s),r=t.split(" "),i=Math.floor(n*r.length);if(i===0)return"";const o=r.slice(0,i);if(i<r.length){const d=n*r.length-i,y=r[i],a=Math.floor(d*y.length);a>0&&o.push(y.slice(0,a))}return o.join(" ")}update(){}destroy(){this.textElements.clear(),this.textElements=null,this.layerElement&&this.layerElement.parentNode&&this.layerElement.parentNode.removeChild(this.layerElement),this.layerElement=null,super.destroy()}}exports.default=u;
|
|
2
|
+
//# sourceMappingURL=index8.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index8.cjs","sources":["../src/layers/TextLayer.js"],"sourcesContent":["import BaseLayer from \"./BaseLayer.js\";\nimport { rectNormToAbs } from \"../utils/coordinateUtils.js\";\n\n/**\n * TextLayer - Renders text annotations with progressive reveal\n *\n * Extends BaseLayer to render text box annotations with progressive\n * word-by-word text reveal animation (typing effect). Text appears\n * immediately and types in character by character, simulating real-time typing.\n *\n * @extends BaseLayer\n */\nclass TextLayer extends BaseLayer {\n /**\n * Creates a new TextLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n */\n constructor(container, viewport) {\n // Call parent constructor first\n super(container, viewport);\n\n // Create layer container element\n this.layerElement = document.createElement(\"div\");\n this.layerElement.style.position = \"absolute\";\n this.layerElement.style.inset = \"0\";\n this.layerElement.style.pointerEvents = \"none\";\n this.layerElement.style.zIndex = \"30\";\n\n // Append to parent container\n this.container.appendChild(this.layerElement);\n\n // Initialize element storage (Map of id -> element)\n this.textElements = new Map();\n }\n\n /**\n * Renders text box elements for all annotations\n *\n * Creates DOM structure for each text annotation. Creates text box divs\n * with proper positioning and styling. Initially hidden (visibility\n * controlled by updateTime).\n *\n * This method is idempotent - safe to call multiple times.\n */\n render() {\n // Clear existing elements\n this.layerElement.innerHTML = \"\";\n this.textElements.clear();\n\n // Process each annotation\n this.annotations.forEach((annotation) => {\n // Convert normalized coordinates to absolute pixels\n const abs = rectNormToAbs(annotation, this.viewport);\n\n // Create text box div\n const textBox = document.createElement(\"div\");\n\n // Set positioning\n textBox.style.position = \"absolute\";\n textBox.style.left = `${abs.left}px`;\n textBox.style.top = `${abs.top}px`;\n textBox.style.width = `${abs.width}px`;\n textBox.style.height = `${abs.height}px`;\n\n // Set background and border\n textBox.style.backgroundColor =\n annotation.style?.bg || \"rgba(255,255,255,0.9)\";\n textBox.style.borderRadius = \"4px\";\n textBox.style.padding = \"8px\";\n\n // Set text styling\n textBox.style.fontSize = \"14px\";\n textBox.style.lineHeight = \"1.4\";\n textBox.style.color = annotation.style?.color || \"#1f2937\";\n textBox.style.fontFamily = \"system-ui, -apple-system, sans-serif\";\n\n // Set layout\n textBox.style.display = \"flex\";\n textBox.style.alignItems = \"center\";\n textBox.style.justifyContent = \"flex-start\";\n textBox.style.overflow = \"hidden\";\n textBox.style.wordWrap = \"break-word\";\n\n // Initially hidden (will be shown/updated in updateTime)\n textBox.style.display = \"none\";\n\n // Append to layer\n this.layerElement.appendChild(textBox);\n\n // Store reference\n this.textElements.set(annotation.id, {\n element: textBox,\n annotation: annotation,\n });\n });\n }\n\n /**\n * Updates text box visibility and content based on timeline position\n *\n * Shows/hides text boxes based on start time. Calculates visible text\n * for progressive reveal (typing effect). Text appears immediately when\n * start time is reached and types in progressively.\n *\n * @param {number} nowSec - Current timeline position in seconds\n */\n updateTime(nowSec) {\n // Call parent implementation\n super.updateTime(nowSec);\n\n // Update each text element\n this.textElements.forEach(({ element, annotation }) => {\n // Check if annotation should be visible\n if (nowSec < annotation.start) {\n // Before start time - hide\n element.style.display = \"none\";\n } else {\n // After start time - show\n element.style.display = \"flex\";\n\n // Calculate visible text\n const visibleText = this._getVisibleText(\n annotation.content,\n annotation.start,\n annotation.end,\n nowSec\n );\n\n // Update text content (typing effect)\n element.textContent = visibleText;\n }\n });\n }\n\n /**\n * Calculates visible text based on progress between start and end times\n *\n * Implements word-by-word reveal with partial character reveal for\n * the current word being typed.\n *\n * @private\n * @param {string} content - Full text content\n * @param {number} start - Start time in seconds\n * @param {number} end - End time in seconds\n * @param {number} currentTime - Current timeline position in seconds\n * @returns {string} Visible portion of text\n */\n _getVisibleText(content, start, end, currentTime) {\n // Before start - no text visible\n if (currentTime < start) {\n return \"\";\n }\n\n // After end - full text visible\n if (currentTime >= end) {\n return content;\n }\n\n // During animation - calculate progress\n const progress = (currentTime - start) / (end - start);\n\n // Split into words\n const words = content.split(\" \");\n\n // Calculate visible word count\n const visibleWordCount = Math.floor(progress * words.length);\n\n // No words visible yet\n if (visibleWordCount === 0) {\n return \"\";\n }\n\n // Get complete visible words\n const visibleWords = words.slice(0, visibleWordCount);\n\n // Add partial of the next word if not at the end\n if (visibleWordCount < words.length) {\n // Calculate progress within current word\n const currentWordProgress = progress * words.length - visibleWordCount;\n\n // Get current word being typed\n const currentWord = words[visibleWordCount];\n\n // Calculate visible character count\n const visibleCharCount = Math.floor(\n currentWordProgress * currentWord.length\n );\n\n // Add partial word if any characters are visible\n if (visibleCharCount > 0) {\n visibleWords.push(currentWord.slice(0, visibleCharCount));\n }\n }\n\n // Join words with spaces\n return visibleWords.join(\" \");\n }\n\n /**\n * Updates the visual state of the layer\n *\n * Not used by TextLayer - updateTime handles all updates directly.\n * Implemented to satisfy BaseLayer contract.\n */\n update() {\n // Not used - updateTime handles updates directly\n }\n\n /**\n * Destroys the layer and releases all resources\n *\n * Clears element storage, removes DOM elements, and calls parent cleanup.\n */\n destroy() {\n // Clear element storage\n this.textElements.clear();\n this.textElements = null;\n\n // Remove layer element from DOM\n if (this.layerElement && this.layerElement.parentNode) {\n this.layerElement.parentNode.removeChild(this.layerElement);\n }\n this.layerElement = null;\n\n // Call parent destroy\n super.destroy();\n }\n}\n\nexport default TextLayer;\n"],"names":["TextLayer","BaseLayer","container","viewport","annotation","abs","rectNormToAbs","textBox","nowSec","element","visibleText","content","start","end","currentTime","progress","words","visibleWordCount","visibleWords","currentWordProgress","currentWord","visibleCharCount"],"mappings":"uKAYA,MAAMA,UAAkBC,EAAAA,OAAU,CAUhC,YAAYC,EAAWC,EAAU,CAE/B,MAAMD,EAAWC,CAAQ,EAGzB,KAAK,aAAe,SAAS,cAAc,KAAK,EAChD,KAAK,aAAa,MAAM,SAAW,WACnC,KAAK,aAAa,MAAM,MAAQ,IAChC,KAAK,aAAa,MAAM,cAAgB,OACxC,KAAK,aAAa,MAAM,OAAS,KAGjC,KAAK,UAAU,YAAY,KAAK,YAAY,EAG5C,KAAK,aAAe,IAAI,GAC1B,CAWA,QAAS,CAEP,KAAK,aAAa,UAAY,GAC9B,KAAK,aAAa,MAAK,EAGvB,KAAK,YAAY,QAASC,GAAe,CAEvC,MAAMC,EAAMC,EAAAA,cAAcF,EAAY,KAAK,QAAQ,EAG7CG,EAAU,SAAS,cAAc,KAAK,EAG5CA,EAAQ,MAAM,SAAW,WACzBA,EAAQ,MAAM,KAAO,GAAGF,EAAI,IAAI,KAChCE,EAAQ,MAAM,IAAM,GAAGF,EAAI,GAAG,KAC9BE,EAAQ,MAAM,MAAQ,GAAGF,EAAI,KAAK,KAClCE,EAAQ,MAAM,OAAS,GAAGF,EAAI,MAAM,KAGpCE,EAAQ,MAAM,gBACZH,EAAW,OAAO,IAAM,wBAC1BG,EAAQ,MAAM,aAAe,MAC7BA,EAAQ,MAAM,QAAU,MAGxBA,EAAQ,MAAM,SAAW,OACzBA,EAAQ,MAAM,WAAa,MAC3BA,EAAQ,MAAM,MAAQH,EAAW,OAAO,OAAS,UACjDG,EAAQ,MAAM,WAAa,uCAG3BA,EAAQ,MAAM,QAAU,OACxBA,EAAQ,MAAM,WAAa,SAC3BA,EAAQ,MAAM,eAAiB,aAC/BA,EAAQ,MAAM,SAAW,SACzBA,EAAQ,MAAM,SAAW,aAGzBA,EAAQ,MAAM,QAAU,OAGxB,KAAK,aAAa,YAAYA,CAAO,EAGrC,KAAK,aAAa,IAAIH,EAAW,GAAI,CACnC,QAASG,EACT,WAAYH,CACpB,CAAO,CACH,CAAC,CACH,CAWA,WAAWI,EAAQ,CAEjB,MAAM,WAAWA,CAAM,EAGvB,KAAK,aAAa,QAAQ,CAAC,CAAE,QAAAC,EAAS,WAAAL,CAAU,IAAO,CAErD,GAAII,EAASJ,EAAW,MAEtBK,EAAQ,MAAM,QAAU,WACnB,CAELA,EAAQ,MAAM,QAAU,OAGxB,MAAMC,EAAc,KAAK,gBACvBN,EAAW,QACXA,EAAW,MACXA,EAAW,IACXI,CACV,EAGQC,EAAQ,YAAcC,CACxB,CACF,CAAC,CACH,CAeA,gBAAgBC,EAASC,EAAOC,EAAKC,EAAa,CAEhD,GAAIA,EAAcF,EAChB,MAAO,GAIT,GAAIE,GAAeD,EACjB,OAAOF,EAIT,MAAMI,GAAYD,EAAcF,IAAUC,EAAMD,GAG1CI,EAAQL,EAAQ,MAAM,GAAG,EAGzBM,EAAmB,KAAK,MAAMF,EAAWC,EAAM,MAAM,EAG3D,GAAIC,IAAqB,EACvB,MAAO,GAIT,MAAMC,EAAeF,EAAM,MAAM,EAAGC,CAAgB,EAGpD,GAAIA,EAAmBD,EAAM,OAAQ,CAEnC,MAAMG,EAAsBJ,EAAWC,EAAM,OAASC,EAGhDG,EAAcJ,EAAMC,CAAgB,EAGpCI,EAAmB,KAAK,MAC5BF,EAAsBC,EAAY,MAC1C,EAGUC,EAAmB,GACrBH,EAAa,KAAKE,EAAY,MAAM,EAAGC,CAAgB,CAAC,CAE5D,CAGA,OAAOH,EAAa,KAAK,GAAG,CAC9B,CAQA,QAAS,CAET,CAOA,SAAU,CAER,KAAK,aAAa,MAAK,EACvB,KAAK,aAAe,KAGhB,KAAK,cAAgB,KAAK,aAAa,YACzC,KAAK,aAAa,WAAW,YAAY,KAAK,YAAY,EAE5D,KAAK,aAAe,KAGpB,MAAM,QAAO,CACf,CACF"}
|
package/dist/index8.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import d from "./index6.js";
|
|
2
|
+
import { rectNormToAbs as p } from "./index10.js";
|
|
3
|
+
class f extends d {
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new TextLayer instance
|
|
6
|
+
*
|
|
7
|
+
* @param {HTMLElement} container - Parent DOM element for layer content
|
|
8
|
+
* @param {Object} viewport - Initial viewport dimensions
|
|
9
|
+
* @param {number} viewport.width - Viewport width in pixels
|
|
10
|
+
* @param {number} viewport.height - Viewport height in pixels
|
|
11
|
+
* @param {number} viewport.scale - PDF scale/zoom level
|
|
12
|
+
*/
|
|
13
|
+
constructor(t, s) {
|
|
14
|
+
super(t, s), this.layerElement = document.createElement("div"), this.layerElement.style.position = "absolute", this.layerElement.style.inset = "0", this.layerElement.style.pointerEvents = "none", this.layerElement.style.zIndex = "30", this.container.appendChild(this.layerElement), this.textElements = /* @__PURE__ */ new Map();
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Renders text box elements for all annotations
|
|
18
|
+
*
|
|
19
|
+
* Creates DOM structure for each text annotation. Creates text box divs
|
|
20
|
+
* with proper positioning and styling. Initially hidden (visibility
|
|
21
|
+
* controlled by updateTime).
|
|
22
|
+
*
|
|
23
|
+
* This method is idempotent - safe to call multiple times.
|
|
24
|
+
*/
|
|
25
|
+
render() {
|
|
26
|
+
this.layerElement.innerHTML = "", this.textElements.clear(), this.annotations.forEach((t) => {
|
|
27
|
+
const s = p(t, this.viewport), e = document.createElement("div");
|
|
28
|
+
e.style.position = "absolute", e.style.left = `${s.left}px`, e.style.top = `${s.top}px`, e.style.width = `${s.width}px`, e.style.height = `${s.height}px`, e.style.backgroundColor = t.style?.bg || "rgba(255,255,255,0.9)", e.style.borderRadius = "4px", e.style.padding = "8px", e.style.fontSize = "14px", e.style.lineHeight = "1.4", e.style.color = t.style?.color || "#1f2937", e.style.fontFamily = "system-ui, -apple-system, sans-serif", e.style.display = "flex", e.style.alignItems = "center", e.style.justifyContent = "flex-start", e.style.overflow = "hidden", e.style.wordWrap = "break-word", e.style.display = "none", this.layerElement.appendChild(e), this.textElements.set(t.id, {
|
|
29
|
+
element: e,
|
|
30
|
+
annotation: t
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Updates text box visibility and content based on timeline position
|
|
36
|
+
*
|
|
37
|
+
* Shows/hides text boxes based on start time. Calculates visible text
|
|
38
|
+
* for progressive reveal (typing effect). Text appears immediately when
|
|
39
|
+
* start time is reached and types in progressively.
|
|
40
|
+
*
|
|
41
|
+
* @param {number} nowSec - Current timeline position in seconds
|
|
42
|
+
*/
|
|
43
|
+
updateTime(t) {
|
|
44
|
+
super.updateTime(t), this.textElements.forEach(({ element: s, annotation: e }) => {
|
|
45
|
+
if (t < e.start)
|
|
46
|
+
s.style.display = "none";
|
|
47
|
+
else {
|
|
48
|
+
s.style.display = "flex";
|
|
49
|
+
const l = this._getVisibleText(
|
|
50
|
+
e.content,
|
|
51
|
+
e.start,
|
|
52
|
+
e.end,
|
|
53
|
+
t
|
|
54
|
+
);
|
|
55
|
+
s.textContent = l;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Calculates visible text based on progress between start and end times
|
|
61
|
+
*
|
|
62
|
+
* Implements word-by-word reveal with partial character reveal for
|
|
63
|
+
* the current word being typed.
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
* @param {string} content - Full text content
|
|
67
|
+
* @param {number} start - Start time in seconds
|
|
68
|
+
* @param {number} end - End time in seconds
|
|
69
|
+
* @param {number} currentTime - Current timeline position in seconds
|
|
70
|
+
* @returns {string} Visible portion of text
|
|
71
|
+
*/
|
|
72
|
+
_getVisibleText(t, s, e, l) {
|
|
73
|
+
if (l < s)
|
|
74
|
+
return "";
|
|
75
|
+
if (l >= e)
|
|
76
|
+
return t;
|
|
77
|
+
const n = (l - s) / (e - s), r = t.split(" "), i = Math.floor(n * r.length);
|
|
78
|
+
if (i === 0)
|
|
79
|
+
return "";
|
|
80
|
+
const o = r.slice(0, i);
|
|
81
|
+
if (i < r.length) {
|
|
82
|
+
const h = n * r.length - i, y = r[i], a = Math.floor(
|
|
83
|
+
h * y.length
|
|
84
|
+
);
|
|
85
|
+
a > 0 && o.push(y.slice(0, a));
|
|
86
|
+
}
|
|
87
|
+
return o.join(" ");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Updates the visual state of the layer
|
|
91
|
+
*
|
|
92
|
+
* Not used by TextLayer - updateTime handles all updates directly.
|
|
93
|
+
* Implemented to satisfy BaseLayer contract.
|
|
94
|
+
*/
|
|
95
|
+
update() {
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Destroys the layer and releases all resources
|
|
99
|
+
*
|
|
100
|
+
* Clears element storage, removes DOM elements, and calls parent cleanup.
|
|
101
|
+
*/
|
|
102
|
+
destroy() {
|
|
103
|
+
this.textElements.clear(), this.textElements = null, this.layerElement && this.layerElement.parentNode && this.layerElement.parentNode.removeChild(this.layerElement), this.layerElement = null, super.destroy();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
f as default
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=index8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index8.js","sources":["../src/layers/TextLayer.js"],"sourcesContent":["import BaseLayer from \"./BaseLayer.js\";\nimport { rectNormToAbs } from \"../utils/coordinateUtils.js\";\n\n/**\n * TextLayer - Renders text annotations with progressive reveal\n *\n * Extends BaseLayer to render text box annotations with progressive\n * word-by-word text reveal animation (typing effect). Text appears\n * immediately and types in character by character, simulating real-time typing.\n *\n * @extends BaseLayer\n */\nclass TextLayer extends BaseLayer {\n /**\n * Creates a new TextLayer instance\n *\n * @param {HTMLElement} container - Parent DOM element for layer content\n * @param {Object} viewport - Initial viewport dimensions\n * @param {number} viewport.width - Viewport width in pixels\n * @param {number} viewport.height - Viewport height in pixels\n * @param {number} viewport.scale - PDF scale/zoom level\n */\n constructor(container, viewport) {\n // Call parent constructor first\n super(container, viewport);\n\n // Create layer container element\n this.layerElement = document.createElement(\"div\");\n this.layerElement.style.position = \"absolute\";\n this.layerElement.style.inset = \"0\";\n this.layerElement.style.pointerEvents = \"none\";\n this.layerElement.style.zIndex = \"30\";\n\n // Append to parent container\n this.container.appendChild(this.layerElement);\n\n // Initialize element storage (Map of id -> element)\n this.textElements = new Map();\n }\n\n /**\n * Renders text box elements for all annotations\n *\n * Creates DOM structure for each text annotation. Creates text box divs\n * with proper positioning and styling. Initially hidden (visibility\n * controlled by updateTime).\n *\n * This method is idempotent - safe to call multiple times.\n */\n render() {\n // Clear existing elements\n this.layerElement.innerHTML = \"\";\n this.textElements.clear();\n\n // Process each annotation\n this.annotations.forEach((annotation) => {\n // Convert normalized coordinates to absolute pixels\n const abs = rectNormToAbs(annotation, this.viewport);\n\n // Create text box div\n const textBox = document.createElement(\"div\");\n\n // Set positioning\n textBox.style.position = \"absolute\";\n textBox.style.left = `${abs.left}px`;\n textBox.style.top = `${abs.top}px`;\n textBox.style.width = `${abs.width}px`;\n textBox.style.height = `${abs.height}px`;\n\n // Set background and border\n textBox.style.backgroundColor =\n annotation.style?.bg || \"rgba(255,255,255,0.9)\";\n textBox.style.borderRadius = \"4px\";\n textBox.style.padding = \"8px\";\n\n // Set text styling\n textBox.style.fontSize = \"14px\";\n textBox.style.lineHeight = \"1.4\";\n textBox.style.color = annotation.style?.color || \"#1f2937\";\n textBox.style.fontFamily = \"system-ui, -apple-system, sans-serif\";\n\n // Set layout\n textBox.style.display = \"flex\";\n textBox.style.alignItems = \"center\";\n textBox.style.justifyContent = \"flex-start\";\n textBox.style.overflow = \"hidden\";\n textBox.style.wordWrap = \"break-word\";\n\n // Initially hidden (will be shown/updated in updateTime)\n textBox.style.display = \"none\";\n\n // Append to layer\n this.layerElement.appendChild(textBox);\n\n // Store reference\n this.textElements.set(annotation.id, {\n element: textBox,\n annotation: annotation,\n });\n });\n }\n\n /**\n * Updates text box visibility and content based on timeline position\n *\n * Shows/hides text boxes based on start time. Calculates visible text\n * for progressive reveal (typing effect). Text appears immediately when\n * start time is reached and types in progressively.\n *\n * @param {number} nowSec - Current timeline position in seconds\n */\n updateTime(nowSec) {\n // Call parent implementation\n super.updateTime(nowSec);\n\n // Update each text element\n this.textElements.forEach(({ element, annotation }) => {\n // Check if annotation should be visible\n if (nowSec < annotation.start) {\n // Before start time - hide\n element.style.display = \"none\";\n } else {\n // After start time - show\n element.style.display = \"flex\";\n\n // Calculate visible text\n const visibleText = this._getVisibleText(\n annotation.content,\n annotation.start,\n annotation.end,\n nowSec\n );\n\n // Update text content (typing effect)\n element.textContent = visibleText;\n }\n });\n }\n\n /**\n * Calculates visible text based on progress between start and end times\n *\n * Implements word-by-word reveal with partial character reveal for\n * the current word being typed.\n *\n * @private\n * @param {string} content - Full text content\n * @param {number} start - Start time in seconds\n * @param {number} end - End time in seconds\n * @param {number} currentTime - Current timeline position in seconds\n * @returns {string} Visible portion of text\n */\n _getVisibleText(content, start, end, currentTime) {\n // Before start - no text visible\n if (currentTime < start) {\n return \"\";\n }\n\n // After end - full text visible\n if (currentTime >= end) {\n return content;\n }\n\n // During animation - calculate progress\n const progress = (currentTime - start) / (end - start);\n\n // Split into words\n const words = content.split(\" \");\n\n // Calculate visible word count\n const visibleWordCount = Math.floor(progress * words.length);\n\n // No words visible yet\n if (visibleWordCount === 0) {\n return \"\";\n }\n\n // Get complete visible words\n const visibleWords = words.slice(0, visibleWordCount);\n\n // Add partial of the next word if not at the end\n if (visibleWordCount < words.length) {\n // Calculate progress within current word\n const currentWordProgress = progress * words.length - visibleWordCount;\n\n // Get current word being typed\n const currentWord = words[visibleWordCount];\n\n // Calculate visible character count\n const visibleCharCount = Math.floor(\n currentWordProgress * currentWord.length\n );\n\n // Add partial word if any characters are visible\n if (visibleCharCount > 0) {\n visibleWords.push(currentWord.slice(0, visibleCharCount));\n }\n }\n\n // Join words with spaces\n return visibleWords.join(\" \");\n }\n\n /**\n * Updates the visual state of the layer\n *\n * Not used by TextLayer - updateTime handles all updates directly.\n * Implemented to satisfy BaseLayer contract.\n */\n update() {\n // Not used - updateTime handles updates directly\n }\n\n /**\n * Destroys the layer and releases all resources\n *\n * Clears element storage, removes DOM elements, and calls parent cleanup.\n */\n destroy() {\n // Clear element storage\n this.textElements.clear();\n this.textElements = null;\n\n // Remove layer element from DOM\n if (this.layerElement && this.layerElement.parentNode) {\n this.layerElement.parentNode.removeChild(this.layerElement);\n }\n this.layerElement = null;\n\n // Call parent destroy\n super.destroy();\n }\n}\n\nexport default TextLayer;\n"],"names":["TextLayer","BaseLayer","container","viewport","annotation","abs","rectNormToAbs","textBox","nowSec","element","visibleText","content","start","end","currentTime","progress","words","visibleWordCount","visibleWords","currentWordProgress","currentWord","visibleCharCount"],"mappings":"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"}
|
package/dist/index9.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const c=require("./index6.cjs");class d extends c.default{constructor(t,s){super(t,s),this.canvasElement=document.createElement("canvas"),this.canvasElement.style.position="absolute",this.canvasElement.style.inset="0",this.canvasElement.style.pointerEvents="none",this.canvasElement.style.zIndex="40",this.container.appendChild(this.canvasElement),this.ctx=this.canvasElement.getContext("2d"),this.rafId=null,this._setupCanvas()}_setupCanvas(){const t=window.devicePixelRatio||1;this.canvasElement.width=Math.round(this.viewport.width*t),this.canvasElement.height=Math.round(this.viewport.height*t),this.canvasElement.style.width=`${this.viewport.width}px`,this.canvasElement.style.height=`${this.viewport.height}px`,this.ctx.setTransform(t,0,0,t,0,0)}setViewport(t){super.setViewport(t),this._setupCanvas()}updateTime(t){super.updateTime(t),this.rafId&&cancelAnimationFrame(this.rafId);const s=()=>{if(!this.isDestroyed){this.ctx.clearRect(0,0,this.canvasElement.width,this.canvasElement.height);for(const e of this.annotations){if(t<e.start)continue;const o=e.end-e.start,l=Math.min(t-e.start,o);for(const i of e.strokes||[]){this.ctx.lineCap="round",this.ctx.lineJoin="round",this.ctx.strokeStyle=i.color||"#1f2937",this.ctx.lineWidth=i.size||3,this.ctx.beginPath();let n=!1;for(const a of i.points){if(a.t>l)break;const h=a.x*this.viewport.width,r=a.y*this.viewport.height;n?this.ctx.lineTo(h,r):(this.ctx.moveTo(h,r),n=!0)}n&&this.ctx.stroke()}}this.rafId=requestAnimationFrame(s)}};s()}render(){}update(){}destroy(){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=null),this.ctx=null,this.canvasElement&&this.canvasElement.parentNode&&this.canvasElement.parentNode.removeChild(this.canvasElement),this.canvasElement=null,super.destroy()}}exports.default=d;
|
|
2
|
+
//# sourceMappingURL=index9.cjs.map
|