import { normalizePath } from "obsidian" ; import { Logger } from "./logger" ; import { FLAGMD_REDFLAG , LOG_LEVEL } from "./types" ; export function arrayBufferToBase64 ( buffer ) { return new Promise ( ( res ) => { const blob = new Blob ( [ buffer ] , { type : "application/octet-binary" } ) ; const reader = new FileReader ( ) ; reader . onload = function ( evt ) { const dataurl = evt . target . result . toString ( ) ; res ( dataurl . substr ( dataurl . indexOf ( "," ) + 1 ) ) ; } ; reader . readAsDataURL ( blob ) ; } ) ; } export function base64ToString ( base64 ) { try { const binary_string = window . atob ( base64 ) ; const len = binary_string . length ; const bytes = new Uint8Array ( len ) ; for ( let i = 0 ; i < len ; i ++ ) { bytes [ i ] = binary_string . charCodeAt ( i ) ; } return new TextDecoder ( ) . decode ( bytes ) ; } catch ( ex ) { return base64 ; } } export function base64ToArrayBuffer ( base64 ) { try { const binary_string = window . atob ( base64 ) ; const len = binary_string . length ; const bytes = new Uint8Array ( len ) ; for ( let i = 0 ; i < len ; i ++ ) { bytes [ i ] = binary_string . charCodeAt ( i ) ; } return bytes . buffer ; } catch ( ex ) { try { return new Uint16Array ( [ ] . map . call ( base64 , function ( c ) { return c . charCodeAt ( 0 ) ; } ) ) . buffer ; } catch ( ex2 ) { return null ; } } } export const escapeStringToHTML = ( str ) => { if ( ! str ) return "" ; return str . replace ( /[<>&"'`]/g , ( match ) => { const escape = { "<" : "&lt;" , ">" : "&gt;" , "&" : "&amp;" , '"' : "&quot;" , "'" : "&#39;" , "`" : "&#x60;" , } ; return escape [ match ] ; } ) ; } ; export function resolveWithIgnoreKnownError < T > ( p , def ) { return new Promise ( ( res , rej ) => { p . then ( res ) . catch ( ( ex ) => ( ex . status && ex . status == 404 ? res ( def ) : rej ( ex ) ) ) ; } ) ; } export function isValidPath ( filename ) { const regex = /[\u0000-\u001f]|[\\":?<>|*#]/g ; let x = filename . replace ( regex , "_" ) ; const win = /(\\|\/)(COM\d|LPT\d|CON|PRN|AUX|NUL|CLOCK$)($|\.)/gi ; const sx = ( x = x . replace ( win , "/_" ) ) ; return sx == filename ; } export function shouldBeIgnored ( filename ) { if ( filename == FLAGMD_REDFLAG ) { return true ; } return false ; } export function versionNumberString2Number ( version ) { return version . split ( "." ) . reverse ( ) . map ( ( e , i ) => ( ( e as any ) / 1 ) * 1000 ** i ) . reduce ( ( prev , current ) => prev + current , 0 ) ; } export const delay = ( ms ) : Promise < void > => { return new Promise ( ( res ) => { setTimeout ( ( ) => { res ( ) ; } , ms ) ; } ) ; } ; export function path2id ( filename ) { let x = normalizePath ( filename ) ; if ( x . startsWith ( "_" ) ) x = "/" + x ; return x ; } export function id2path ( filename ) { return normalizePath ( filename ) ; } const runningProcs = [ ] ; const pendingProcs = { } ; function objectToKey ( key ) { if ( typeof key === "string" ) return key ; const keys = Object . keys ( key ) . sort ( ( a , b ) => a . localeCompare ( b ) ) ; return keys . map ( ( e ) => e + objectToKey ( key [ e ] ) ) . join ( ":" ) ; } export function getProcessingCounts ( ) { let count = 0 ; for ( const v in pendingProcs ) { count += pendingProcs [ v ] . length ; } count += runningProcs . length ; return count ; } let externalNotifier = ( ) => { } ; let notifyTimer = null ; export function setLockNotifier ( fn ) { externalNotifier = fn ; } function notifyLock ( ) { if ( notifyTimer != null ) { window . clearTimeout ( notifyTimer ) ; } notifyTimer = window . setTimeout ( ( ) => { externalNotifier ( ) ; } , 100 ) ; } export function runWithLock < T > ( key , ignoreWhenRunning , proc ) { const lockKey = typeof key === "string" ? key : objectToKey ( key ) ; const handleNextProcs = ( ) => { if ( typeof pendingProcs [ lockKey ] === "undefined" ) { runningProcs . remove ( lockKey ) ; notifyLock ( ) ; } else { Logger ( `Lock:${ lockKey }:left ${ pendingProcs [ lockKey ] . length }` , LOG_LEVEL . VERBOSE ) ; let nextProc = null ; nextProc = pendingProcs [ lockKey ] . shift ( ) ; notifyLock ( ) ; if ( nextProc ) { nextProc ( ) . then ( ) . catch ( ( err ) => { Logger ( err ) ; } ) . finally ( ( ) => { if ( pendingProcs && lockKey in pendingProcs && pendingProcs [ lockKey ] . length == 0 ) { delete pendingProcs [ lockKey ] ; notifyLock ( ) ; } queueMicrotask ( ( ) => { handleNextProcs ( ) ; } ) ; } ) ; } else { if ( pendingProcs && lockKey in pendingProcs && pendingProcs [ lockKey ] . length == 0 ) { delete pendingProcs [ lockKey ] ; notifyLock ( ) ; } } } } ; if ( runningProcs . contains ( lockKey ) ) { if ( ignoreWhenRunning ) { return null ; } if ( typeof pendingProcs [ lockKey ] === "undefined" ) { pendingProcs [ lockKey ] = [ ] ; } let responderRes ; let responderRej ; const responder = new Promise < T > ( ( res , rej ) => { responderRes = res ; responderRej = rej ; } ) ; const subproc = ( ) => new Promise < void > ( ( res , rej ) => { proc ( ) . then ( ( v ) => { handleNextProcs ( ) ; responderRes ( v ) ; res ( ) ; } ) . catch ( ( reason ) => { Logger ( `Lock:${ key }:rejected` , LOG_LEVEL . VERBOSE ) ; handleNextProcs ( ) ; rej ( reason ) ; responderRej ( reason ) ; } ) ; } ) ; pendingProcs [ lockKey ] . push ( subproc ) ; notifyLock ( ) ; return responder ; } else { runningProcs . push ( lockKey ) ; notifyLock ( ) ; return new Promise ( ( res , rej ) => { proc ( ) . then ( ( v ) => { handleNextProcs ( ) ; res ( v ) ; } ) . catch ( ( reason ) => { handleNextProcs ( ) ; rej ( reason ) ; } ) ; } ) ; } }