lib:file opener

none

  1. // ==UserScript==
  2. // @name lib:file opener
  3. // @version 10
  4. // @description none
  5. // @license GPLv3
  6. // @run-at document-start
  7. // @author rssaromeo
  8. // @match *://*/*
  9. // @include *
  10. // @tag lib
  11. // @exclude /livereload.net\/files\/ffopen\/index.html$/
  12. // @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=
  13. // @grant none
  14. // @namespace https://greasyfork.org/users/1184528
  15. // ==/UserScript==
  16. ;(() => {
  17. const a = loadlib("allfuncs")
  18. const progressBar = loadlib("progress bar")
  19. const run = {
  20. file: runfile,
  21. folder: runfolder,
  22. globals: window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck ?? [],
  23. }
  24. delete window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck
  25. ;(async () => {
  26. var cac = {}
  27. async function newglobaljs(name, func = (e) => e, newname) {
  28. if ((newname ?? name).startsWith("blob:http")) return
  29. var text = cac[name] ?? (await (await fetch(name)).text())
  30. if (newname) name = newname
  31. cac[name] ??= text
  32. run.globals.push({
  33. text: func(text),
  34. name: name,
  35. })
  36. }
  37. run.newglobaljs = newglobaljs
  38. })()
  39. function hashformat({ isglobal, name }) {
  40. const hashformat = "#__isglobal: filename"
  41. if (isglobal == true) isglobal = "global"
  42. if (isglobal == false) isglobal = "local"
  43. return _replaceall(hashformat, [
  44. ["isglobal", isglobal],
  45. ["filename", name],
  46. ])
  47. }
  48.  
  49. loadlib("libloader").savelib("file opener", run)
  50. async function runfile(file) {
  51. file = await formatfiles(await file.getFile())
  52. replaceglobalurls(file)
  53. await updateglobals(file)
  54. newurl(file, file.format)
  55. return openfile(file, file.name)
  56. }
  57. async function updateglobals(file) {
  58. const tempglobals = JSON.stringify([
  59. ...run.globals.map((e) => {
  60. return { name: e.name, text: e.text }
  61. }),
  62. ])
  63. // .replaceAll("<", "&lt;")
  64. // .replaceAll("&", "&amp;")
  65. if (file.name.endsWith(".html"))
  66. file.text =
  67. `<script>window.ERwkOoQYn9C3jxDZdovIZoZ2DmGt5wKyTMPU2uck = ${tempglobals}<\/script>` +
  68. file.text
  69. }
  70. async function runfolder(folder, mainfile = "index.html") {
  71. var files = await getfilesfromfolder(folder)
  72. const name = files[0].path.match(/^([^\/]+)\//, "")[1]
  73. files = await formatfiles(files)
  74.  
  75. setupget(files)
  76. var index = files.get(mainfile)
  77. if (!index) {
  78. error(
  79. `folder ${name} doesn't contain ${mainfile}, searching for index.html instead`
  80. )
  81. index = files.get("index.html")
  82. }
  83. if (!index)
  84. throw new Error(`folder ${name} doesn't contain index.html`)
  85. var htmls = files.get(/\.html/i)
  86. // error(a(files.get(/\.png/i)[0].file).readfile('DataURL'))
  87. var BAR = new progressBar(0, files.length + htmls.length)
  88. {
  89. for (var i in files) {
  90. var file = files[i]
  91. BAR.set(i, file.name)
  92. newurl(file, file.format)
  93. if (Number(i) % 15 == 0) await a.wait(0)
  94. }
  95. var f = files.get(/\./)
  96. f = f.filter((e) => ["js", "css"].includes(e.extension))
  97. for (var i in f) {
  98. var e = f[i]
  99. if (Number(i) % 15 == 0) {
  100. BAR.set(Number(i), e.name)
  101. await a.wait(0)
  102. }
  103. replaceallurls(e, files)
  104. replaceglobalurls(e)
  105. newurl(e)
  106. }
  107. for (var i in htmls) {
  108. var e = htmls[i]
  109. if (Number(i) % 15 == 0) {
  110. BAR.set(Number(i) + files.length, e.name)
  111. await a.wait(0)
  112. }
  113. replaceallurls(e, files)
  114. replaceglobalurls(e)
  115. newurl(e, "text/html")
  116. }
  117. }
  118. // warn(index, index.path.split("/"))
  119. replaceallurls(index, files, true)
  120. await updateglobals(index)
  121. newurl(index, "text/html")
  122. BAR.remove()
  123. return openfile(index, name)
  124. }
  125. function openfile(file, name) {
  126. name ??= file?.file?.name
  127. return file.url
  128. }
  129. function getallgoodpaths(file, files, lll) {
  130. var p = file.path.split("/")
  131. var n = p.pop()
  132. return files.map((e) => {
  133. if (!e.path) error(e, file)
  134. var path = e.path.split("/")
  135. var name = path.pop()
  136. if (same(p, path)) {
  137. return { ...e, path: name }
  138. }
  139. var newpath = ""
  140. var rs = false
  141. p.forEach((e, i) => {
  142. if (same(e, path[i]) && !rs) return
  143. rs = true
  144. newpath += "../"
  145. })
  146. path.push("")
  147. return {
  148. ...e,
  149. path: newpath + path.join("/") + name,
  150. }
  151. })
  152. function same(a, s) {
  153. return JSON.stringify(a) == JSON.stringify(s)
  154. }
  155. }
  156. function replaceallurls(file, files, lll) {
  157. if (file.text.startsWith("#redirect")) {
  158. var redir = file.text.match(/^#redirect (.*)/)[1]
  159. var redirfile = files.get(redir)
  160. if (!redirfile)
  161. throw new Error(
  162. `failed to redirect from ${file.name} to ${redir}`
  163. )
  164. file.text =
  165. file.text.replace(`#redirect ${redir}`, "") +
  166. "\n" +
  167. redirfile.text
  168. }
  169. var goodfiles = getallgoodpaths(file, files, lll)
  170. goodfiles.forEach(({ path, url }) => {
  171. file.text = file.text.replaceAll(
  172. new RegExp(`(['"])(?:\\.\\/)*${regescape(path)}\\1`, "gi"),
  173. `"${url}${hashformat({ isglobal: false, name: path })}"`
  174. )
  175. })
  176. replaceglobalurls(file)
  177. }
  178. function regescape(reg) {
  179. return reg.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")
  180. }
  181. function newurl(file, type) {
  182. type ??= file.format
  183. var blob =
  184. type && type.startsWith("image/")
  185. ? new Blob([file.file], { type })
  186. : new Blob([file.text], { type })
  187. file.url = URL.createObjectURL(blob)
  188. return file
  189. }
  190. async function replaceglobalurls(file) {
  191. run.globals.forEach((e) => {
  192. if (!e.regex)
  193. e.regex = new RegExp(
  194. `(['"])(?:\\.?\\.\\/)*${regescape(e.name)}\\1`,
  195. "gi"
  196. )
  197. if (!e.url)
  198. e.url = URL.createObjectURL(
  199. new Blob([e.text], { type: "text/javascript" })
  200. )
  201. file.text = file.text.replaceAll(
  202. e.regex,
  203. `"${e.url}${hashformat({ isglobal: true, name: e.name })}"`
  204. )
  205. })
  206. return file
  207. }
  208. async function formatfiles(files) {
  209. if (!a.gettype(files, "array")) return await format(files)
  210. return await Promise.all(files.map(format))
  211. async function format(file) {
  212. var data = await a.readfile(file)
  213. // if(file.name.match(/\.(\w+)$/)?.[1]=='svg'){
  214. // error(file)
  215. // }
  216. return {
  217. name: file.name,
  218. text: data,
  219. path: file?.path?.replace?.(/^[^\/]+\//, ""),
  220. extension: file.name.match(/\.(\w+)$/)?.[1],
  221. format: {
  222. js: "text/javascript",
  223. html: "text/html",
  224. css: "text/css",
  225. jpg: "image/jpg",
  226. jpeg: "image/jpeg",
  227. png: "image/png",
  228. svg: "image/svg+xml",
  229. }[file.name.match(/\.(\w+)$/)?.[1]],
  230. file,
  231. }
  232. }
  233. }
  234. function setupget(files) {
  235. files.get = function (name, skip = 0) {
  236. if (a.gettype(name, "string"))
  237. return files.find((e) => {
  238. return e.path == name
  239. })
  240. else return files.filter((e) => name.test(e.path))
  241. }
  242. }
  243.  
  244. async function getfilesfromfolder(
  245. dirHandle,
  246. path = dirHandle.name
  247. ) {
  248. const dirs = []
  249. const files = []
  250. // warn(path)
  251. for await (const entry of dirHandle.values()) {
  252. const nestedPath = `${path}/${entry.name}`
  253. if (
  254. nestedPath.startsWith(dirHandle.name + "/codemirror/mode/ja")
  255. )
  256. error(nestedPath, entry)
  257. if (entry.kind === "file") {
  258. files.push(
  259. entry.getFile().then((file) => {
  260. file.directoryHandle = dirHandle
  261. file.handle = entry
  262. Object.defineProperty(file, "path", {
  263. configurable: true,
  264. enumerable: true,
  265. get: () => nestedPath,
  266. })
  267. return Object.defineProperty(file, "webkitRelativePath", {
  268. configurable: true,
  269. enumerable: true,
  270. get: () => nestedPath,
  271. })
  272. })
  273. )
  274. } else if (entry.kind === "directory") {
  275. // warn(entry, nestedPath)
  276. dirs.push(getfilesfromfolder(entry, nestedPath))
  277. } else {
  278. error(entry.kind)
  279. }
  280. }
  281. return [
  282. ...(await Promise.all(dirs)).flat(),
  283. ...(await Promise.all(files)),
  284. ]
  285. }
  286. function _replaceall(q, w, e) {
  287. switch (a.gettype(w, "array") + " " + a.gettype(e, "array")) {
  288. case "true true":
  289. if (e.length == w.length) {
  290. w.forEach((ww, i) => {
  291. q = q.replaceAll(ww, e[i])
  292. })
  293. return q
  294. }
  295. throw new Error(
  296. "when both are arrays the length must be the same"
  297. )
  298. break
  299. case "true false":
  300. if (a.gettype(w[0], "array")) {
  301. w.forEach(([ww, e]) => {
  302. q = q.replaceAll(ww, e)
  303. })
  304. } else {
  305. w.forEach((ww) => {
  306. q = q.replaceAll(ww, e)
  307. })
  308. }
  309. return q
  310. break
  311. case "false false":
  312. return q.replaceAll(w, e)
  313. break
  314. }
  315. }
  316. })()