Utilisateur:Yopyop456/Brouillon/utils
Apparence
Cette page est un brouillon appartenant à Yopyop456
Conseils de rédaction
- → N'hésitez pas à publier sur le brouillon un texte inachevé et à le modifier autant que vous le souhaitez.
- → Pour enregistrer vos modifications au brouillon, il est nécessaire de cliquer sur le bouton bleu : « Publier les modifications ». Il n'y a pas d'enregistrement automatique.
Si votre but est de publier un nouvel article, votre brouillon doit respecter les points suivants :
- Respectez le droit d'auteur en créant un texte spécialement pour Wikipédia en français (pas de copier-coller venu d'ailleurs).
- Indiquez les éléments démontrant la notoriété du sujet (aide).
- Liez chaque fait présenté à une source de qualité (quelles sources – comment les insérer).
- Utilisez un ton neutre, qui ne soit ni orienté ni publicitaire (aide).
- Veillez également à structurer votre article, de manière à ce qu'il soit conforme aux autres pages de l'encyclopédie (structurer – mettre en page).
- → Si ces points sont respectés, pour transformer votre brouillon en article, utilisez le bouton « publier le brouillon » en haut à droite. Votre brouillon sera alors transféré dans l'espace encyclopédique.
export const version = 'v2024-0429'
/*
fetch from cache
url: string
options: request options
options.cacheReload: boolean
return: response
*/
export async function cacheFetch(url, options = {}){
if(options.cacheReload) await cacheClear(url)
return cachePut(url, undefined, options)
}
/*
put in cache
url: string
content: string or buf
options: response options
options.cacheName: string
return: response
*/
export async function cachePut(url, content, options = {}){
let request, response, cache
cache = await caches.open(options.cacheName || "v1")
request = new Request(url, content ? {} : options)
response = await cache.match(request)
if(typeof content != 'undefined'){
if(response) return console.warn('cachePut error, already in cache') || true
request = new Request(url)
response = new Response(content, options)
await cache.put(request, response)
return true
}
if(!response){
response = await fetch(request)
if(response.status == 200) {
await cache.put(request, response.clone());
}
}
return response
}
/*
clear cache
re: regex string filter url
options.cacheName: string
*/
export async function cacheClear(re, options = {}){
if(!re){
await caches.delete(options.cacheName || 'v1')
return []
}
options.cacheDelete = true
return await cacheGetOutdated(re, options)
}
/*
get outdated cached files
substr: sub string filter url
options.cacheName: string
options.cacheDelete: boolean
*/
export async function cacheGetOutdated(substr, options = {}){
let cache, keys, key, res, web, local
cache = await caches.open(options.cacheName || 'v1')
keys = await cache.keys()
res = []
for(key of keys){
if(key.url.includes(substr)) {
if(options.cacheDelete){
res.push(key.url)
await cache.delete(key)
continue
}
web = await fetchHead(key.url)
local = await cache.match(key).then(x=>x.arrayBuffer())
if(!web['content-length']){
console.warn('no content-length: ' +key.url)
}
else if(web['content-length'] != local.byteLength){
res.push(key.url)
}
}
}
return res
}
/*
merge buffers
tarray: Array of TypedArray
keepOriginal: bool
return: TypedArray
let i, a, b
console.time("aaa")
for(i=1; i<110000; i++){
a = new Uint8Array(i).fill(1)
b = new Uint8Array(i).fill(2)
_.mergeBuf([a, b], true)
}
console.timeEnd("aaa")
console.time("bbb")
for(i=1; i<110000; i++){
a = new Uint8Array(i).fill(1)
b = new Uint8Array(i).fill(2)
_.mergeBuf([a, b], false)
}
console.timeEnd("bbb")
*/
export function mergeBuf(tarray, keepOriginal = true){
let size, buf, view, i
size = 0
for(i in tarray) size += tarray[i].byteLength
if(tarray[0].buffer.transfer){
i = keepOriginal ? 0 : tarray[0].byteLength
view = new tarray[0].constructor(keepOriginal ? size : tarray[0].buffer.transfer(size))
size = i
if(tarray[0].buffer.detached) tarray[0] = new tarray[0].constructor()
}
else {
buf = new ArrayBuffer(keepOriginal ? size : 0, {maxByteLength: size})
view = new tarray[0].constructor(buf)
size = 0
}
for(i in tarray){
if(buf && !keepOriginal) buf.resize(size + tarray[i].byteLength)
view.set(tarray[i], size / tarray[i].BYTES_PER_ELEMENT)
size += tarray[i].byteLength
if(!keepOriginal) tarray[i] = null
}
if(buf){
buf = buf.transferToFixedLength()
view = new view.constructor(buf)
}
return view
}
/*
fetch with download progress
url: string
options: fetch options
options.callback : function or selector string
options.resume: bool resume from partial content cached
fetchProgress.abort(resumable) : abort fetch / resumable bool
return: TypedArray
*/
export async function fetchProgress(url, options = {}){
if(fetchProgress.abort){
throw new Error('ongoing fetch')
}
let controller = new AbortController();
options.signal = controller.signal
let response, reader, contentLength, result
let receivedLength = 0;
let chunks = [];
if(options.resume){
response = await cacheFetch(url).then(x=>x.arrayBuffer())
await cacheClear(url)
chunks.push(response)
if(!options.headers) options.headers = {}
options.headers.range = 'bytes=' + response.byteLength + '-'
}
response = await fetch(url, options);
reader = response.body.getReader();
contentLength = +response.headers.get('Content-Length');
if(!contentLength) console.warn('not a range request')
fetchProgress.abort = async (resumable = false) => {
controller.abort()
delete fetchProgress.abort
if(!(resumable && contentLength)) return
result = mergeBuf(chunks, false)
await cachePut(url, result, {status: 202})
}
while(true) {
const {done, value} = await reader.read();
if (done) break;
chunks.push(value);
receivedLength += value.length;
if(typeof options.callback == 'function'){
options.callback(receivedLength, contentLength)
}
else if(typeof options.callback == 'string'){
let div = document.querySelector(options.callback)
let prog = div.querySelector('progress')
if(!prog){
prog = document.createElement('progress')
div.appendChild(prog)
prog.max = contentLength
}
else prog.max = contentLength
prog.value = receivedLength
}
}
result = mergeBuf(chunks, false)
delete fetchProgress.abort
return result
}
/*
wait for a specific amount of time
ms : int
*/
export function sleep(ms){
return new Promise(r => setTimeout(r, ms | 0))
}
/*
load a script
s: script text or js / mjs url
div: html element or div id to append, default document.head
return: html element
*/
export function loadScript(s, div = '') {
return new Promise(r => {
let elm = document.createElement('script')
elm.onload = () => r(elm)
elm.id = 'script' + document.querySelectorAll('script').length
if(s.includes('.mjs')) {
// elm.id = /(\w+).mjs/.exec(s)?.at(1)
elm.type = 'module'
elm.innerHTML = `
(async ()=>{
window.${elm.id} = await import('${s}');
document.getElementById('${elm.id}').dispatchEvent(new Event('load'));
})()
`
}
else if(s.includes('.js')) elm.src = s
else elm.innerHTML = s + `\ndocument.getElementById('${elm.id}').dispatchEvent(new Event('load'));`
div = div?.constructor?.name == 'HTMLBodyElement' ? div : (document.getElementById(div) || document.head)
div.appendChild(elm)
})
}
/*
load a stylesheet
s: style text or css url
div: html element or div id to append, default document.head
return: html element
*/
export function loadStyle(s, div = '') {
return new Promise(r => {
let elm
if(s.includes('.css')) {
elm = document.createElement('link')
elm.rel = 'stylesheet'
elm.href = s
}
else {
elm = document.createElement('style')
elm.innerHTML = s
}
elm.onload = () => r(elm)
div = div?.constructor?.name == 'HTMLBodyElement' ? div : (document.getElementById(div) || document.head)
div.appendChild(elm)
})
}
/*
set dark mode css
*/
export async function setDarkMode(){
let csstext
csstext = `
@media (prefers-color-scheme: dark) {
html {
filter: invert(100%);
}
}
`
if(getComputedStyle(document.body).background.includes('0, 0, 0')){
csstext += 'body { background: white }'
}
return await loadStyle(csstext)
}
/*
fetch multiple url simultaneously
cache: object {key url : value html}
options.max: max number of simultaneous connections
options.urlmodif: fetch url modifier
options.valmodif: fetch result modifier
options.method: string
options.responsetype: string
let cache = {
"https://huggingface.co/Xenova/Qwen1.5-0.5B-Chat/raw/main/merges.txt":"",
"https://huggingface.co/Xenova/Qwen1.5-0.5B-Chat/raw/main/tokenizer.json":"",
"https://huggingface.co/Xenova/Qwen1.5-0.5B-Chat/raw/main/vocab.json":""
}
*/
export function fetchAll (cache, options = {}) {
let {max = 4, urlmodif, valmodif, method, responsetype = "text"} = options
return typeof cache == 'object' && new Promise(r => {
let a, links, count, nth
if(fetchAll.abort) throw new Error('ongoing fetch')
fetchAll.abort = ()=>{max = 0}
links = []
for(a in cache) if(a.includes('http') && cache[a] == '') links.push(a)
count = 0
nth = 0
for(a=0; a<max; a++) getNext()
async function fetchOne(nth){
cache[links[nth]] = '...'
let txt, url, args
url = typeof urlmodif == 'function' ? (await urlmodif(links[nth])) : links[nth];
if(method == 'post'){
txt = url.split('?')
url = txt[0]
args = {
"headers": {
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
"body": txt[1] || '',
"method": "POST"
}
} else {
args = {}
}
txt = await fetch(url, args).then(x=>x[responsetype]())
cache[links[nth]] = typeof valmodif == 'function' ? (await valmodif(txt)) : txt
count--
getNext()
}
function getNext(){
if(nth >= links.length || !max){
if(count) return
else {
delete fetchAll.abort
return r('finish')
}
}
if(count >= max) return;
count++
fetchOne(nth++)
}
})
}
/*
Limit functions call. Execute once no call during a pediod of time
f : function to execute
ms : time to wait
return : new function
*/
export function delayfct(fn, ms=100){
let running = false
return () => {
function isrunning() {
if(running) return;
running = true;
setTimeout(function() {
running = false;
fn()
}, ms);
}
isrunning()
}
}
/*
Donwload a file
file : blob or string
name : filename, default creation date time
*/
export function download(file, name="") {
if(!(file instanceof Blob)) file = new Blob([file]);
let url = URL.createObjectURL(file);
let a = document.createElement("a");
a.href = url;
if(!name || name[0] == '.') {
let dat = (new Date()).toISOString()
name = dat.slice(0, 10).replaceAll("-", "_") + '_' + dat.slice(-7).replaceAll(/\D/g,"") + name
}
a.download = name;
a.style.display = "none";
document.body.appendChild(a);
if(name.trim()){
a.click();
setTimeout(()=>a.remove(), 100)
} else return a
}
/*
Fetch file head
url: string
*/
export async function fetchHead(url){
return await fetch(url, {method: 'HEAD'}).then((result) => {
let head = {}
for(let key of result.headers.keys()){
head[key] = result.headers.get(key)
}
return head
// return result.headers.get("content-length")
})
}
/*
# 1 - ArrayBuffer utilities
*/
/*
string to buffer
str: string, number (16 x 16 hex values)
return: uint8
*/
export function str2buf(str, nextarr = null){
let encoder, res, tmp
if(typeof str == "string") {
encoder = new TextEncoder("utf-8");
res = encoder.encode(str);
}
else if(typeof str == "number") {
res = new Uint8Array(16);
for(tmp=15; tmp>=0; tmp--){
res[tmp] = str%16
str = (str - (str%16)) / 16
}
}
else {
res = new Uint8Array(str);
}
if(nextarr){
nextarr = str2buf(nextarr)
res = mergeBuf([str, nextarr])
}
return res
}
/*
buffer to string
*/
export function buf2str(buffer) {
let decoder = new TextDecoder("utf-8");
return decoder.decode(buffer);
}
/*
buffer to base64
b64: string
url: url friendly escape chars
*/
export function buf2b64(buffer, url=false) {
// btoa(String.fromCharCode.apply(null, uint8));
let binary = '';
let bytes = new Uint8Array( buffer );
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
let b64 = btoa( binary );
return url ? b64.replace(/\//g, '_').replace(/\+/g, '-').replace(/=/g, '') : b64;
}
/*
base64 to buffer
b64: string
url: url friendly escape chars
*/
export function b642buf(b64, url=false){
// Uint8Array.from(atob( str ), c => c.charCodeAt(0))
if(url) b64 = b64.replaceAll(/_/g, '/').replaceAll(/-/g, '+');
let binary_string = atob(b64);
let len = binary_string.length;
let bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
}
/*
buf to hex
buf: string
alt: bool alternative representation
*/
export function buf2hex(buf, alt=false) {
let bytes = new Uint8Array(buf);
let hex = "";
for(let i=0; i<bytes.length; i++){
hex += alt ? "\\x" : "";
hex += ('0' + bytes[i].toString(16)).slice(-2);
}
return hex;
}
/*
hex to buf
hexString: string
*/
export function hex2buf(hexString) {
let result = [];
hexString = hexString.replaceAll('\\x', '')
for(let i=0; i < hexString.length; i+=2) {
result.push(parseInt(hexString.substr(i, 2), 16));
}
return (new Uint8Array(result));
}
/*
# 2 - Miscellaneous
*/
/*
Read file input
id : element id string, HTML Element, Array of Files
fn : function modifier
changeInputElm : boolean update files list in input
*/
export function readfiles (id, fn=null, changeInputElm=false){
return new Promise(r=>{
let input, files, len, result, file, a
if(id?.constructor?.name == 'Array' || id?.constructor?.name == 'FileList') input = {files: id}
else if(id?.constructor?.name == 'HTMLBodyElement') input = id
else input = document.getElementById(id)
files = input.files
len = files.length
const reader = new FileReader();
const dataTransfer = new DataTransfer()
result = {}
readfile(0)
function readfile(x){
reader.onload = (event) => {
result[files[x].name] = new Uint8Array(event.target.result)
if(x+1 == len) process()
else readfile(x+1)
}
reader.readAsArrayBuffer(files[x]);
}
async function process(){
if(typeof fn == 'function'){
await fn(result)
}
if(changeInputElm){
for(a in result){
if(a.length == 1) continue
file = new File([result[a]], a)
dataTransfer.items.add(file)
}
input.files = dataTransfer.files
}
r(result)
}
});
};
/*
save user setting, using method. If no val, get the data
val: string value
method: string cookie or localStorage
*/
export function saveUserSettings(val, method='cookie'){
if(method == 'cookie'){
if(val){
let s = val.includes('=') ? 86400 * 14 : -1;
return document.cookie = val+';max-age='+s+';sameSite;secure'
}
let elms, i, res
res = {}
elms = document.cookie.replaceAll('; ', '=')
elms = elms ? elms.split('=') : []
for(i=0; i<elms.length; i+=2) res[elms[i]] = elms[i+1]
return res
}
else if(method == 'localStorage'){
let key = location.pathname.substring(1).replaceAll('/', '_')
if(val) localStorage.setItem(key, typeof val == 'string' ? val : JSON.stringify(val))
else return JSON.parse(localStorage.getItem(key))
}
}
/*
post data to url
url : url
obj : formdata, selector string, or key value object
return fetch
*/
export function post(url, obj){
let data
if(obj?.constructor?.name == 'FormData') data = obj
else if(typeof obj == 'string') data = new FormData(document.querySelector(obj))
else {
data = new FormData();
for(let key in obj){
data.append(key, obj[key])
}
}
if(!url) return new Promise((r)=>r(data))
return fetch(url, {
method: "post",
body: data
})
}
/*
https://github.com/stephenjjbrown/string-similarity-js
string similarity based on the Sørensen–Dice coefficient
str1: string
str2: string
substringLength: string
caseSensitive: false
*/
export function stringSimilarity(str1, str2, substringLength = 2, caseSensitive = false) {
if (!caseSensitive) {
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();
}
if (str1.length < substringLength || str2.length < substringLength)
return 0;
var map = new Map();
for (var i = 0; i < str1.length - (substringLength - 1); i++) {
var substr1 = str1.substr(i, substringLength);
map.set(substr1, map.has(substr1) ? map.get(substr1) + 1 : 1);
}
var match = 0;
for (var j = 0; j < str2.length - (substringLength - 1); j++) {
var substr2 = str2.substr(j, substringLength);
var count = map.has(substr2) ? map.get(substr2) : 0;
if (count > 0) {
map.set(substr2, count - 1);
match++;
}
}
return (match * 2) / (str1.length + str2.length - ((substringLength - 1) * 2));
};
/*
Escape html entities
txt: string
*/
export function escapeHTML(txt){
let elm = document.createElement('textarea');
elm.textContent = txt
txt = elm.innerHTML
return txt
}
/*
Compress file using gzip deflate or deflate-raw
blob: blob or Uint8Array
dec: boolean decompress instead
*/
export async function compress(blob, dec = false){
if(blob?.constructor?.name == 'Uint8Array') blob = new Blob([blob])
const stream = dec ? new DecompressionStream("gzip") : new CompressionStream("gzip");
const readablestream = blob.stream().pipeThrough(stream);
return await new Response(readablestream).blob();
}
/*
Decompress file using gzip deflate or deflate-raw
blob: blob or Uint8Array
*/
export async function decompress(blob){
return await compress(blob, true)
}
/**
TAR HEADER
Field offset Field size Field
0 100 File name
100 8 File mode (octal)
108 8 Owner's numeric user ID (octal)
116 8 Group's numeric user ID (octal)
124 12 File size in bytes (octal, null terminated)
136 12 Last modification time in numeric Unix time format (octal, null terminated)
148 8 Checksum for header record
156 1 Link indicator (file type: 0 file, 5 folder)
157 100 Name of linked file
format ustar
257 "ustar"
262 version "00"
345 préfixe fichier
chunks of 512 bytes
blockchunks of 10240 bytes
end with two empty chunks
**/
/*
create tar archive
arr: n+0 filename n+1 arraybuf || key value object
opt.folder string
return: {tar buffer, idx n+0 position n+1 length}
*/
export function tarCreate(arr, opt = {}){
let i, j, bufsize = 0, buf, bloc, offset, idx, tmp;
if(typeof arr != "object") throw new Error('arr: not an object')
if(arr instanceof Array){
if(arr.length == 0) return;
if(arr.length%2) return;
} else {
tmp = []
for(i in arr){
if(!(typeof arr[i] == 'object' && arr[i].constructor.name.includes('Array'))) continue
tmp.push(i.replace(/.+\//, ''), arr[i])
}
arr = tmp
}
idx = [];
bufsize += 512;
for(i=0; i<arr.length; i+=2){
arr[i+1] = new Uint8Array(arr[i+1])
j = arr[i+1].byteLength;
j = 1024 + j - ((j-1)%512) - 1;
bufsize += j
}
bufsize += 1024;
bufsize = 10240 + bufsize - ((bufsize-1)%10240) - 1;
buf = new ArrayBuffer(bufsize);
offset = 0;
for(i=0; i<arr.length; i+=2){
bloc = new Uint8Array(buf, offset, 512);
bloc.set(str2buf("" + arr[i].substring(0, 100)), 0);
bloc.set(str2buf("0000775"), 100);
bloc.set(str2buf("0001750"), 108);
bloc.set(str2buf("0001750"), 116);
j = ("00000000000"+(arr[i+1].byteLength).toString(8)).slice(-11);
bloc.set(str2buf(j), 124);
j = ("00000000000"+(Math.trunc(Date.now()/1000)).toString(8)).slice(-11);
bloc.set(str2buf(j), 136);
j = arr[i+1].byteLength == 0 ? "5" : "0";
// 148
bloc.set(str2buf(j), 156);
bloc.set(str2buf("ustar"), 257);
bloc.set(str2buf("00"), 262);
bloc.set(str2buf('folder' in opt ? opt.folder : '_'), 345);
let chksum = 32 * 8;
for(j = 0; j < 512; j++) {
chksum += bloc[j];
}
bloc.set(str2buf(chksum.toString(8)), 148);
offset += 512;
j = arr[i+1].byteLength;
if(!j) continue;
idx.push(offset, j);
j = 512 + j - ((j-1)%512)-1;
bloc = new Uint8Array(buf, offset, j);
bloc.set(arr[i+1], 0);
offset += j;
// debugger;
}
return {tar:buf, idx:idx};
}
/*
append tar
first: object from tarCreate
second: object from tarCreate
*/
export function tarAppend(first, second, opt = {}){
let a, firstLen, secondLen, firstIdx, secondIdx, bufsize, result
if(!first && second) return tarCreate(second, opt)
first = first.tar ? first : tarCreate(fist, opt)
firstIdx = first.idx
first = new Uint8Array(first.tar)
for(a = first.byteLength - 1024; a>=0 ;a--){
if(first[a]) break
}
firstLen = 512 + a - ((a-1)%512) - 1;
second = second.tar ? second : tarCreate(second, opt)
secondIdx = second.idx
second = new Uint8Array(second.tar)
for(a = second.byteLength - 1024; a>=0 ;a--){
if(second[a]) break
}
secondLen = 512 + a - ((a-1)%512) - 1;
bufsize = firstLen + secondLen + 1024
bufsize = 10240 + bufsize - ((bufsize-1)%10240) - 1;
result = new Uint8Array(bufsize)
result.set(first.subarray(0, firstLen))
result.set(second.subarray(0, secondLen), firstLen)
for(a=0; a<secondIdx.length; a+=2){
secondIdx[a] = secondIdx[a] + firstLen
}
firstIdx.push(...secondIdx)
return {tar: result.buffer, idx: firstIdx}
}
/*
create webworker
str: js filepath or string with a main function
*/
export function workerCreate(str = ''){
let worker
if(str.includes('.js')){
worker = new Worker(str)
}
else if(str.includes('main')) {
str += `
self.addEventListener('message', function(e) {
self.postMessage(main(e.data))
});
`
str = new Blob([str2buf(str)])
str = URL.createObjectURL(str)
worker = new Worker(str)
}
else throw new Error('js filepath or string with a main function')
return worker
}
/*
run unit test
tests: {testName: [testFn, expected value]}
timeLog: boolean
*/
export async function assert(tests = {}, timeLog = false){
let line, out, testName, testRes, testFn, expectVal, AsyncFunction
AsyncFunction = Object.getPrototypeOf(async ()=>{}).constructor
out = ''
for(testName in tests){
if(typeof tests[testName] != 'object'){
tests[testName] = [tests[testName]]
}
if(typeof tests[testName][0] == 'string'){
testFn = (tests[testName][0].includes('return') ? '' : 'return await ') + tests[testName][0]
testFn = new AsyncFunction(testFn)
}
else if(typeof tests[testName][0] == 'function'){
testFn = tests[testName][0]
}
else continue
try {
if(timeLog) console.time(testName)
testRes = await testFn()
if(timeLog) console.timeEnd(testName)
if(typeof tests[testName][1] != 'undefined') expectVal = tests[testName][1]
else {
testRes = testRes ? true : false
expectVal = true
}
if(testRes.constructor.name == 'Object'){
testRes = JSON.stringify(testRes)
expectVal = JSON.stringify(expectVal)
}
else{
testRes = testRes.toString()
expectVal = expectVal.toString()
}
if(testRes != expectVal){
throw new Error(testRes + ' != ' + expectVal)
}
line = testName + ': OK' + '\n'
}
catch(e){
line = testName + ': Failed (' + e.message + ')\n'
}
console.log(line)
out += line
}
return out
}
/*
change url without reload
newURL: string
push: boolean
*/
export function changeURL(newURL, push = false){
if(push) history.pushState({}, null, newURL)
else history.replaceState({}, null, newURL);
}
/*
read (if no text) or write clip board
text: string
*/
export async function clipboard(text){
console.log(text)
if(text) await navigator.clipboard.writeText(text)
else await navigator.clipboard.readText()
}
/*
access to file system
handle: expected string(openFile, saveFile, directory, OPFS, clearOPFS) or object [handle]
data: data to write ('-' to delete)
path: file path if directory
*/
export async function fileSys(handle="OPFS", data, path){
async function getSubFile(dirHandle, path, create = false){
let retHandle
path = path.split('/')
retHandle = dirHandle
for(let i=0; ; i++){
if(i < path.length - 1){
retHandle = await retHandle.getDirectoryHandle(path[i], { create })
}
else {
retHandle = await retHandle.getFileHandle(path[i], { create })
break
}
}
return retHandle
}
let ret, handle2
if(handle == "openFile"){
[handle] = await showOpenFilePicker({multiple: false})
ret = await handle.getFile()
}
else if(handle == "saveFile"){
handle = await showSaveFilePicker()
}
else if(handle == "directory"){
handle = await showDirectoryPicker()
}
else if(handle == "OPFS"){
handle = await navigator.storage.getDirectory()
}
else if(handle == "clearOPFS"){
await navigator.storage.getDirectory().then(x=>x.remove({ recursive: true }))
}
else if(typeof handle[0] == "object"){
handle = handle[0]
}
else throw new Error('handle: expected string(openFile, saveFile, directory, OPFS, clearOPFS) or object [handle]')
if(typeof data != 'undefined' && data){
if(handle.constructor.name == 'FileSystemDirectoryHandle'){
if(typeof path == 'undefined') throw new Error('no path for DirectoryHandle')
handle2 = await getSubFile(handle, path, true)
if(data == "-") {
await handle2.remove()
return [handle, ret]
}
}
else handle2 = handle
handle2 = await handle2.createWritable()
await handle2.write(data)
await handle2.close()
}
else if(path && handle.constructor.name == 'FileSystemDirectoryHandle'){
ret = await getSubFile(handle, path).then(x=>x.getFile())
}
else if(handle.constructor.name == 'FileSystemDirectoryHandle'){
ret = []
for await (let [name, fileHandle] of handle) {
if(fileHandle.constructor.name.includes('Directory')){
let list, a
list = (await fileSys([fileHandle]))[1]
for(a in list) list[a] = name + '/' + list[a]
ret.push(name + '/', ...list)
}
else ret.push(name)
}
}
return [handle, ret]
}
/*
fullscreen
element: string element id, or html element
*/
export function fullscreen(elm){
if(typeof id == 'string') document.getElementById(elm).requestFullscreen()
else elm.requestFullscreen()
}
/*
get geo location
*/
export function geolocation(){
return new Promise(r=>{
navigator.geolocation.getCurrentPosition((x)=>r(x))
})
}
/*
byteArray
*/
export function byteArray(n){
class ByteArray{
constructor(n){
this.arr = ArrayBuffer.isView(n) ? n : new Uint8Array(1+((n/8)|0))
}
get(n){
return this.arr[((n/8)|0)] & (1<<(n%8))
}
set(n){
this.arr[((n/8)|0)] |= (1<<(n%8))
}
unset(n){
this.arr[((n/8)|0)] &= ~(1<<(n%8))
}
}
return new ByteArray(n)
}
/*
get object from string object name
*/
export function objectFromString(str, lexicallyScoped = false){
function makeFunction() {
var params = [];
for (var i = 0; i < arguments.length - 1; i++) {
params.push(arguments[i]);
}
var code = arguments[arguments.length - 1];
return eval('[function (' + params.join(',')+ '){' + code + '}][0]');
}
let fn
str = 'return typeof '+str+' == "undefined" || '+str
fn = lexicallyScoped ? makeFunction(str) : new Function(str)
return fn
}
/*
WIP
*/
export async function webCrawl(url, depth = 5){
let a, b, page, webPages, relPath, basePath, m
webPages = {[url] : ''}
relPath = /^.+\//.exec(url)?.at(0)
basePath = /^[^\.]+[^\/]+/.exec(url)?.at(0)
for(a=0; a<depth; a++){
await fetchAll(webPages)
for(b in webPages){
page = webPages[b]
if(!b.match(/css$|js$/) && page.length > 5){
[
/<link[^>]+href ?= ?['"]([^'"]+)['"]/g,
/<script[^>]+src ?= ?['"]([^'"]+)['"]/g,
/<a[^>]+href ?= ?['"]([^#][^'"]+)['"]/g,
].forEach(re=>{
while(m = re.exec(page)){
m = m.at(1)
m = m.includes('http') ? m : m[0] == '/' ? basePath + m : relPath + m
if(!webPages[m]) webPages[m] = ''
}
})
}
webPages[b] = 'xxx'
}
}
return webPages
}
/*
SIMPLE HACK
*/
export function wrapFetch(callback){
if(!fetch.toString().includes('native')) return
let _fetch = fetch
fetch = (...args) => {
callback(args)
return _fetch.call(window, ...args)
}
}
export function wrapEventListener(){
Node.prototype._addEventListener = Node.prototype.addEventListener;
Node.prototype._removeEventListener = Node.prototype.removeEventListener;
Node.prototype.addEventListener = function(a, b, c){
if(!this._eventListeners) this._eventListeners = new Array()
this._eventListeners.push({a, b, c})
this._addEventListener(a, b, c)
};
Node.prototype.removeEventListener = function(a, b, c){
let i
if(!this._eventListeners) return
for(i = this._eventListeners.length-1; i>=0; i--) if(b==this._eventListeners[i].b) break
if(i<0) return
this._eventListeners.splice(i, 1)
this._removeEventListener(a, b, c)
}
}
export function wrapResizeObs(callback, elmId){
if(!callback) callback = (entries) => {
entries.forEach(entry => {
console.log(entry.target, entry.contentRect)
})
}
const observer = new ResizeObserver(callback)
if(elmId) observer.observe(document.getElementById(elmId), { box: 'border-box' })
return observer
}
export function wrapIntersectionObs(callback, elmId){
if(!callback) callback = (entries) => {
entries.forEach(entry => {
console.log(entry.target, entry.isIntersecting)
})
}
const observer = new IntersectionObserver(callback, { threshold: 0 })
if(elmId) observer.observe(document.getElementById(elmId))
return observer
}
export function wrapMutationObs(callback, elmId){
if(!callback) callback = (mutationList) => {
for(let mutation of mutationList){
for(let child of mutation.addedNodes){
console.log(child)
}
}
}
const observer = new MutationObserver(callback)
if(elmId) observer.observe(document.getElementById(elmId), {childList: true, subtree: false})
return observer
}
export function wrapEvent(name, elmId){
let ev = {}
ev.tabev = new KeyboardEvent('keydown', {'key': 'Tab', "keyCode":9, bubbles:true, cancelable:true})
ev.enterev = new KeyboardEvent('keydown', {'key': 'Enter', "keyCode":10, bubbles:true, cancelable:true})
ev.f4ev = new KeyboardEvent('keydown', {'key': 'F4', "keyCode":62, bubbles:true, cancelable:true})
ev.mouseev = new MouseEvent('dblclick', {view:window, bubbles:true, cancelable:true})
document.getElementById(elmId).dispatchEvent(ev[name])
}
export function wrapXHR(callback){
let _XMLHttpRequest = XMLHttpRequest
XMLHttpRequest = new_XMLHttpRequest
if(!callback) callback = ()=>{}
function new_XMLHttpRequest() {
let xhr = new _XMLHttpRequest();
xhr.onreadystatechange = ()=>{}
let _open = xhr.open;
let _send = xhr.send;
let _url = '';
xhr.open = function() {
_url = arguments[1]
return _open.apply(this, arguments);
}
xhr.send = function() {
callback(arguments)
return _send.apply(this, arguments);
}
return xhr;
}
}
/*
BITMAP
*/
/*
BMP HEADER
0h 2 42 4D "BM" ID field (42h, 4Dh)
2h 4 46 00 00 00 70 bytes (54+16) Size of the BMP file (54 bytes header + 16 bytes data)
6h 2 00 00 Unused Application specific
8h 2 00 00 Unused Application specific
Ah 4 36 00 00 00 54 bytes (14+40) Offset where the pixel array (bitmap data) can be found
DIB Header
Eh 4 28 00 00 00 40 bytes Number of bytes in the DIB header (from this point)
12h 4 02 00 00 00 2 pixels (left to right order) Width of the bitmap in pixels
16h 4 02 00 00 00 2 pixels (bottom to top order) Height of the bitmap in pixels. Positive for bottom to top pixel order.
1Ah 2 01 00 1 plane Number of color planes being used
1Ch 2 18 00 24 bits Number of bits per pixel
1Eh 4 00 00 00 00 0 BI_RGB, no pixel array compression used
22h 4 10 00 00 00 16 bytes Size of the raw bitmap data (including padding)
26h 4 13 0B 00 00 2835 pixels/metre horizontal Print resolution of the image,
72 DPI × 39.3701 inches per metre yields 2834.6472
2Ah 4 13 0B 00 00 2835 pixels/metre vertical
2Eh 4 00 00 00 00 0 colors Number of colors in the palette
32h 4 00 00 00 00 0 important colors 0 means all colors are important
Start of pixel array (bitmap data)
36h 3 00 00 FF 0 0 255 Red, Pixel (0,1)
39h 3 FF FF FF 255 255 255 White, Pixel (1,1)
3Ch 2 00 00 0 0 Padding for 4 byte alignment (could be a value other than zero)
3Eh 3 FF 00 00 255 0 0 Blue, Pixel (0,0)
41h 3 00 FF 00 0 255 0 Green, Pixel (1,0)
44h 2 00 00 0 0 Padding for 4 byte alignment (could be a value other than zero)
octets multiple de 4
*/
export function toBMP(tt, nn=""){
var vector;
if(nn){
vector = nn;
if(vector.byteLength != 16) throw 'expect 16 bytes vector';
} else {
vector = new Uint8Array(16);
}
tt = str2buf(tt);
var z;
for(z=1;(tt.length+16)>z*4*z*32;z++);
var w = z*32;
// negative value, top to bottom
var h = 256*256*256*256-w;
var s = 62 + z*4*z*32;
var u8 = new Uint8Array(s);
// a=b=c=x=y=9;
var header = [
0x42,0x4D,
(s>>>0&255),(s>>>8&255),(s>>>16&255),(s>>>24&255),
(tt.length>>>0&255),(tt.length>>>8&255),(tt.length>>>16&255),(tt.length>>>24&255),
0x3E,0,0,0,
0x28,0,0,0,
(w>>>0&255),(w>>>8&255),(w>>>16&255),(w>>>24&255),
(h>>>0&255),(h>>>8&255),(h>>>16&255),(h>>>24&255),
0x01,0,
0x01,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0,
0xFF,0xFF,0xFF,
0,0,0,0,0
];
u8.set(header);
u8.set(vector,62);
u8.set(tt,62+16);
var rd = window.crypto.getRandomValues(new Uint8Array(s-62-16-tt.length));
u8.set(rd,62+16+tt.length);
// console.log(u8);
// var bb = new Blob([u8]);
// dl(bb,'aze.bmp');
// xx=u8;
return u8;
}
export function fromBMP(u8){
if(!(u8 instanceof Uint8Array) || u8.length < 62+16) return u8;
let len = u8[6] + (u8[7]<<8) + (u8[8]<<16) + (u8[9]<<24);
let ret = {};
ret.result = u8.slice(62+16, 62+16+len);
ret.iv = u8.slice(62, 62+16);
return ret;
}
/** STORE **/
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="/js/0utils/simpledropzone.js"></script>
<script>
(async function (){
window._ = await import('/js/0utils/utils3.mjs')
window.cr = await import('/js/0utils/simplewebcrypto.mjs')
simpleDropzone("main")
window.userData = null
window.handleFiles = async function(files){
if(!userData) userData = _.saveUserSettings('','localStorage') || {_part: 0, _tarObj: null, _filenames: []}
let i, path, filenames, tmp
files = await _.readfiles(files)
filenames = Object.keys(files)
path = document.getElementById('path').value
for(i=0; i<filenames.length; i++){
userData._filenames.push(filenames[i])
userData._tarObj = _.tarAppend(userData._tarObj, [filenames[i], files[filenames[i]]])
}
}
})()
</script>
</head>
<body>
<center style="margin: 16px;"><input type="text" id="path" style="width: 100%; max-width: 480px;"></center>
<div id="main"></div>
</body>
</html>