// ==UserScript==
// @name lib:file opener
// @version 4
// @description none
// @license GPLv3
// @run-at document-start
// @author You
// @match *://*/*
// @exclude /livereload.net\/files\/ffopen\/index.html$/
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHJQTFRFAAAAEIijAo2yAI60BYyuF4WaFIifAY6zBI2wB4usGIaZEYigIoiZCIyrE4igG4iYD4mjEomhFoedCoqpDIqnDomlBYyvE4efEYmiDYqlA42xBoytD4mkCYqqGYSUFYidC4qoC4upAo6yCoupDYqmCYur4zowOQAAACZ0Uk5TAO////9vr////1+/D/+/L+/Pf/////+f3///////H4////////+5G91rAAACgUlEQVR4nM2Y22KjIBCGidg1264liZqDadK03X3/V2wNKHMC7MpF/xthHD5mgERAqZhWhfYqH6K+Qf2qNNf625hCoFj9/gblMUi5q5jLkXLCKudgyiRm0FMK82cWJp1fLbV5VmvJbCIc0GCYaFqqlDJgADdBjncqAXYobm1xh72aFMflbysteFfdy2Yi1XGOm5HGBzQ1dq7TzEoxjeNTjQZb7VA3e1c7+ImgasAgQ9+xusNVNZIo5xmOMgihIS2PbCQIiHEUdTvhxCcS/kPomfFI2zHy2PkWmA6aNatIJpKFJyekyy02xh5Y3DI9T4aOT6VhIUrsNTFp1pf79Z4SIIVDegl6IJO6cHiL/GimIZDhgTu/BlYWCQzHMl0zBWT/T3KAhtxOuUB9FtBrpsz0RV4xsjHmW+UCaffcSy/5viMGer0/6HdFNMZBq/vjJL38H9Dqx4Fuy0Em12DbZy+9pGtiDijbglwAehyj11n0tRD3WUBm+lwulE/8h4BuA+iWAQQnteg2Xm63WQLTpnMnpjdge0Mgu/GRPsV4xdjQ94Lfi624fabhDkfUqIKNrM64Q837v8yL0prasepCgrtvw1sJpoqanGEX7b5mQboNW8eawXaWXTMfMGxub472hzWzHSn6Sg2G9+6TAyRruE71s+zAzjWaknoyJCQzwxrghH2k5FDT4eqWunuNxyN9QCGcxVod5oADbYnIUkDTGZEf1xDJnSFteQ3KdsT8zYDMQXcHxsevcLH1TrsABzkNPyA/L7b0jg704viMMlpQI96WsHknCt/3YH0kOEo9zcGkwrFK39ck72rmoehmKqo2RKlilzSy/nJKEV45CT38myJp456fezktHjN5aeMAAAAASUVORK5CYII=
// @grant none
// @namespace https://greasyfork.org/users/1184528
// ==/UserScript==
;(() => {
const a = loadlib("allfuncs")
const run = {
file: runfile,
folder: runfolder,
globals: window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck ?? [],
}
delete window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck
;(async () => {
var cac = {}
async function newglobaljs(name, func = (e) => e, newname) {
if ((newname ?? name).startsWith("blob:http")) return
var text = cac[name] ?? (await (await fetch(name)).text())
if (newname) name = newname
cac[name] ??= text
run.globals.push({
text: func(text),
name: name,
})
}
run.newglobaljs = newglobaljs
})()
function hashformat({ isglobal, name }) {
const hashformat = "#__isglobal: filename"
if (isglobal == true) isglobal = "global"
if (isglobal == false) isglobal = "local"
return _replaceall(hashformat, [
["isglobal", isglobal],
["filename", name],
])
}
loadlib("libloader").savelib("file opener", run)
async function runfile(file) {
file = await formatfiles(await file.getFile())
replaceglobalurls(file)
await updateglobals(file)
newurl(file, file.format)
openfile(file, file.name)
}
async function updateglobals(file) {
const tempglobals = JSON.stringify([
...run.globals.map((e) => {
return { name: e.name, text: e.text }
}),
])
// .replaceAll("<", "<")
// .replaceAll("&", "&")
if (file.name.endsWith(".html"))
file.text =
`<script>window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck = ${tempglobals}<\/script>` +
file.text
}
async function runfolder(folder, mainfile = "index.html") {
var files = await getfilesfromfolder(folder)
const name = files[0].path.match(/^([^\/]+)\//, "")[1]
files = await formatfiles(files)
setupget(files)
var index = files.get(mainfile)
if (!index) {
error(
`folder ${name} doesn't contain ${mainfile}, searching for index.html instead`
)
index = files.get("index.html")
}
if (!index) throw new Error(`folder ${name} doesn't contain index.html`)
var htmls = files.get(/\.html/i)
// error(a(files.get(/\.png/i)[0].file).readfile('DataURL'))
{
function updatebar(i, name) {
progresstext.innerHTML = `${i}/${files.length + htmls.length}: ${name}`
innerprogress.style.width =
"calc(" +
a(Number(i)).rerange(0, files.length + htmls.length, 0, 100) +
"% - 2px)"
}
var progress = a(document.body)
.createelem("div", {
position: "fixed",
top: "0",
left: 0,
border: "30px solid #999",
backgroundColor: "black",
color: "white",
width: "calc(100vw - 60px)",
height: "29px",
})
.createelem.same("div", {
backgroundColor: "#777",
width:
a(files.length).rerange(0, files.length + htmls.length, 0, 100) +
"%",
height: "10px",
})
.createelem.same("div", {
backgroundColor: "#555",
width:
100 -
a(files.length).rerange(0, files.length + htmls.length, 0, 100) +
"%",
position: "relative",
top: "-10px",
left:
a(files.length).rerange(0, files.length + htmls.length, 0, 100) +
"%",
height: "10px",
}).val
var innerprogress = a(progress).createelem("div", {
backgroundColor: "#aaa",
width: 0,
position: "relative",
top: "-18px",
left: "2px",
height: "6px",
}).val
var progresstext = a(progress).createelem("span", {
position: "relative",
top: "-16px",
}).val
for (var i in files) {
var file = files[i]
updatebar(i, file.name)
newurl(file, file.format)
if (Number(i) % 15 == 0) await a(0).wait()
}
var f = files.get(/\./)
f = f.filter((e) => ["js", "css"].includes(e.extension))
for (var i in f) {
var e = f[i]
if (Number(i) % 15 == 0) {
updatebar(Number(i), e.name)
await a(0).wait()
}
replaceallurls(e, files)
replaceglobalurls(e)
newurl(e)
}
for (var i in htmls) {
var e = htmls[i]
if (Number(i) % 15 == 0) {
updatebar(Number(i) + files.length, e.name)
await a(0).wait()
}
replaceallurls(e, files)
replaceglobalurls(e)
newurl(e, "text/html")
}
}
// warn(index, index.path.split("/"))
replaceallurls(index, files, true)
await updateglobals(index)
newurl(index, "text/html")
progress.remove()
openfile(index, name)
}
function openfile(file, name) {
name ??= file?.file?.name
return open(file.url, location.href + name)
}
function getallgoodpaths(file, files, lll) {
var p = file.path.split("/")
var n = p.pop()
return files.map((e) => {
if (!e.path) error(e, file)
var path = e.path.split("/")
var name = path.pop()
if (same(p, path)) {
return { ...e, path: name }
}
var newpath = ""
var rs = false
p.forEach((e, i) => {
if (same(e, path[i]) && !rs) return
rs = true
newpath += "../"
})
path.push("")
return {
...e,
path: newpath + path.join("/") + name,
}
})
function same(a, s) {
return JSON.stringify(a) == JSON.stringify(s)
}
}
function replaceallurls(file, files, lll) {
if (file.text.startsWith("#redirect")) {
var redir = file.text.match(/^#redirect (.*)/)[1]
var redirfile = files.get(redir)
if (!redirfile)
throw new Error(`failed to redirect from ${file.name} to ${redir}`)
file.text =
file.text.replace(`#redirect ${redir}`, "") + "\n" + redirfile.text
}
var goodfiles = getallgoodpaths(file, files, lll)
goodfiles.forEach(({ path, url }) => {
file.text = file.text.replaceAll(
new RegExp(`(['"])(?:\\.\\/)*${regescape(path)}\\1`, "gi"),
`"${url}${hashformat({ isglobal: false, name: path })}"`
)
})
replaceglobalurls(file)
}
function regescape(reg) {
return reg.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")
}
function newurl(file, type) {
type ??= file.format
var blob =
type && type.startsWith("image/")
? new Blob([file.file], { type })
: new Blob([file.text], { type })
file.url = URL.createObjectURL(blob)
return file
}
async function replaceglobalurls(file) {
run.globals.forEach((e) => {
if (!e.regex)
e.regex = new RegExp(
`(['"])(?:\\.?\\.\\/)*${regescape(e.name)}\\1`,
"gi"
)
if (!e.url)
e.url = URL.createObjectURL(
new Blob([e.text], { type: "text/javascript" })
)
file.text = file.text.replaceAll(
e.regex,
`"${e.url}${hashformat({ isglobal: true, name: e.name })}"`
)
})
return file
}
async function formatfiles(files) {
if (!a(files).gettype("array").val) return await format(files)
return await Promise.all(files.map(format))
async function format(file) {
var data = await a(file).readfile()
// if(file.name.match(/\.(\w+)$/)?.[1]=='svg'){
// error(file)
// }
return {
name: file.name,
text: data,
path: file?.path?.replace?.(/^[^\/]+\//, ""),
extension: file.name.match(/\.(\w+)$/)?.[1],
format: {
js: "text/javascript",
html: "text/html",
css: "text/css",
jpg: "image/jpg",
jpeg: "image/jpeg",
png: "image/png",
svg: "image/svg+xml",
}[file.name.match(/\.(\w+)$/)?.[1]],
file,
}
}
}
function setupget(files) {
files.get = function (name, skip = 0) {
if (a(name).gettype("string").val)
return files.find((e) => {
return e.path == name
})
else return files.filter((e) => name.test(e.path))
}
}
async function getfilesfromfolder(dirHandle, path = dirHandle.name) {
const dirs = []
const files = []
// warn(path)
for await (const entry of dirHandle.values()) {
const nestedPath = `${path}/${entry.name}`
if (nestedPath.startsWith(dirHandle.name + "/codemirror/mode/ja"))
error(nestedPath, entry)
if (entry.kind === "file") {
files.push(
entry.getFile().then((file) => {
file.directoryHandle = dirHandle
file.handle = entry
Object.defineProperty(file, "path", {
configurable: true,
enumerable: true,
get: () => nestedPath,
})
return Object.defineProperty(file, "webkitRelativePath", {
configurable: true,
enumerable: true,
get: () => nestedPath,
})
})
)
} else if (entry.kind === "directory") {
warn(entry, nestedPath)
dirs.push(getfilesfromfolder(entry, nestedPath))
} else {
error(entry.kind)
}
}
return [...(await Promise.all(dirs)).flat(), ...(await Promise.all(files))]
}
function _replaceall(q, w, e) {
switch (a(w).gettype("array").val + " " + a(e).gettype("array").val) {
case "true true":
if (e.length == w.length) {
w.forEach((ww, i) => {
q = q.replaceAll(ww, e[i])
})
return q
}
throw new Error("when both are arrays the length must be the same")
break
case "true false":
if (a(w[0]).gettype("array").val) {
w.forEach(([ww, e]) => {
q = q.replaceAll(ww, e)
})
} else {
w.forEach((ww) => {
q = q.replaceAll(ww, e)
})
}
return q
break
case "false false":
return q.replaceAll(w, e)
break
}
}
})()