basic navigation for firefox50

Massimo Melina committed Apr 23, 2024 at 14:29 UTC 58d0c04e6dbddbb6c2e0d500504231e123ca7421
15 files changed +74 -37
admin/src/FileForm.ts
+1 -1
@@ -301,7 +301,7 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
301 const { reload, error } = statusApi
302 // workaround to get fresh data and be rerendered even when mounted inside imperative dialog
303 const requestRender = useRequestRender()
304 - useEffect(() => statusApi.sub(requestRender), [])
304 + useEffect(() => { statusApi.sub(requestRender) }, [])
305 const data = statusApi.getData()
306
307 const urls: string[] = data?.urls.https || data?.urls.http
frontend/package.json
+1
@@ -23,6 +23,7 @@
23 "@types/lodash": "^4.14.178",
24 "@types/react": "^18.0.15",
25 "@types/react-dom": "^18.0.6",
26 + "@vitejs/plugin-legacy": "^5.3.2",
27 "cross-env": "^7.0.3",
28 "sass": "^1.54.5",
29 "vite": "*"
frontend/src/App.ts
+6 -1
@@ -9,7 +9,7 @@ import { useSnapState } from './state'
9 import { I18Nprovider } from './i18n'
10 import { proxy, useSnapshot } from "valtio"
11 import { Spinner } from "./components"
12 -import { getHFS, getPrefixUrl } from '@hfs/shared'
12 +import { getHFS, getPrefixUrl, loadScript } from '@hfs/shared'
13 import { Toasts } from './toasts'
14
15 function App() {
@@ -46,3 +46,8 @@ const pageState = proxy({ ready: document.readyState === 'complete' })
46 document.addEventListener('readystatechange', () => {
47 pageState.ready = document.readyState === 'complete'
48 })
49 +
50 +// load plugins' now, as vite-legacy delayed app's loading
51 +for (const [plugin, files] of Object.entries(getHFS().loadScripts))
52 + if (Array.isArray(files)) for (const f of files)
53 + loadScript(f, { plugin })
\ No newline at end of file
frontend/src/i18n.ts
+2 -1
@@ -64,7 +64,8 @@ export function t(keyOrTpl: string | string[] | TemplateStringsArray, params?: a
64 }).join('')
65
66 function plural(v: any, rest: string) {
67 - const plural = new Intl.PluralRules(selectedLang || embedded).select(Number(v))
67 + const plural = !Intl.PluralRules ? 'other'
68 + : new Intl.PluralRules(selectedLang || embedded).select(Number(v))
69 let other = ''
70 let pickNext = false
71 let collectOther = false
frontend/src/icons.ts
+1 -1
@@ -47,7 +47,7 @@ const SYS_ICONS: Record<string, [string] | [string, string | false]> = { // fals
47 warning: ['⚠️', false],
48 }
49
50 -document.fonts.ready.then(async ()=> {
50 +document.fonts?.ready.then(async ()=> {
51 const fontTester = '9px fontello'
52 await document.fonts.load(fontTester) // force font to be loaded even if we didn't display anything with it yet
53 state.iconsReady = document.fonts.check(fontTester)
frontend/src/index.ts
+1 -1
@@ -4,7 +4,7 @@ import { createElement as h, StrictMode } from 'react'
4 import { createRoot } from 'react-dom/client'
5 import './index.scss'
6 import '@hfs/shared/min-crypto-polyfill'
7 -import '@hfs/shared/chrome69-polyfill'
7 +import '@hfs/shared/polyfills'
8 import App from './App'
9 import { disableConsoleDebug } from '@hfs/shared'
10
frontend/src/menu.ts
+4 -3
@@ -3,7 +3,8 @@
3 import { state, useSnapState } from './state'
4 import { createElement as h, Fragment, useEffect, useMemo, useState } from 'react'
5 import { alertDialog, confirmDialog, ConfirmOptions, promptDialog, toast } from './dialog'
6 -import { defaultPerms, err2msg, ErrorMsg, onlyTruthy, prefix, throw_, useStateMounted, VfsPerms, working } from './misc'
6 +import { defaultPerms, err2msg, ErrorMsg, onlyTruthy, prefix, throw_, useStateMounted, VfsPerms, working,
7 + buildUrlQueryString} from './misc'
8 import { loginDialog } from './login'
9 import { showOptions } from './options'
10 import showUserPanel from './UserPanel'
@@ -96,11 +97,11 @@ export function MenuPanel() {
97 disabled: !can_archive,
98 tooltip: list ? t('zip_tooltip_selected', "Download selected elements as a single zip file")
99 : t('zip_tooltip_whole', "Download whole list (unfiltered) as a single zip file. If you select some elements, only those will be downloaded."),
99 - href: '?'+String(new URLSearchParams(_.pickBy({
100 + href: buildUrlQueryString(_.pickBy({
101 get: 'zip',
102 search: remoteSearch,
103 list
103 - }))),
104 + })),
105 ...!list && {
106 confirm: remoteSearch ? t('zip_confirm_search', "Download ALL results of this search as ZIP archive?")
107 : t('zip_confirm_folder', "Download WHOLE folder as ZIP archive?"),
frontend/src/show.ts
+9 -5
@@ -85,8 +85,8 @@ export function fileShow(entry: DirEntry, { startPlaying=false } = {}) {
85 return () => clearTimeout(h)
86 }, [autoPlaying, cur])
87 const {mediaSession} = navigator
88 - mediaSession.setActionHandler('nexttrack', goNext)
89 - mediaSession.setActionHandler('previoustrack', goPrev)
88 + mediaSession?.setActionHandler('nexttrack', goNext)
89 + mediaSession?.setActionHandler('previoustrack', goPrev)
90
91 const {t} = useI18N()
92 const autoPlaySecondsLabel = t('autoplay_seconds', "Seconds to wait on images")
@@ -154,13 +154,17 @@ export function fileShow(entry: DirEntry, { startPlaying=false } = {}) {
154 const covers = state.list.filter(x => folder === dirname(x.n) // same folder
155 && x.name.match(/(?:folder|cover|albumart.*)\.jpe?g$/i))
156 setCover(_.maxBy(covers, 's')?.n || '')
157 - const meta = navigator.mediaSession.metadata = new MediaMetadata({
157 + const meta = {
158 title: cur.name,
159 album: decodeURIComponent(basename(dirname(cur.uri))),
160 artwork: covers.map(x => ({ src: x.n }))
161 - })
162 - if (cur.ext === 'mp3')
161 + }
162 + if (window.MediaMetadata)
163 + navigator.mediaSession.metadata = new MediaMetadata(meta)
164 + if (cur.ext === 'mp3') {
165 setTags(Object.assign(meta, await getId3Tags(location + cur.n).catch(() => {})))
166 + Object.assign(navigator.mediaSession?.metadata || {}, meta)
167 + }
168 }
169 }),
170 tags && h('div', { className: 'meta-tags' },
frontend/src/upload.ts
+3 -2
@@ -4,7 +4,8 @@ import { createElement as h, DragEvent, Fragment, useMemo, CSSProperties, useSta
4 import { Btn, Flex, FlexV, iconBtn, Select } from './components'
5 import {
6 basename, closeDialog, formatBytes, formatPerc, hIcon, useIsMobile, newDialog, prefix, selectFiles, working,
7 - HTTP_CONFLICT, HTTP_PAYLOAD_TOO_LARGE, formatSpeed, dirname, getHFS, onlyTruthy, with_, cpuSpeedIndex
7 + HTTP_CONFLICT, HTTP_PAYLOAD_TOO_LARGE, formatSpeed, dirname, getHFS, onlyTruthy, with_, cpuSpeedIndex,
8 + buildUrlQueryString,
9 } from './misc'
10 import _ from 'lodash'
11 import { INTERNAL_Snapshot, proxy, ref, snapshot, subscribe, useSnapshot } from 'valtio'
@@ -335,7 +336,7 @@ async function startUpload(toUpload: ToUpload, to: string, resume=0) {
336 let uploadPath = path(toUpload.file)
337 if (toUpload.name)
338 uploadPath = prefix('', dirname(uploadPath), '/') + toUpload.name
338 - req.open('PUT', to + encodeURIComponent(uploadPath) + '?' + new URLSearchParams({
339 + req.open('PUT', to + encodeURIComponent(uploadPath) + buildUrlQueryString({
340 notificationChannel,
341 ...resume && { resume: String(resume) },
342 ...toUpload.comment && { comment: toUpload.comment },
frontend/vite.config.ts
+9 -1
@@ -1,4 +1,5 @@
1 import { defineConfig } from 'vite'
2 +import legacy from '@vitejs/plugin-legacy'
3
4 // https://vitejs.dev/config/
5 export default defineConfig({
@@ -18,5 +19,12 @@ export default defineConfig({
19 ws: true,
20 }
21 }
21 - }
22 + },
23 + plugins: [
24 + legacy({
25 + renderModernChunks: false, // single version, legacy one
26 + polyfills: false, // keeping polyfills at a minimum, manually
27 + targets: ['firefox 40'],
28 + }),
29 + ],
30 })
shared/api.ts
+4 -3
@@ -2,7 +2,8 @@
2
3 import _ from 'lodash';
4 import { useCallback, useEffect, useMemo, useRef } from 'react';
5 -import { Dict, EventEmitter, Falsy, getPrefixUrl, pendingPromise, useStateMounted, wait } from '.'
5 +import { Callback, Dict, EventEmitter, Falsy, getPrefixUrl, pendingPromise, useStateMounted, wait,
6 + buildUrlQueryString, } from '.'
7
8 export const API_URL = '/~/api/'
9
@@ -112,7 +113,7 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object, options: Api
113 reloadingRef.current = pendingPromise()
114 }, [setForcer])
115 const ee = useMemo(() => new EventEmitter, [])
115 - const sub = useCallback((cb: EventListener) => ee.on('data', cb), [])
116 + const sub = useCallback((cb: Callback) => ee.on('data', cb), [])
117 useEffect(() => ee.emit('data'), [data])
118 return { data, setData, error, reload, sub, loading: loadingRef.current || reloadingRef.current, getData: () => dataRef.current, }
119 }
@@ -122,7 +123,7 @@ type EventHandler = (type:string, data?:any) => void
123 export function apiEvents(cmd: string, params: Dict, cb:EventHandler) {
124 params = _.omitBy(params, _.isUndefined)
125 console.debug('API EVENTS', cmd, params)
125 - const source = new EventSource(getPrefixUrl() + API_URL + cmd + '?' + new URLSearchParams(params))
126 + const source = new EventSource(getPrefixUrl() + API_URL + cmd + buildUrlQueryString(params))
127 source.onopen = () => cb('connected')
128 source.onerror = err => cb('error', err)
129 source.onmessage = ({ data }) => {
shared/chrome69-polyfill.js deleted
-8
@@ -1,8 +0,0 @@
1 -/*
2 - * Polyfill service v3.111.0
3 - * Disable minification (remove `.min` from URL path) for more info
4 -*/
5 -
6 -try {
7 - (function(self, undefined) {Object.defineProperty(Symbol,"matchAll",{value:Symbol("matchAll")});function AdvanceStringIndex(e,n,t){if(n>Number.MAX_SAFE_INTEGER)throw new TypeError("Assertion failed: `index` must be <= 2**53");return!1===t?n+1:n+1>=e.length?n+1:n+e.codePointAt(n).length}function Call(t,l){var n=arguments.length>2?arguments[2]:[];if(!1===IsCallable(t))throw new TypeError(Object.prototype.toString.call(t)+"is not a function.");return t.apply(l,n)}function CreateDataProperty(e,r,t){var a={value:t,writable:!0,enumerable:!0,configurable:!0};try{return Object.defineProperty(e,r,a),!0}catch(n){return!1}}function CreateDataPropertyOrThrow(t,r,o){var e=CreateDataProperty(t,r,o);if(!e)throw new TypeError("Cannot assign value `"+Object.prototype.toString.call(o)+"` to property `"+Object.prototype.toString.call(r)+"` on object `"+Object.prototype.toString.call(t)+"`");return e}function CreateMethodProperty(e,r,t){var a={value:t,writable:!0,enumerable:!1,configurable:!0};Object.defineProperty(e,r,a)}function Get(n,t){return n[t]}function IsCallable(n){return"function"==typeof n}function RequireObjectCoercible(e){if(null===e||e===undefined)throw TypeError(Object.prototype.toString.call(e)+" is not coercible to Object.");return e}function ToBoolean(o){return Boolean(o)}function ToNumber(r){return Number(r)}function ToIntegerOrInfinity(n){var i=ToNumber(n);if(isNaN(i)||0===i||1/i==-Infinity)return 0;if(i===Infinity)return Infinity;if(i===-Infinity)return-Infinity;var r=Math.floor(Math.abs(i));return i<0&&(r=-r),r}function ToObject(e){if(null===e||e===undefined)throw TypeError();return Object(e)}function GetV(t,e){return ToObject(t)[e]}function GetMethod(e,n){var r=GetV(e,n);if(null===r||r===undefined)return undefined;if(!1===IsCallable(r))throw new TypeError("Method not callable: "+n);return r}function Invoke(n,e){var t=arguments.length>2?arguments[2]:[],l=GetV(n,e);return Call(l,n,t)}function Type(e){switch(typeof e){case"undefined":return"undefined";case"boolean":return"boolean";case"number":return"number";case"string":return"string";case"symbol":return"symbol";default:return null===e?"null":"Symbol"in self&&(e instanceof self.Symbol||e.constructor===self.Symbol)?"symbol":"object"}}function CreateIterResultObject(e,r){if("boolean"!==Type(r))throw new Error;var t={};return CreateDataProperty(t,"value",e),CreateDataProperty(t,"done",r),t}function GetIterator(t){var e=arguments.length>1?arguments[1]:GetMethod(t,Symbol.iterator),r=Call(e,t);if("object"!==Type(r))throw new TypeError("bad iterator");var o=GetV(r,"next"),a=Object.create(null);return a["[[Iterator]]"]=r,a["[[NextMethod]]"]=o,a["[[Done]]"]=!1,a}function GetPrototypeFromConstructor(t,o){var r=Get(t,"prototype");return"object"!==Type(r)&&(r=o),r}function OrdinaryCreateFromConstructor(r,e){var t=arguments[2]||{},o=GetPrototypeFromConstructor(r,e),a=Object.create(o);for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&Object.defineProperty(a,n,{configurable:!0,enumerable:!1,writable:!0,value:t[n]});return a}var GetSubstitution=function(){function e(e){return/^[0-9]$/.test(e)}return function t(n,r,l,i,a,f){for(var s=n.length,h=r.length,c=l+s,u=i.length,v="",g=0;g<f.length;g+=1){var o=f.charAt(g),$=g+1>=f.length,d=g+2>=f.length;if("$"!==o||$)v+=f.charAt(g);else{var p=f.charAt(g+1);if("$"===p)v+="$",g+=1;else if("&"===p)v+=n,g+=1;else if("`"===p)v+=0===l?"":r.slice(0,l-1),g+=1;else if("'"===p)v+=c>=h?"":r.slice(c),g+=1;else{var A=d?null:f.charAt(g+2);if(!e(p)||"0"===p||!d&&e(A))if(e(p)&&(d||e(A))){var y=p+A,I=parseInt(y,10)-1;v+=y<=u&&"Undefined"===Type(i[I])?"":i[I],g+=2}else v+="$";else{var T=parseInt(p,10);v+=T<=u&&"Undefined"===Type(i[T-1])?"":i[T-1],g+=1}}}}return v}}();function IsConstructor(t){return"object"===Type(t)&&("function"==typeof t&&!!t.prototype)}function Construct(r){var t=arguments.length>2?arguments[2]:r,o=arguments.length>1?arguments[1]:[];if(!IsConstructor(r))throw new TypeError("F must be a constructor.");if(!IsConstructor(t))throw new TypeError("newTarget must be a constructor.");if(t===r)return new(Function.prototype.bind.apply(r,[null].concat(o)));var n=OrdinaryCreateFromConstructor(t,Object.prototype);return Call(r,n,o)}function IsRegExp(e){if("object"!==Type(e))return!1;var n="Symbol"in self&&"match"in self.Symbol?Get(e,self.Symbol.match):undefined;if(n!==undefined)return ToBoolean(n);try{var t=e.lastIndex;return e.lastIndex=0,RegExp.prototype.exec.call(e),!0}catch(l){}finally{e.lastIndex=t}return!1}function IteratorClose(r,t){if("object"!==Type(r["[[Iterator]]"]))throw new Error(Object.prototype.toString.call(r["[[Iterator]]"])+"is not an Object.");var e=r["[[Iterator]]"],o=GetMethod(e,"return");if(o===undefined)return t;try{var n=Call(o,e)}catch(c){var a=c}if(t)return t;if(a)throw a;if("object"!==Type(n))throw new TypeError("Iterator's return method returned a non-object.");return t}function IteratorComplete(t){if("object"!==Type(t))throw new Error(Object.prototype.toString.call(t)+"is not an Object.");return ToBoolean(Get(t,"done"))}function IteratorNext(t){if(arguments.length<2)var e=Call(t["[[NextMethod]]"],t["[[Iterator]]"]);else e=Call(t["[[NextMethod]]"],t["[[Iterator]]"],[arguments[1]]);if("object"!==Type(e))throw new TypeError("bad iterator");return e}function IteratorStep(t){var r=IteratorNext(t);return!0!==IteratorComplete(r)&&r}function IteratorValue(t){if("object"!==Type(t))throw new Error(Object.prototype.toString.call(t)+"is not an Object.");return Get(t,"value")}var AddEntriesFromIterable=function(){var r={}.toString,t="".split;return function e(a,o,n){if(!1===IsCallable(n))throw new TypeError("adder is not callable.");for(var l=GetIterator(o);;){var c=IteratorStep(l);if(!1===c)return a;var i=IteratorValue(c);if("object"!==Type(i)){var s=new TypeError("nextItem is not an object");throw IteratorClose(l,s),s}i=("string"===Type(i)||i instanceof String)&&"[object String]"==r.call(i)?t.call(i,""):i;var I;try{I=Get(i,"0")}catch(I){return IteratorClose(l,I)}var u;try{u=Get(i,"1")}catch(u){return IteratorClose(l,u)}try{Call(n,a,[I,u])}catch(v){return IteratorClose(l,v)}}}}();function IterableToList(t){for(var r=arguments.length>1?GetIterator(t,arguments[1]):GetIterator(t),e=[],a=!0;!1!==a;)if(!1!==(a=IteratorStep(r))){var o=IteratorValue(a);e.push(o)}return e}!function(){function r(r,e){var t=void 0===e?new Error:new Error(e);CreateDataPropertyOrThrow(this,"name","AggregateError"),CreateDataPropertyOrThrow(this,"message",t.message),CreateDataPropertyOrThrow(this,"stack",t.stack);var o;if(Array.isArray(r))o=r.slice();else try{o=IterableToList(r)}catch(a){throw new TypeError("Argument is not iterable")}CreateDataPropertyOrThrow(this,"errors",o)}r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,self.AggregateError=r}();!function(){CreateMethodProperty(Promise,"allSettled",function e(r){var t=this;if("object"!==Type(t))throw new TypeError("`this` value must be an object");var n;if(Array.isArray(r))n=r;else try{n=IterableToList(r)}catch(o){return Promise.reject(new TypeError("Argument of Promise.allSettled is not iterable"))}var a=n.map(function(e){var r=function(e){return{status:"fulfilled",value:e}},n=function(e){return{status:"rejected",reason:e}},a=t.resolve(e);try{return a.then(r,n)}catch(o){return t.reject(o)}});return t.all(a)})}();!function(){var r=function(r){return r};CreateMethodProperty(Promise,"any",function e(t){var n=this;if("object"!==Type(n))throw new TypeError("`this` value must be an object");var o;if(Array.isArray(t))o=t;else try{o=IterableToList(t)}catch(c){return Promise.reject(new TypeError("Argument of Promise.any is not iterable"))}var a=function(r){return n.reject(r)},i=o.map(function(e){var t=n.resolve(e);try{return t.then(a,r)}catch(o){return o}});return n.all(i).then(function(r){throw new AggregateError(r,"Every promise rejected")},r)})}();function OrdinaryToPrimitive(r,t){if("string"===t)var e=["toString","valueOf"];else e=["valueOf","toString"];for(var i=0;i<e.length;++i){var n=e[i],a=Get(r,n);if(IsCallable(a)){var o=Call(a,r);if("object"!==Type(o))return o}}throw new TypeError("Cannot convert to primitive.")}function RegExpExec(e,l){var r=Get(e,"exec");if(IsCallable(r)){var t=Call(r,e,[l]);if("object"!==Type(t)&&"null"!==Type(t))throw new TypeError("Invalid result: must be an object or null.");return t}return Call(RegExp.prototype.exec,e,[l])}function SpeciesConstructor(e,o){var r=Get(e,"constructor");if(r===undefined)return o;if("object"!==Type(r))throw new TypeError("O.constructor is not an Object");var n="function"==typeof self.Symbol&&"symbol"==typeof self.Symbol.species?r[self.Symbol.species]:undefined;if(n===undefined||null===n)return o;if(IsConstructor(n))return n;throw new TypeError("No constructor found")}function StringIndexOf(r,n,e){var f=r.length;if(""===n&&e<=f)return e;for(var t=n.length,a=e,i=-1;a+t<=f;){for(var g=!0,o=0;o<t;o+=1)if(r[a+o]!==n[o]){g=!1;break}if(g){i=a;break}a+=1}return i}function ToInteger(n){if("symbol"===Type(n))throw new TypeError("Cannot convert a Symbol value to a number");var t=Number(n);return isNaN(t)?0:1/t===Infinity||1/t==-Infinity||t===Infinity||t===-Infinity?t:(t<0?-1:1)*Math.floor(Math.abs(t))}function ToLength(n){var t=ToInteger(n);return t<=0?0:Math.min(t,Math.pow(2,53)-1)}function LengthOfArrayLike(e){return ToLength(Get(e,"length"))}function ToPrimitive(e){var t=arguments.length>1?arguments[1]:undefined;if("object"===Type(e)){if(arguments.length<2)var i="default";else t===String?i="string":t===Number&&(i="number");var r="function"==typeof self.Symbol&&"symbol"==typeof self.Symbol.toPrimitive?GetMethod(e,self.Symbol.toPrimitive):undefined;if(r!==undefined){var n=Call(r,e,[i]);if("object"!==Type(n))return n;throw new TypeError("Cannot convert exotic object to primitive.")}return"default"===i&&(i="number"),OrdinaryToPrimitive(e,i)}return e}function ToString(t){switch(Type(t)){case"symbol":throw new TypeError("Cannot convert a Symbol value to a string");case"object":return ToString(ToPrimitive(t,String));default:return String(t)}}CreateMethodProperty(Array.prototype,"at",function t(e){var r=ToObject(this),n=LengthOfArrayLike(r),o=ToIntegerOrInfinity(e),i=o>=0?o:n+o;return i<0||i>=n?undefined:Get(r,ToString(i))});CreateMethodProperty(String.prototype,"at",function e(t){var r=RequireObjectCoercible(this),n=ToString(r),i=n.length,o=ToIntegerOrInfinity(t),g=o>=0?o:i+o;return g<0||g>=i?undefined:n.substring(g,g+1)});CreateMethodProperty(String.prototype,"replaceAll",function e(r,t){"use strict";var n=RequireObjectCoercible(this);if(r!==undefined&&null!==r){if(IsRegExp(r)){var i=Get(r,"flags");if(!("flags"in RegExp.prototype||!0===r.global))throw TypeError("");if("flags"in RegExp.prototype&&(RequireObjectCoercible(i),-1===ToString(i).indexOf("g")))throw TypeError("")}var l="Symbol"in self&&"replace"in self.Symbol?GetMethod(r,self.Symbol.replace):undefined;if(l!==undefined)return Call(l,r,[n,t])}var o=ToString(n),a=ToString(r),f=IsCallable(t);!1===f&&(t=ToString(t));for(var g=a.length,s=Math.max(1,g),u=[],p=StringIndexOf(o,a,0);-1!==p;)u.push(p),p=StringIndexOf(o,a,p+s);for(var d=0,b="",S=0;S<u.length;S++){var h=o.substring(d,u[S]);if(f)var c=ToString(Call(t,undefined,[a,u[S],o]));else{var v=[];c=GetSubstitution(a,o,u[S],v,undefined,t)}b=b+h+c,d=u[S]+g}return d<o.length&&(b+=o.substring(d)),b});!function(){function t(t,r){return e?t[r]:t._getter(r)}function r(r){var e=this,o=e.length,a=ToIntegerOrInfinity(r),p=a>=0?a:o+a;return p<0||p>=o?undefined:t(e,ToString(p))}var e=function(){var t=new Uint8Array(2);return t[0]=42,42===t[0]};"__proto__"in self.Int8Array.prototype?CreateMethodProperty(self.Int8Array.prototype.__proto__,"at",r):(CreateMethodProperty(self.Int8Array.prototype,"at",r),CreateMethodProperty(self.Uint8Array.prototype,"at",r),CreateMethodProperty(self.Uint8ClampedArray.prototype,"at",r),CreateMethodProperty(self.Int16Array.prototype,"at",r),CreateMethodProperty(self.Uint16Array.prototype,"at",r),CreateMethodProperty(self.Int32Array.prototype,"at",r),CreateMethodProperty(self.Uint32Array.prototype,"at",r),CreateMethodProperty(self.Float32Array.prototype,"at",r),CreateMethodProperty(self.Float64Array.prototype,"at",r))}();function CreateRegExpStringIterator(e,t,r,n){var a={};return CreateMethodProperty(a,"next",function i(){if(!0===this["[[Done]]"])return CreateIterResultObject(undefined,!0);var a=RegExpExec(e,t);if(null===a)return this["[[Done]]"]=!0,CreateIterResultObject(undefined,!0);if(!1===r){var i=CreateIterResultObject(a,!1);return this["[[Done]]"]=!0,i}if(""===ToString(Get(a,"0"))){var o=ToLength(Get(e,"lastIndex")),u=AdvanceStringIndex(t,o,n);e.lastIndex=u}return CreateIterResultObject(a,!1)}),Object.defineProperty(a,Symbol.toStringTag,{configurable:!0,enumerable:!1,writable:!1,value:"RegExp String Iterator"}),CreateMethodProperty(a,Symbol.iterator,function o(){return this}),a}var supportsRegexpLiteralConstructorWithFlags=function(){try{return new RegExp(/x/,"g"),!0}catch(t){return!1}}();CreateMethodProperty(RegExp.prototype,Symbol.matchAll,function(t){"use strict";var e=this;if("object"!==Type(e))throw new TypeError("Method called on incompatible type: must be an object.");var r=ToString(t),o=SpeciesConstructor(e,RegExp),n=ToString(Get(e,"flags"));"flags"in RegExp.prototype||(n="",!0===e.global&&(n+="g"),!0===e.ignoreCase&&(n+="i"),!0===e.multiline&&(n+="m"));var a=Construct(o,[supportsRegexpLiteralConstructorWithFlags?e:e.source,n]),i=ToLength(Get(e,"lastIndex"));a.lastIndex=i;var p=n.indexOf("g")>-1,s=n.indexOf("u")>-1;return CreateRegExpStringIterator(a,r,p,s)});CreateMethodProperty(String.prototype,"matchAll",function e(l){"use strict";var r=RequireObjectCoercible(this);if(l!==undefined&&null!==l){if(IsRegExp(l)){var t=Get(l,"flags");if(!("flags"in RegExp.prototype||!0===l.global))throw TypeError("");if("flags"in RegExp.prototype&&(RequireObjectCoercible(t),-1===ToString(t).indexOf("g")))throw TypeError("")}var o="Symbol"in self&&"matchAll"in self.Symbol?GetMethod(l,self.Symbol.matchAll):undefined;if(o!==undefined)return Call(o,l,[r])}var i=ToString(r),n=new RegExp(l,"g");return Invoke(n,"Symbol"in self&&"matchAll"in self.Symbol&&self.Symbol.matchAll,[i])});function ToPropertyKey(r){var i=ToPrimitive(r,String);return"symbol"===Type(i)?i:ToString(i)}CreateMethodProperty(Object,"fromEntries",function r(e){RequireObjectCoercible(e);var t={},o=function(r,e){var t=this,o=ToPropertyKey(r);CreateDataPropertyOrThrow(t,o,e)};return AddEntriesFromIterable(t,e,o)});})('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
8 -} catch {}
shared/index.ts
+24 -6
@@ -2,7 +2,7 @@
2
3 import _ from 'lodash'
4 import { apiCall } from './api'
5 -import { DAY, HOUR, MINUTE, objSameKeys, typedEntries } from '../src/cross'
5 +import { Callback, DAY, Dict, getOrSet, HOUR, MINUTE, objSameKeys, typedEntries } from '../src/cross'
6 export * from './react'
7 export * from './dialogs'
8 export * from './md'
@@ -36,6 +36,10 @@ function getScriptAttr(k: string) {
36
37 export const urlParams = Object.fromEntries(new URLSearchParams(window.location.search).entries())
38
39 +export function buildUrlQueryString(params: Dict) { // not using URLSearchParams.toString as it doesn't work on firefox50
40 + return '?' + Object.entries(params).map(pair => pair.map(encodeURIComponent).join('=') ).join('&')
41 +}
42 +
43 export function domOn<K extends keyof WindowEventMap>(eventName: K, cb: (ev: WindowEventMap[K]) => void, { target=window }={}) {
44 target.addEventListener(eventName, cb)
45 return () => target.removeEventListener(eventName, cb)
@@ -115,6 +119,15 @@ export function disableConsoleDebug() {
119 console.debug = (...args) => (window as any).DEV && was(...args)
120 }
121
122 +export function loadScript(url: string, more={}) {
123 + const el = document.createElement('script')
124 + el.type = 'text/javascript'
125 + el.src = url
126 + for (const [k,v] of Object.entries(more))
127 + el.setAttribute(k, String(v))
128 + document.head.appendChild(el)
129 +}
130 +
131 type DurationUnit = 'day' | 'hour' | 'minute' | 'second'
132 export function createDurationFormatter({ locale=undefined, unitDisplay='narrow', largest='day', smallest='second', maxTokens, skipZeroes }:
133 { skipZeroes?: boolean, largest?: DurationUnit, smallest?: DurationUnit, locale?: string, unitDisplay?: 'long' | 'short' | 'narrow', maxTokens?: 1 | 2 | 3 }={}) {
@@ -140,11 +153,16 @@ export function createDurationFormatter({ locale=undefined, unitDisplay='narrow'
153 }
154 }
155
143 -export class EventEmitter extends EventTarget {
144 - emit(name: string) { this.dispatchEvent(new Event(name)) }
145 - on(name: string, cb: EventListener) {
146 - this.addEventListener(name, cb)
147 - return () => this.removeEventListener(name, cb)
156 +// basic event emitter without names
157 +export class EventEmitter {
158 + listeners = {} as Record<string, Callback[]>
159 + emit(name: string, ...args: any[]) {
160 + for (const cb of this.listeners[name] || []) cb(...args)
161 + }
162 + on(name: string, cb: Callback) {
163 + const q = getOrSet(this.listeners, name, () => [])
164 + q.push(cb)
165 + return () => _.pull(q, cb)
166 }
167 }
168
shared/polyfills.js new
+8
@@ -0,0 +1,8 @@
1 +window.BigInt ||= Number; // avoid crash at boot for chrome66
2 +
3 +//https://polyfill.io/v3/polyfill.min.js?features=Object.fromEntries%2CArray.prototype.flat%2CPromise.prototype.finally
4 +/*
5 + * Polyfill service v3.111.0
6 + * Disable minification (remove `.min` from URL path) for more info
7 +*/
8 +(function(self, undefined) {function ArrayCreate(r){if(1/r==-Infinity&&(r=0),r>Math.pow(2,32)-1)throw new RangeError("Invalid array length");var n=[];return n.length=r,n}function Call(t,l){var n=arguments.length>2?arguments[2]:[];if(!1===IsCallable(t))throw new TypeError(Object.prototype.toString.call(t)+"is not a function.");return t.apply(l,n)}function CreateDataProperty(e,r,t){var a={value:t,writable:!0,enumerable:!0,configurable:!0};try{return Object.defineProperty(e,r,a),!0}catch(n){return!1}}function CreateDataPropertyOrThrow(t,r,o){var e=CreateDataProperty(t,r,o);if(!e)throw new TypeError("Cannot assign value `"+Object.prototype.toString.call(o)+"` to property `"+Object.prototype.toString.call(r)+"` on object `"+Object.prototype.toString.call(t)+"`");return e}function CreateMethodProperty(e,r,t){var a={value:t,writable:!0,enumerable:!1,configurable:!0};Object.defineProperty(e,r,a)}function Get(n,t){return n[t]}function HasProperty(n,r){return r in n}function IsArray(r){return"[object Array]"===Object.prototype.toString.call(r)}function IsCallable(n){return"function"==typeof n}function RequireObjectCoercible(e){if(null===e||e===undefined)throw TypeError(Object.prototype.toString.call(e)+" is not coercible to Object.");return e}function ToBoolean(o){return Boolean(o)}function ToObject(e){if(null===e||e===undefined)throw TypeError();return Object(e)}function GetV(t,e){return ToObject(t)[e]}function GetMethod(e,n){var r=GetV(e,n);if(null===r||r===undefined)return undefined;if(!1===IsCallable(r))throw new TypeError("Method not callable: "+n);return r}function Type(e){switch(typeof e){case"undefined":return"undefined";case"boolean":return"boolean";case"number":return"number";case"string":return"string";case"symbol":return"symbol";default:return null===e?"null":"Symbol"in self&&(e instanceof self.Symbol||e.constructor===self.Symbol)?"symbol":"object"}}function GetIterator(t){var e=arguments.length>1?arguments[1]:GetMethod(t,Symbol.iterator),r=Call(e,t);if("object"!==Type(r))throw new TypeError("bad iterator");var o=GetV(r,"next"),a=Object.create(null);return a["[[Iterator]]"]=r,a["[[NextMethod]]"]=o,a["[[Done]]"]=!1,a}function GetPrototypeFromConstructor(t,o){var r=Get(t,"prototype");return"object"!==Type(r)&&(r=o),r}function OrdinaryCreateFromConstructor(r,e){var t=arguments[2]||{},o=GetPrototypeFromConstructor(r,e),a=Object.create(o);for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&Object.defineProperty(a,n,{configurable:!0,enumerable:!1,writable:!0,value:t[n]});return a}function IsConstructor(t){return"object"===Type(t)&&("function"==typeof t&&!!t.prototype)}function Construct(r){var t=arguments.length>2?arguments[2]:r,o=arguments.length>1?arguments[1]:[];if(!IsConstructor(r))throw new TypeError("F must be a constructor.");if(!IsConstructor(t))throw new TypeError("newTarget must be a constructor.");if(t===r)return new(Function.prototype.bind.apply(r,[null].concat(o)));var n=OrdinaryCreateFromConstructor(t,Object.prototype);return Call(r,n,o)}function ArraySpeciesCreate(e,r){if(0===r&&1/r==-Infinity&&(r=0),!1===IsArray(e))return ArrayCreate(r);var n=Get(e,"constructor");if("object"===Type(n)&&null===(n="Symbol"in self&&"species"in self.Symbol?Get(n,self.Symbol.species):undefined)&&(n=undefined),n===undefined)return ArrayCreate(r);if(!IsConstructor(n))throw new TypeError("C must be a constructor");return Construct(n,[r])}function IteratorClose(r,t){if("object"!==Type(r["[[Iterator]]"]))throw new Error(Object.prototype.toString.call(r["[[Iterator]]"])+"is not an Object.");var e=r["[[Iterator]]"],o=GetMethod(e,"return");if(o===undefined)return t;try{var n=Call(o,e)}catch(c){var a=c}if(t)return t;if(a)throw a;if("object"!==Type(n))throw new TypeError("Iterator's return method returned a non-object.");return t}function IteratorComplete(t){if("object"!==Type(t))throw new Error(Object.prototype.toString.call(t)+"is not an Object.");return ToBoolean(Get(t,"done"))}function IteratorNext(t){if(arguments.length<2)var e=Call(t["[[NextMethod]]"],t["[[Iterator]]"]);else e=Call(t["[[NextMethod]]"],t["[[Iterator]]"],[arguments[1]]);if("object"!==Type(e))throw new TypeError("bad iterator");return e}function IteratorStep(t){var r=IteratorNext(t);return!0!==IteratorComplete(r)&&r}function IteratorValue(t){if("object"!==Type(t))throw new Error(Object.prototype.toString.call(t)+"is not an Object.");return Get(t,"value")}var AddEntriesFromIterable=function(){var r={}.toString,t="".split;return function e(a,o,n){if(!1===IsCallable(n))throw new TypeError("adder is not callable.");for(var l=GetIterator(o);;){var c=IteratorStep(l);if(!1===c)return a;var i=IteratorValue(c);if("object"!==Type(i)){var s=new TypeError("nextItem is not an object");throw IteratorClose(l,s),s}i=("string"===Type(i)||i instanceof String)&&"[object String]"==r.call(i)?t.call(i,""):i;var I;try{I=Get(i,"0")}catch(I){return IteratorClose(l,I)}var u;try{u=Get(i,"1")}catch(u){return IteratorClose(l,u)}try{Call(n,a,[I,u])}catch(v){return IteratorClose(l,v)}}}}();function OrdinaryToPrimitive(r,t){if("string"===t)var e=["toString","valueOf"];else e=["valueOf","toString"];for(var i=0;i<e.length;++i){var n=e[i],a=Get(r,n);if(IsCallable(a)){var o=Call(a,r);if("object"!==Type(o))return o}}throw new TypeError("Cannot convert to primitive.")}function SpeciesConstructor(e,o){var r=Get(e,"constructor");if(r===undefined)return o;if("object"!==Type(r))throw new TypeError("O.constructor is not an Object");var n="function"==typeof self.Symbol&&"symbol"==typeof self.Symbol.species?r[self.Symbol.species]:undefined;if(n===undefined||null===n)return o;if(IsConstructor(n))return n;throw new TypeError("No constructor found")}!function(){var t=Function.prototype.bind.call(Function.prototype.call,Promise.prototype.then),o=function(t,o){return new t(function(t){t(o())})};CreateMethodProperty(Promise.prototype,"finally",function(e){var r=this;if("object"!==Type(r))throw new TypeError("Method %PromisePrototype%.finally called on incompatible receiver "+Object.prototype.toString.call(r));var n=SpeciesConstructor(r,Promise);if(!1===IsCallable(e))var i=e,c=e;else i=function(r){return t(o(n,e),function(){return r})},c=function(r){return t(o(n,e),function(){throw r})};return t(r,i,c)})}();function ToInteger(n){if("symbol"===Type(n))throw new TypeError("Cannot convert a Symbol value to a number");var t=Number(n);return isNaN(t)?0:1/t===Infinity||1/t==-Infinity||t===Infinity||t===-Infinity?t:(t<0?-1:1)*Math.floor(Math.abs(t))}function ToLength(n){var t=ToInteger(n);return t<=0?0:Math.min(t,Math.pow(2,53)-1)}function ToPrimitive(e){var t=arguments.length>1?arguments[1]:undefined;if("object"===Type(e)){if(arguments.length<2)var i="default";else t===String?i="string":t===Number&&(i="number");var r="function"==typeof self.Symbol&&"symbol"==typeof self.Symbol.toPrimitive?GetMethod(e,self.Symbol.toPrimitive):undefined;if(r!==undefined){var n=Call(r,e,[i]);if("object"!==Type(n))return n;throw new TypeError("Cannot convert exotic object to primitive.")}return"default"===i&&(i="number"),OrdinaryToPrimitive(e,i)}return e}function ToString(t){switch(Type(t)){case"symbol":throw new TypeError("Cannot convert a Symbol value to a string");case"object":return ToString(ToPrimitive(t,String));default:return String(t)}}function FlattenIntoArray(r,t,e,a,n){for(var o=arguments[5],i=arguments[6],l=a,g=0;g<e;){var h=ToString(g);if(!0===HasProperty(t,h)){var y=Get(t,h);5 in arguments&&(y=Call(o,i,[y,g,t]));var f=!1;if(n>0&&(f=IsArray(y)),!0===f){l=FlattenIntoArray(r,y,ToLength(Get(y,"length")),l,n-1)}else{if(l>=Math.pow(2,53)-1)throw new TypeError("targetIndex is greater than or equal to 2^53-1");CreateDataPropertyOrThrow(r,ToString(l),y),l+=1}}g+=1}return l}CreateMethodProperty(Array.prototype,"flat",function t(){"use strict";var t=arguments[0],e=ToObject(this),r=ToLength(Get(e,"length")),o=1;void 0!==t&&(o=ToInteger(t));var a=ArraySpeciesCreate(e,0);return FlattenIntoArray(a,e,r,0,o),a});function ToPropertyKey(r){var i=ToPrimitive(r,String);return"symbol"===Type(i)?i:ToString(i)}CreateMethodProperty(Object,"fromEntries",function r(e){RequireObjectCoercible(e);var t={},o=function(r,e){var t=this,o=ToPropertyKey(r);CreateDataPropertyOrThrow(t,o,e)};return AddEntriesFromIterable(t,e,o)});})('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
\ No newline at end of file
src/serveGuiFiles.ts
+1 -4
@@ -107,6 +107,7 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
107 SPECIAL_URI, PLUGINS_PUB_URI, FRONTEND_URI,
108 session: session instanceof ApiError ? null : session,
109 plugins,
110 + loadScripts: Object.fromEntries(mapPlugins((p, id) => [id, p.frontend_js?.map(f => f.includes('//') ? f : pub + id + '/' + f)])),
111 prefixUrl: ctx.state.revProxyPath,
112 dontOverwriteUploading: dontOverwriteUploading.get(),
113 customHtml: _.omit(Object.fromEntries(customHtmlState.sections),
@@ -130,10 +131,6 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
131 plug.frontend_css?.map(f =>
132 `<link rel='stylesheet' type='text/css' href='${f.includes('//') ? f : pub + id + '/' + f}' plugin=${JSON.stringify(id)}/>`))
133 .flat().filter(Boolean).join('\n')}
133 - ${!isFrontend ? '' : mapPlugins((plug,id) =>
134 - plug.frontend_js?.map(f =>
135 - `<script defer plugin=${JSON.stringify(id)} src='${f.includes('//') ? f : pub + id + '/' + f}'></script>`))
136 - .flat().filter(Boolean).join('\n')}
134 `
135 if (isBody && isClose)
136 return getSection('bottom') + all