lib:file opener

none

当前为 2024-05-15 提交的版本,查看 最新版本

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