Greasy Fork is available in English.

lib:file opener

none

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