@@ -30,6 +30,9 @@ import { validateCache, setCache } from './cache.js';
3030import { compress as cAXOR , decompress as dAXOR } from './modes/axor.js' ;
3131import { B64toUI8A , UI8AtoB64 } from '../lib/uint8.js' ;
3232
33+ let JSSCInstanceID = 0n ;
34+ const JSSCInstances = { } ;
35+
3336function cryptCharCode (
3437 code , get = false ,
3538 repeatBefore = false , repeatAfter = false ,
@@ -139,6 +142,9 @@ function readOptions(options, defaults) {
139142 if ( ( key == 'depth' || key . toLowerCase ( ) == 'depthlimit' || key == 'worker' || key . toLowerCase ( ) == 'workerlimit' ) && typeof value == 'number' ) {
140143 defaults [ key . toLowerCase ( ) ] = value ;
141144 continue ;
145+ } if ( key == 'instance' && typeof value == 'string' ) {
146+ defaults [ key ] = value ;
147+ continue ;
142148 }
143149 if ( typeof value == 'undefined' ) continue ;
144150 if ( typeof value != 'boolean' ) throw new Error ( prefix + 'Invalid options input.' ) ;
@@ -179,7 +185,7 @@ function getModeID(code1, code2) {
179185 return code1 ;
180186 }
181187}
182- class JSSC {
188+ class JSSCDebug {
183189 constructor ( com , dec , opts , m = 0 , workers = false ) {
184190 const headerchar = decToBin ( com . charCodeAt ( 0 ) , 16 ) ;
185191 const code1 = headerchar . slice ( 11 ) ;
@@ -300,8 +306,40 @@ export async function compress(input, options) {
300306 debug : false ,
301307
302308 depth : 0 ,
303- worker : 0
309+ worker : 0 ,
310+
311+ instance : undefined ,
304312 } ;
313+
314+ let progressAll = 0 ;
315+ let progressModes = 0 ;
316+ let progressMisc = 0 ;
317+ let progressLast = 0 ;
318+ const candidates = [
319+ IIE ,
320+ DIP ,
321+ B64IE ,
322+ TDCCC ,
323+ TBCCC ,
324+ CE ,
325+ AE ,
326+ FM ,
327+ URL_ ,
328+ S ,
329+ SR ,
330+ EP ,
331+ B64P ,
332+ OE ,
333+ LZS ,
334+ AXOR
335+ ] ;
336+ let done = candidates . length + 2 ;
337+ function onProgress ( ) {
338+ if ( typeof opts . instance != 'undefined' && progressAll != progressLast ) {
339+ progressLast = progressAll ;
340+ JSSCInstances [ opts . instance ] . events [ 'onCompressProgress' ] ( Math . floor ( progressAll / done * 100 ) ) ;
341+ }
342+ }
305343
306344 /* Read options */
307345 if ( options ) opts = readOptions ( options , opts ) ;
@@ -396,6 +434,7 @@ export async function compress(input, options) {
396434 code3 = 3 ;
397435 } } catch ( _ ) {
398436 } }
437+ progressMisc ++ ; progressAll ++ ; onProgress ( ) ;
399438
400439 if ( ! / \d / . test ( str ) ) {
401440 str = repeatChars ( str ) ;
@@ -424,12 +463,19 @@ export async function compress(input, options) {
424463 }
425464
426465 const safeTry = async ( fn ) => {
466+ let result = null ;
427467 try {
428- return await fn ( ) ;
468+ result = await fn ( ) ;
429469 } catch ( err ) {
430470 if ( opts . debug ) console . warn ( err ) ;
431- return null ;
471+ result = null ;
432472 }
473+ if ( typeof opts . instance != 'undefined' && result ) {
474+ const modeID = ( new JSSCDebug ( result , input , opts , 0 , false ) ) . output . mode ;
475+ JSSCInstances [ opts . instance ] . events [ 'onCompressionMode' ] ( modeID , input , result ) ;
476+ progressAll ++ ; progressModes ++ ; onProgress ( ) ;
477+ }
478+ return result ;
433479 } ;
434480
435481 const validate = async ( compressed ) => {
@@ -445,26 +491,8 @@ export async function compress(input, options) {
445491 const context = {
446492 opts,
447493 str, isNum, code3, originalInput,
448- beginId, repeatBefore
494+ beginId, repeatBefore,
449495 } ;
450- const candidates = [
451- IIE ,
452- DIP ,
453- B64IE ,
454- TDCCC ,
455- TBCCC ,
456- CE ,
457- AE ,
458- FM ,
459- URL_ ,
460- S ,
461- SR ,
462- EP ,
463- B64P ,
464- OE ,
465- LZS ,
466- AXOR
467- ] ;
468496 async function noWorkers ( ) {
469497 return await Promise . all ( candidates . map ( fn => safeTry ( async ( ) => await fn ( context ) ) ) ) ;
470498 }
@@ -493,6 +521,9 @@ export async function compress(input, options) {
493521 usedWorkers = false ;
494522 }
495523
524+ if ( progressModes != candidates . length ) progressAll = progressMisc + candidates . length ;
525+ onProgress ( ) ;
526+
496527 results = results . filter ( r => typeof r === 'string' && r . length <= String ( originalInput ) . length ) ;
497528
498529 let best ;
@@ -520,7 +551,11 @@ export async function compress(input, options) {
520551 if ( await validateOffsetEncoding ( res , best , enc [ 2 ] ) ) best = res ;
521552 }
522553
523- if ( opts . debug ) return new JSSC ( best , originalInput , opts , 0 , usedWorkers ) ;
554+ progressAll ++ ; progressMisc ++ ; onProgress ( ) ;
555+
556+ if ( typeof opts . instance != 'undefined' ) JSSCInstances [ opts . instance ] . events [ 'onCompressed' ] ( input , best ) ;
557+
558+ if ( opts . debug ) return new JSSCDebug ( best , originalInput , opts , 0 , usedWorkers ) ;
524559
525560 return best ;
526561}
@@ -577,7 +612,9 @@ export async function decompress(str, stringify = false) {
577612 let opts = {
578613 stringify : false ,
579614
580- debug : false
615+ debug : false ,
616+
617+ instance : undefined ,
581618 }
582619
583620 /* Read options */
@@ -625,7 +662,8 @@ export async function decompress(str, stringify = false) {
625662 }
626663
627664 function checkOutput ( out ) {
628- if ( opts . debug ) return new JSSC ( s , out , opts , 1 ) ;
665+ if ( typeof opts . instance != 'undefined' ) JSSCInstances [ opts . instance ] . events [ 'onDecompressed' ] ( str , out ) ;
666+ if ( opts . debug ) return new JSSCDebug ( s , out , opts , 1 ) ;
629667 return out ;
630668 }
631669 async function processOutput ( out , checkOut = true ) {
@@ -869,7 +907,7 @@ export async function decompress(str, stringify = false) {
869907}
870908
871909function noDebugMode ( result ) {
872- if ( result instanceof JSSC ) throw new Error ( prefix + 'Invalid options input.' ) ;
910+ if ( result instanceof JSSCDebug ) throw new Error ( prefix + 'Invalid options input.' ) ;
873911 return result ;
874912}
875913
@@ -1330,7 +1368,8 @@ export async function S(context) {
13301368 const segOpts = {
13311369 ...opts ,
13321370 segmentation : false ,
1333- depth : opts . depth + 1
1371+ depth : opts . depth + 1 ,
1372+ instance : undefined
13341373 }
13351374 const compressed = await compress ( seg , segOpts ) ;
13361375
@@ -1467,7 +1506,8 @@ export async function OE(context) {
14671506 const res = enc [ 1 ] + await compress ( enc [ 0 ] , {
14681507 ...opts ,
14691508 offsetencoding : false ,
1470- depth : opts . depth + 1
1509+ depth : opts . depth + 1 ,
1510+ instance : undefined
14711511 } ) ;
14721512 if ( await validateOffsetEncoding ( res , originalInput , enc [ 2 ] ) ) return res ;
14731513 return null ;
@@ -1509,10 +1549,79 @@ export function setWorkerURL(url) {
15091549 }
15101550 } ) ( )
15111551 )
1512- ) throw new Error ( prefix + 'invalid URL.' ) ;
1552+ ) throw new Error ( prefix + 'Invalid URL.' ) ;
15131553 customWorkerURL = url ;
15141554}
15151555export function getWorkerURL ( ) {
15161556 if ( typeof customWorkerURL == 'string' || typeof customWorkerURL == 'object' ) return customWorkerURL ;
15171557 return workerURL ;
15181558}
1559+
1560+ /* JSSC Instance */
1561+
1562+ function JSSCFunctions ( id , ...functions ) {
1563+ const output = [ ] ;
1564+ for ( let i = 0 ; i < functions . length ; i ++ ) {
1565+ output . push ( async ( input , options , ...args ) => {
1566+ let opts = options || { } ;
1567+ if ( typeof options == 'boolean' ) opts = { stringify : options } ;
1568+ opts . instance = id ;
1569+ return await functions [ i ] ( input , opts , ...args ) ;
1570+ } ) ;
1571+ }
1572+ return output ;
1573+ }
1574+ export const JSSC = class JSSC {
1575+ #events;
1576+ constructor ( cloneInstanceID ) {
1577+ const ID = convertBase ( ( JSSCInstanceID ++ ) . toString ( 10 ) , 10 , 64 ) ;
1578+ this . ID = ID ;
1579+ const isClone = typeof cloneInstanceID == 'string' && cloneInstanceID in JSSCInstances ;
1580+ const parent = JSSCInstances [ cloneInstanceID ] ;
1581+ this . #events = isClone ? parent . events : {
1582+ onCompressed : ( input , output ) => { } ,
1583+ onCompressProgress : ( percentage ) => { } ,
1584+ onCompressionMode : ( modeID , input , output ) => { } ,
1585+ onDecompressed : ( input , output ) => { } ,
1586+ } ;
1587+ const events = this . #events;
1588+ function set ( name , func ) {
1589+ if ( typeof func != 'function' ) throw new Error ( prefix + 'Invalid event listener.' ) ;
1590+ events [ name ] = func ;
1591+ JSSCInstances [ ID ] . events = events ;
1592+ }
1593+ this . events = {
1594+ get [ 'onCompressed' ] ( ) { return events . onCompressed } ,
1595+ get [ 'onCompressProgress' ] ( ) { return events . onCompressProgress } ,
1596+ get [ 'onCompressionMode' ] ( ) { return events . onCompressionMode } ,
1597+ get [ 'onDecompressed' ] ( ) { return events . onDecompressed } ,
1598+ set [ 'onCompressed' ] ( func ) { set ( 'onCompressed' , func ) } ,
1599+ set [ 'onCompressProgress' ] ( func ) { set ( 'onCompressProgress' , func ) } ,
1600+ set [ 'onCompressionMode' ] ( func ) { set ( 'onCompressionMode' , func ) } ,
1601+ set [ 'onDecompressed' ] ( func ) { set ( 'onDecompressed' , func ) } ,
1602+ } ;
1603+ JSSCInstances [ this . ID ] = this ;
1604+ [
1605+ this . compress , this . decompress ,
1606+ this . compressToBase64 , this . decompressFromBase64 ,
1607+ this . compressToBase64URL , this . decompressFromBase64URL ,
1608+ this . compressToUint8Array , this . decompressFromUint8Array ,
1609+ this . compressLarge , this . compressLargeToBase64 , this . compressLargeToBase64URL , this . compressLargeToUint8Array
1610+ ] = JSSCFunctions ( this . ID ,
1611+ compress , decompress ,
1612+ compressToBase64 , decompressFromBase64 ,
1613+ compressToBase64URL , decompressFromBase64URL ,
1614+ compressToUint8Array , decompressFromUint8Array ,
1615+ compressLarge , compressLargeToBase64 , compressLargeToBase64URL , compressLargeToUint8Array
1616+ ) ;
1617+ this . clone = ( ) => {
1618+ return new JSSC ( this . ID ) ;
1619+ } ;
1620+ this . delete = ( ) => {
1621+ delete JSSCInstances [ this . ID ] ;
1622+ for ( const [ key ] of Object . keys ( this ) ) {
1623+ delete this [ key ] ;
1624+ }
1625+ }
1626+ }
1627+ }
0 commit comments