lib:file opener

none

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

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