@ceeblue/web-utils 7.0.1 → 7.2.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/README.md +8 -5
- package/dist/web-utils.d.ts +18 -8
- package/dist/web-utils.js +55 -23
- package/dist/web-utils.js.map +1 -1
- package/dist/web-utils.min.js +1 -1
- package/dist/web-utils.min.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Then [import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modu
|
|
|
14
14
|
```javascript
|
|
15
15
|
import { Util, ILog } from '@ceeblue/web-utils';
|
|
16
16
|
```
|
|
17
|
-
>
|
|
17
|
+
> [!IMPORTANT]
|
|
18
18
|
>
|
|
19
19
|
> If your project uses TypeScript, it is recommended that you set target: "ES6" in your configuration to match our use of ES6 features and ensure that your build will succeed (for those requiring a backward-compatible UMD version, a local build is recommended).
|
|
20
20
|
> Then define the "moduleResolution" compiler option: "Node" in tsconfig.json helps with import failures by ensuring that TypeScript uses the correct import resolution strategy based on the targeted Node.js version.
|
|
@@ -27,13 +27,11 @@ import { Util, ILog } from '@ceeblue/web-utils';
|
|
|
27
27
|
> }
|
|
28
28
|
> ```
|
|
29
29
|
|
|
30
|
-
>
|
|
30
|
+
> [!TIP]
|
|
31
31
|
>
|
|
32
32
|
> To debug production code without modifying it, the library can use special query parameter of the main page's URL:
|
|
33
33
|
> - __!cb-override-log-level__ : allows to override the log level for the entire library, see [Log.ts](./src/Log.ts) for details on handling log levels.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
35
|
## Building locally
|
|
38
36
|
|
|
39
37
|
1. [Clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) this repository
|
|
@@ -58,7 +56,12 @@ This monorepo also contains built-in documentation about the APIs in the library
|
|
|
58
56
|
```
|
|
59
57
|
npm run build:docs
|
|
60
58
|
```
|
|
61
|
-
|
|
59
|
+
|
|
60
|
+
Once generated, open the `index.html` file located in the `docs` folder (`./docs/index.html`) with your browser.
|
|
61
|
+
|
|
62
|
+
> [!NOTE]
|
|
63
|
+
>
|
|
64
|
+
> An online, continuously maintained version of the latest released documentation is available at https://ceebluetv.github.io/web-utils/
|
|
62
65
|
|
|
63
66
|
## Contribution
|
|
64
67
|
|
package/dist/web-utils.d.ts
CHANGED
|
@@ -467,7 +467,7 @@ declare class EventEmitter extends Loggable {
|
|
|
467
467
|
* Event subscription only one time, once time fired it's automatically unsubscribe
|
|
468
468
|
* @param name Name of event without the `on` prefix (ex: `log` to `onLog` event declared)
|
|
469
469
|
* @param event Subscriber Function
|
|
470
|
-
* @param options.
|
|
470
|
+
* @param options.signal Optional `AbortSignal` to stop this or multiple subscriptions in same time
|
|
471
471
|
*/
|
|
472
472
|
once(name: EventKeys<this>, event: Function, options?: {
|
|
473
473
|
signal?: AbortSignal;
|
|
@@ -950,8 +950,20 @@ declare function trimStart(value: string, chars?: string): string;
|
|
|
950
950
|
* @returns string trimmed
|
|
951
951
|
*/
|
|
952
952
|
declare function trimEnd(value: string, chars?: string): string;
|
|
953
|
+
/**
|
|
954
|
+
* Wraps an object with a Proxy that makes property access case-insensitive.
|
|
955
|
+
*
|
|
956
|
+
* Property lookup (e.g. `obj.Foo` or `obj.foo`) will resolve to the same underlying key, regardless of casing.
|
|
957
|
+
* Only affects string-based property access (not symbols).
|
|
958
|
+
*
|
|
959
|
+
* @template T
|
|
960
|
+
* @param {T} obj - The original object.
|
|
961
|
+
* @returns {T} A proxied object with case-insensitive property access.
|
|
962
|
+
*/
|
|
963
|
+
declare function caseInsensitive<T extends Record<string, any>>(obj: any): T;
|
|
953
964
|
|
|
954
965
|
declare const Util_EMPTY_FUNCTION: typeof EMPTY_FUNCTION;
|
|
966
|
+
declare const Util_caseInsensitive: typeof caseInsensitive;
|
|
955
967
|
declare const Util_equal: typeof equal;
|
|
956
968
|
declare const Util_fetch: typeof fetch;
|
|
957
969
|
declare const Util_fetchWithRTT: typeof fetchWithRTT;
|
|
@@ -971,7 +983,7 @@ declare const Util_trimEnd: typeof trimEnd;
|
|
|
971
983
|
declare const Util_trimStart: typeof trimStart;
|
|
972
984
|
declare const Util_unixTime: typeof unixTime;
|
|
973
985
|
declare namespace Util {
|
|
974
|
-
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_equal as equal, Util_fetch as fetch, Util_fetchWithRTT as fetchWithRTT, Util_getBaseFile as getBaseFile, Util_getExtension as getExtension, Util_getFile as getFile, Util_iterableEntries as iterableEntries, Util_objectFrom as objectFrom, Util_options as options, Util_safePromise as safePromise, Util_sleep as sleep, Util_stringify as stringify, Util_time as time, Util_toBin as toBin, Util_trim as trim, Util_trimEnd as trimEnd, Util_trimStart as trimStart, Util_unixTime as unixTime };
|
|
986
|
+
export { Util_EMPTY_FUNCTION as EMPTY_FUNCTION, Util_caseInsensitive as caseInsensitive, Util_equal as equal, Util_fetch as fetch, Util_fetchWithRTT as fetchWithRTT, Util_getBaseFile as getBaseFile, Util_getExtension as getExtension, Util_getFile as getFile, Util_iterableEntries as iterableEntries, Util_objectFrom as objectFrom, Util_options as options, Util_safePromise as safePromise, Util_sleep as sleep, Util_stringify as stringify, Util_time as time, Util_toBin as toBin, Util_trim as trim, Util_trimEnd as trimEnd, Util_trimStart as trimStart, Util_unixTime as unixTime };
|
|
975
987
|
}
|
|
976
988
|
|
|
977
989
|
/**
|
|
@@ -1139,24 +1151,22 @@ declare class WebSocketReliable extends EventEmitter {
|
|
|
1139
1151
|
* @param {HTMLCanvasElement} canvas
|
|
1140
1152
|
* @param {CanvasRenderingContext2D} context
|
|
1141
1153
|
* @param {Date} now current date, new Date() by default
|
|
1142
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
1143
1154
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
1144
1155
|
* @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
|
|
1145
1156
|
*/
|
|
1146
|
-
declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date,
|
|
1157
|
+
declare function getLatency(sourceEl: HTMLVideoElement, canvas: HTMLCanvasElement, context: CanvasRenderingContext2D, now?: Date, tolerance?: number): number;
|
|
1147
1158
|
/**
|
|
1148
1159
|
* Decode a previously encoded timestamp from a canvas
|
|
1149
1160
|
*
|
|
1150
1161
|
* @param {CanvasRenderingContext2D} context
|
|
1151
1162
|
* @param {Number} lineWidth width of the line in pixels
|
|
1152
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
1153
1163
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
1154
1164
|
* @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
|
|
1155
1165
|
*/
|
|
1156
|
-
declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number,
|
|
1166
|
+
declare function decodeTimestamp(context: CanvasRenderingContext2D, lineWidth: number, tolerance?: number): Date | null;
|
|
1157
1167
|
/**
|
|
1158
|
-
* Encode the
|
|
1159
|
-
* written on the top of the canvas.
|
|
1168
|
+
* Encode the given date (excluding year and month) into a line composed of blocks
|
|
1169
|
+
* of black and white pixels written on the top of the canvas.
|
|
1160
1170
|
*
|
|
1161
1171
|
* @param {CanvasRenderingContext2D} context
|
|
1162
1172
|
* @param {Number} lineWidth width of the line in pixels
|
package/dist/web-utils.js
CHANGED
|
@@ -586,7 +586,30 @@ function trimEnd(value, chars = ' ') {
|
|
|
586
586
|
--i;
|
|
587
587
|
}
|
|
588
588
|
return value.substring(0, i);
|
|
589
|
-
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Wraps an object with a Proxy that makes property access case-insensitive.
|
|
592
|
+
*
|
|
593
|
+
* Property lookup (e.g. `obj.Foo` or `obj.foo`) will resolve to the same underlying key, regardless of casing.
|
|
594
|
+
* Only affects string-based property access (not symbols).
|
|
595
|
+
*
|
|
596
|
+
* @template T
|
|
597
|
+
* @param {T} obj - The original object.
|
|
598
|
+
* @returns {T} A proxied object with case-insensitive property access.
|
|
599
|
+
*/
|
|
600
|
+
function caseInsensitive(obj) {
|
|
601
|
+
return new Proxy(obj, {
|
|
602
|
+
get(target, prop, receiver) {
|
|
603
|
+
if (typeof prop === 'string') {
|
|
604
|
+
const key = Object.keys(target).find(k => k.toLowerCase() === prop.toLowerCase());
|
|
605
|
+
if (key !== undefined) {
|
|
606
|
+
return Reflect.get(target, key, receiver);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return Reflect.get(target, prop, receiver);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}var Util=/*#__PURE__*/Object.freeze({__proto__:null,EMPTY_FUNCTION:EMPTY_FUNCTION,caseInsensitive:caseInsensitive,equal:equal,fetch:fetch,fetchWithRTT:fetchWithRTT,getBaseFile:getBaseFile,getExtension:getExtension,getFile:getFile,iterableEntries:iterableEntries,objectFrom:objectFrom,options:options,safePromise:safePromise,sleep:sleep,stringify:stringify,time:time,toBin:toBin,trim:trim,trimEnd:trimEnd,trimStart:trimStart,unixTime:unixTime});/**
|
|
590
613
|
* Copyright 2024 Ceeblue B.V.
|
|
591
614
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
592
615
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
@@ -1449,7 +1472,7 @@ class EventEmitter extends Loggable {
|
|
|
1449
1472
|
* Event subscription only one time, once time fired it's automatically unsubscribe
|
|
1450
1473
|
* @param name Name of event without the `on` prefix (ex: `log` to `onLog` event declared)
|
|
1451
1474
|
* @param event Subscriber Function
|
|
1452
|
-
* @param options.
|
|
1475
|
+
* @param options.signal Optional `AbortSignal` to stop this or multiple subscriptions in same time
|
|
1453
1476
|
*/
|
|
1454
1477
|
once(name, event, options) {
|
|
1455
1478
|
var _a;
|
|
@@ -2197,6 +2220,8 @@ class WebSocketReliable extends EventEmitter {
|
|
|
2197
2220
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
2198
2221
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
2199
2222
|
*/
|
|
2223
|
+
// Fixed blocks to 32 because timestamp is encoded on 32 bits
|
|
2224
|
+
const _blocksPerRow = 32;
|
|
2200
2225
|
/**
|
|
2201
2226
|
* Decode the timestamp from a video element and compute the latency between the timestamp and the current date.
|
|
2202
2227
|
*
|
|
@@ -2204,19 +2229,18 @@ class WebSocketReliable extends EventEmitter {
|
|
|
2204
2229
|
* @param {HTMLCanvasElement} canvas
|
|
2205
2230
|
* @param {CanvasRenderingContext2D} context
|
|
2206
2231
|
* @param {Date} now current date, new Date() by default
|
|
2207
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
2208
2232
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
2209
2233
|
* @returns {Number} The latency in millisecond between 'now' and the decoded timestamp, 0 if the timestamp cannot be decoded
|
|
2210
2234
|
*/
|
|
2211
|
-
function getLatency(sourceEl, canvas, context, now = new Date(),
|
|
2235
|
+
function getLatency(sourceEl, canvas, context, now = new Date(), tolerance = 0.2) {
|
|
2212
2236
|
canvas.width = sourceEl.videoWidth;
|
|
2213
|
-
canvas.height = Math.floor(canvas.width /
|
|
2237
|
+
canvas.height = Math.floor(canvas.width / _blocksPerRow);
|
|
2214
2238
|
if (!canvas.width || !canvas.height) {
|
|
2215
2239
|
// No pixel to parse!
|
|
2216
2240
|
return 0;
|
|
2217
2241
|
}
|
|
2218
2242
|
context.drawImage(sourceEl, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
|
|
2219
|
-
const timestamp = decodeTimestamp(context, canvas.width,
|
|
2243
|
+
const timestamp = decodeTimestamp(context, canvas.width, tolerance);
|
|
2220
2244
|
return timestamp == null ? 0 : now.getTime() - timestamp.getTime();
|
|
2221
2245
|
}
|
|
2222
2246
|
/**
|
|
@@ -2224,20 +2248,25 @@ function getLatency(sourceEl, canvas, context, now = new Date(), blocksPerRow =
|
|
|
2224
2248
|
*
|
|
2225
2249
|
* @param {CanvasRenderingContext2D} context
|
|
2226
2250
|
* @param {Number} lineWidth width of the line in pixels
|
|
2227
|
-
* @param {Number} blocksPerRow number of blocks in the line, 32 by default
|
|
2228
2251
|
* @param {Number} tolerance percentage of tolerance for the black and white threshold, 0.2 by default
|
|
2229
2252
|
* @returns {Date|null} The Date object representing the timestamp or null if the timestamp cannot be decoded
|
|
2230
2253
|
*/
|
|
2231
|
-
function decodeTimestamp(context, lineWidth,
|
|
2232
|
-
|
|
2254
|
+
function decodeTimestamp(context, lineWidth, tolerance = 0.2) {
|
|
2255
|
+
// Integer block width and ignore the right-hand remainder.
|
|
2256
|
+
const blockSize = Math.floor(lineWidth / _blocksPerRow);
|
|
2233
2257
|
let binaryTime = '';
|
|
2234
|
-
|
|
2235
|
-
|
|
2258
|
+
if (blockSize < 1) {
|
|
2259
|
+
return null;
|
|
2260
|
+
}
|
|
2261
|
+
const effectiveWidth = blockSize * _blocksPerRow;
|
|
2262
|
+
const midBlock = Math.floor(blockSize / 2);
|
|
2263
|
+
const data = context.getImageData(0, midBlock, effectiveWidth, 1).data;
|
|
2236
2264
|
const pixels = new Uint32Array(data.buffer);
|
|
2237
2265
|
const blackThreshold = 0xff * tolerance;
|
|
2238
2266
|
const whiteThreshold = 0xff * (1 - tolerance);
|
|
2239
|
-
|
|
2240
|
-
|
|
2267
|
+
// Sample the center pixel of each integer-sized block that the encoder painted.
|
|
2268
|
+
for (let i = 0; i < effectiveWidth; i += blockSize) {
|
|
2269
|
+
const pixel = pixels[i + midBlock] & 0xffffff;
|
|
2241
2270
|
// Extract luminance from RGB
|
|
2242
2271
|
const Y = 0.299 * ((pixel >> 16) & 0xff) + 0.587 * ((pixel >> 8) & 0xff) + 0.114 * (pixel & 0xff);
|
|
2243
2272
|
if (Y < blackThreshold) {
|
|
@@ -2249,21 +2278,24 @@ function decodeTimestamp(context, lineWidth, blocksPerRow = 32, tolerance = 0.2)
|
|
|
2249
2278
|
binaryTime += '0';
|
|
2250
2279
|
}
|
|
2251
2280
|
else {
|
|
2252
|
-
return;
|
|
2281
|
+
return null;
|
|
2253
2282
|
}
|
|
2254
|
-
i += blockSize;
|
|
2255
2283
|
}
|
|
2256
2284
|
const day = parseInt(binaryTime.slice(0, 5), 2);
|
|
2257
2285
|
const hour = parseInt(binaryTime.slice(5, 10), 2);
|
|
2258
2286
|
const minute = parseInt(binaryTime.slice(10, 16), 2);
|
|
2259
2287
|
const second = parseInt(binaryTime.slice(16, 22), 2);
|
|
2260
2288
|
const millisecond = parseInt(binaryTime.slice(22, 32), 2);
|
|
2289
|
+
// Basic sanity checks (match encoder ranges)
|
|
2290
|
+
if (day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 || millisecond > 999) {
|
|
2291
|
+
return null;
|
|
2292
|
+
}
|
|
2261
2293
|
const now = new Date();
|
|
2262
2294
|
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), day, hour, minute, second, millisecond));
|
|
2263
2295
|
}
|
|
2264
2296
|
/**
|
|
2265
|
-
* Encode the
|
|
2266
|
-
* written on the top of the canvas.
|
|
2297
|
+
* Encode the given date (excluding year and month) into a line composed of blocks
|
|
2298
|
+
* of black and white pixels written on the top of the canvas.
|
|
2267
2299
|
*
|
|
2268
2300
|
* @param {CanvasRenderingContext2D} context
|
|
2269
2301
|
* @param {Number} lineWidth width of the line in pixels
|
|
@@ -2277,11 +2309,11 @@ function encodeTimestamp(context, lineWidth, blocksPerRow = 32, now = new Date()
|
|
|
2277
2309
|
const minute = now.getUTCMinutes();
|
|
2278
2310
|
const second = now.getUTCSeconds();
|
|
2279
2311
|
const millisecond = now.getUTCMilliseconds();
|
|
2280
|
-
const binaryDay = day.toString(2).padStart(5, '0');
|
|
2281
|
-
const binaryHour = hour.toString(2).padStart(5, '0');
|
|
2282
|
-
const binaryMinute = minute.toString(2).padStart(6, '0');
|
|
2283
|
-
const binarySecond = second.toString(2).padStart(6, '0');
|
|
2284
|
-
const binaryMillisecond = millisecond.toString(2).padStart(10, '0');
|
|
2312
|
+
const binaryDay = day.toString(2).padStart(5, '0'); // 31 possible days/32
|
|
2313
|
+
const binaryHour = hour.toString(2).padStart(5, '0'); // 24 possible hours/32
|
|
2314
|
+
const binaryMinute = minute.toString(2).padStart(6, '0'); // 60 possible minutes/64
|
|
2315
|
+
const binarySecond = second.toString(2).padStart(6, '0'); // 60 possible seconds/64
|
|
2316
|
+
const binaryMillisecond = millisecond.toString(2).padStart(10, '0'); // 1000 possible milliseconds/1024
|
|
2285
2317
|
const binaryTime = binaryDay + binaryHour + binaryMinute + binarySecond + binaryMillisecond;
|
|
2286
2318
|
for (let i = 0; i < binaryTime.length; i++) {
|
|
2287
2319
|
const x = (i % blocksPerRow) * blockSize;
|
|
@@ -2548,4 +2580,4 @@ class UIMetrics {
|
|
|
2548
2580
|
* This file is part of https://github.com/CeeblueTV/web-utils which is released under GNU Affero General Public License.
|
|
2549
2581
|
* See file LICENSE or go to https://spdx.org/licenses/AGPL-3.0-or-later.html for full license details.
|
|
2550
2582
|
*/
|
|
2551
|
-
const VERSION = '7.0
|
|
2583
|
+
const VERSION = '7.2.0';export{BinaryReader,BinaryWriter,BitReader,ByteRate,Connect,EpochTime,EventEmitter,FixMap,Log,LogLevel,Loggable,NetAddress,Numbers,Queue,SDP,UIMetrics,Util,VERSION,WebSocketReliable,log};//# sourceMappingURL=web-utils.js.map
|