lib:file opener

none

当前为 2025-03-18 提交的版本,查看 最新版本

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