@browserless.io/browserless 2.31.1 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/build/routes/chrome/http/content.post.body.json +8 -8
  3. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  4. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  5. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  6. package/build/routes/chromium/http/content.post.body.json +8 -8
  7. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  8. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  9. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  10. package/build/routes/edge/http/content.post.body.json +8 -8
  11. package/build/routes/edge/http/pdf.post.body.json +8 -8
  12. package/build/routes/edge/http/scrape.post.body.json +8 -8
  13. package/build/routes/edge/http/screenshot.post.body.json +8 -8
  14. package/build/routes/management/tests/management.spec.js +14 -0
  15. package/build/server.js +4 -5
  16. package/build/shared/utils/enjoi-resolver.d.ts +77 -0
  17. package/build/shared/utils/enjoi-resolver.js +399 -0
  18. package/docker/base/Dockerfile +2 -2
  19. package/extensions/ublocklite/README.md +4 -2
  20. package/extensions/ublocklite/_locales/ar/messages.json +34 -10
  21. package/extensions/ublocklite/_locales/az/messages.json +26 -2
  22. package/extensions/ublocklite/_locales/be/messages.json +28 -4
  23. package/extensions/ublocklite/_locales/bg/messages.json +26 -2
  24. package/extensions/ublocklite/_locales/bn/messages.json +26 -2
  25. package/extensions/ublocklite/_locales/br_FR/messages.json +26 -2
  26. package/extensions/ublocklite/_locales/bs/messages.json +26 -2
  27. package/extensions/ublocklite/_locales/ca/messages.json +26 -2
  28. package/extensions/ublocklite/_locales/cs/messages.json +26 -2
  29. package/extensions/ublocklite/_locales/cv/messages.json +26 -2
  30. package/extensions/ublocklite/_locales/cy/messages.json +37 -13
  31. package/extensions/ublocklite/_locales/da/messages.json +26 -2
  32. package/extensions/ublocklite/_locales/de/messages.json +26 -2
  33. package/extensions/ublocklite/_locales/el/messages.json +29 -5
  34. package/extensions/ublocklite/_locales/en/messages.json +26 -2
  35. package/extensions/ublocklite/_locales/en_GB/messages.json +26 -2
  36. package/extensions/ublocklite/_locales/eo/messages.json +26 -2
  37. package/extensions/ublocklite/_locales/es/messages.json +26 -2
  38. package/extensions/ublocklite/_locales/et/messages.json +26 -2
  39. package/extensions/ublocklite/_locales/eu/messages.json +26 -2
  40. package/extensions/ublocklite/_locales/fa/messages.json +26 -2
  41. package/extensions/ublocklite/_locales/fi/messages.json +26 -2
  42. package/extensions/ublocklite/_locales/fil/messages.json +68 -44
  43. package/extensions/ublocklite/_locales/fr/messages.json +26 -2
  44. package/extensions/ublocklite/_locales/fy/messages.json +26 -2
  45. package/extensions/ublocklite/_locales/gl/messages.json +27 -3
  46. package/extensions/ublocklite/_locales/gu/messages.json +26 -2
  47. package/extensions/ublocklite/_locales/he/messages.json +26 -2
  48. package/extensions/ublocklite/_locales/hi/messages.json +27 -3
  49. package/extensions/ublocklite/_locales/hr/messages.json +26 -2
  50. package/extensions/ublocklite/_locales/hu/messages.json +26 -2
  51. package/extensions/ublocklite/_locales/hy/messages.json +26 -2
  52. package/extensions/ublocklite/_locales/id/messages.json +37 -13
  53. package/extensions/ublocklite/_locales/it/messages.json +26 -2
  54. package/extensions/ublocklite/_locales/ja/messages.json +26 -2
  55. package/extensions/ublocklite/_locales/ka/messages.json +26 -2
  56. package/extensions/ublocklite/_locales/kk/messages.json +26 -2
  57. package/extensions/ublocklite/_locales/kn/messages.json +26 -2
  58. package/extensions/ublocklite/_locales/ko/messages.json +26 -2
  59. package/extensions/ublocklite/_locales/lt/messages.json +26 -2
  60. package/extensions/ublocklite/_locales/lv/messages.json +26 -2
  61. package/extensions/ublocklite/_locales/mk/messages.json +26 -2
  62. package/extensions/ublocklite/_locales/ml/messages.json +26 -2
  63. package/extensions/ublocklite/_locales/mr/messages.json +26 -2
  64. package/extensions/ublocklite/_locales/ms/messages.json +26 -2
  65. package/extensions/ublocklite/_locales/nb/messages.json +26 -2
  66. package/extensions/ublocklite/_locales/nl/messages.json +26 -2
  67. package/extensions/ublocklite/_locales/oc/messages.json +26 -2
  68. package/extensions/ublocklite/_locales/pa/messages.json +26 -2
  69. package/extensions/ublocklite/_locales/pl/messages.json +26 -2
  70. package/extensions/ublocklite/_locales/pt_BR/messages.json +26 -2
  71. package/extensions/ublocklite/_locales/pt_PT/messages.json +29 -5
  72. package/extensions/ublocklite/_locales/ro/messages.json +26 -2
  73. package/extensions/ublocklite/_locales/ru/messages.json +26 -2
  74. package/extensions/ublocklite/_locales/si/messages.json +26 -2
  75. package/extensions/ublocklite/_locales/sk/messages.json +26 -2
  76. package/extensions/ublocklite/_locales/sl/messages.json +26 -2
  77. package/extensions/ublocklite/_locales/so/messages.json +26 -2
  78. package/extensions/ublocklite/_locales/sq/messages.json +26 -2
  79. package/extensions/ublocklite/_locales/sr/messages.json +26 -2
  80. package/extensions/ublocklite/_locales/sv/messages.json +26 -2
  81. package/extensions/ublocklite/_locales/sw/messages.json +26 -2
  82. package/extensions/ublocklite/_locales/ta/messages.json +26 -2
  83. package/extensions/ublocklite/_locales/te/messages.json +26 -2
  84. package/extensions/ublocklite/_locales/th/messages.json +26 -2
  85. package/extensions/ublocklite/_locales/tr/messages.json +26 -2
  86. package/extensions/ublocklite/_locales/uk/messages.json +26 -2
  87. package/extensions/ublocklite/_locales/ur/messages.json +26 -2
  88. package/extensions/ublocklite/_locales/vi/messages.json +34 -10
  89. package/extensions/ublocklite/_locales/zh_CN/messages.json +33 -9
  90. package/extensions/ublocklite/_locales/zh_TW/messages.json +26 -2
  91. package/extensions/ublocklite/css/develop.css +58 -25
  92. package/extensions/ublocklite/css/line-hor-dashed.png +0 -0
  93. package/extensions/ublocklite/dashboard.html +25 -16
  94. package/extensions/ublocklite/js/admin.js +7 -8
  95. package/extensions/ublocklite/js/arglist-parser.js +116 -0
  96. package/extensions/ublocklite/js/background.js +38 -19
  97. package/extensions/ublocklite/js/dashboard.js +3 -3
  98. package/extensions/ublocklite/js/develop.js +540 -798
  99. package/extensions/ublocklite/js/dnr-editor.js +180 -0
  100. package/extensions/ublocklite/js/dnr-parser.js +30 -10
  101. package/extensions/ublocklite/js/i18n.js +5 -0
  102. package/extensions/ublocklite/js/jsonpath.js +470 -0
  103. package/extensions/ublocklite/js/mode-editor.js +87 -0
  104. package/extensions/ublocklite/js/mode-manager.js +11 -48
  105. package/extensions/ublocklite/js/mode-parser.js +211 -0
  106. package/extensions/ublocklite/js/redirect-resources.js +192 -0
  107. package/extensions/ublocklite/js/ro-dnr-editor.js +95 -0
  108. package/extensions/ublocklite/js/ruleset-manager.js +42 -16
  109. package/extensions/ublocklite/js/rw-dnr-editor.js +405 -0
  110. package/extensions/ublocklite/js/settings.js +7 -71
  111. package/extensions/ublocklite/js/static-filtering-parser.js +4413 -0
  112. package/extensions/ublocklite/js/ubo-parser.js +461 -0
  113. package/extensions/ublocklite/lib/codemirror/cm6.bundle.ubol.min.js +1 -1
  114. package/extensions/ublocklite/lib/codemirror/codemirror.LICENSE +21 -0
  115. package/extensions/ublocklite/lib/csstree/LICENSE +19 -0
  116. package/extensions/ublocklite/lib/csstree/css-tree.js +17 -0
  117. package/extensions/ublocklite/manifest.json +1 -1
  118. package/extensions/ublocklite/rulesets/generic-details.json +6 -32
  119. package/extensions/ublocklite/rulesets/main/adguard-mobile.json +7 -18
  120. package/extensions/ublocklite/rulesets/main/annoyances-cookies.json +17 -9
  121. package/extensions/ublocklite/rulesets/main/annoyances-others.json +5 -2
  122. package/extensions/ublocklite/rulesets/main/annoyances-overlays.json +2 -1
  123. package/extensions/ublocklite/rulesets/main/annoyances-social.json +4 -2
  124. package/extensions/ublocklite/rulesets/main/block-lan.json +6 -0
  125. package/extensions/ublocklite/rulesets/main/chn-0.json +30 -64
  126. package/extensions/ublocklite/rulesets/main/cze-0.json +2 -1
  127. package/extensions/ublocklite/rulesets/main/deu-0.json +3 -4
  128. package/extensions/ublocklite/rulesets/main/dpollock-0.json +2 -0
  129. package/extensions/ublocklite/rulesets/main/easylist.json +31 -13
  130. package/extensions/ublocklite/rulesets/main/easyprivacy.json +53 -11
  131. package/extensions/ublocklite/rulesets/main/fin-0.json +11 -2
  132. package/extensions/ublocklite/rulesets/main/fra-0.json +22 -43
  133. package/extensions/ublocklite/rulesets/main/grc-0.json +1 -1
  134. package/extensions/ublocklite/rulesets/main/hun-0.json +9 -2
  135. package/extensions/ublocklite/rulesets/main/idn-0.json +8 -38
  136. package/extensions/ublocklite/rulesets/main/ind-0.json +48 -12
  137. package/extensions/ublocklite/rulesets/main/irn-0.json +1 -0
  138. package/extensions/ublocklite/rulesets/main/isr-0.json +11 -1
  139. package/extensions/ublocklite/rulesets/main/ita-0.json +1 -1
  140. package/extensions/ublocklite/rulesets/main/jpn-1.json +24 -18
  141. package/extensions/ublocklite/rulesets/main/kor-1.json +6 -4
  142. package/extensions/ublocklite/rulesets/main/nld-0.json +4 -2
  143. package/extensions/ublocklite/rulesets/main/nor-0.json +8 -2
  144. package/extensions/ublocklite/rulesets/main/pgl.json +12 -18
  145. package/extensions/ublocklite/rulesets/main/pol-0.json +5 -0
  146. package/extensions/ublocklite/rulesets/main/rus-0.json +35 -30
  147. package/extensions/ublocklite/rulesets/main/spa-1.json +18 -61
  148. package/extensions/ublocklite/rulesets/main/stevenblack-hosts.json +9228 -1275
  149. package/extensions/ublocklite/rulesets/main/swe-1.json +43 -30
  150. package/extensions/ublocklite/rulesets/main/tur-0.json +20 -38
  151. package/extensions/ublocklite/rulesets/main/ublock-badware.json +73 -109
  152. package/extensions/ublocklite/rulesets/main/ublock-filters.json +185 -36
  153. package/extensions/ublocklite/rulesets/main/ukr-0.json +8 -26
  154. package/extensions/ublocklite/rulesets/main/urlhaus-full.json +587 -976
  155. package/extensions/ublocklite/rulesets/main/vie-1.json +42 -36
  156. package/extensions/ublocklite/rulesets/modify-headers/chn-0.json +5 -1
  157. package/extensions/ublocklite/rulesets/modify-headers/fra-0.json +0 -3
  158. package/extensions/ublocklite/rulesets/modify-headers/rus-0.json +2 -1
  159. package/extensions/ublocklite/rulesets/modify-headers/ublock-filters.json +1 -1
  160. package/extensions/ublocklite/rulesets/redirect/adguard-mobile.json +1 -1
  161. package/extensions/ublocklite/rulesets/redirect/annoyances-overlays.json +1 -0
  162. package/extensions/ublocklite/rulesets/redirect/nld-0.json +19 -0
  163. package/extensions/ublocklite/rulesets/redirect/spa-1.json +3 -4
  164. package/extensions/ublocklite/rulesets/redirect/swe-1.json +3 -2
  165. package/extensions/ublocklite/rulesets/redirect/tur-0.json +1 -1
  166. package/extensions/ublocklite/rulesets/redirect/ublock-filters.json +19 -10
  167. package/extensions/ublocklite/rulesets/redirect/vie-1.json +2 -2
  168. package/extensions/ublocklite/rulesets/regex/adguard-mobile.json +13 -0
  169. package/extensions/ublocklite/rulesets/regex/chn-0.json +2 -3
  170. package/extensions/ublocklite/rulesets/regex/jpn-1.json +1 -1
  171. package/extensions/ublocklite/rulesets/regex/kor-1.json +1 -0
  172. package/extensions/ublocklite/rulesets/regex/nor-0.json +10 -0
  173. package/extensions/ublocklite/rulesets/regex/spa-0.json +0 -1
  174. package/extensions/ublocklite/rulesets/regex/spa-1.json +0 -13
  175. package/extensions/ublocklite/rulesets/regex/ublock-badware.json +5 -6
  176. package/extensions/ublocklite/rulesets/regex/ublock-filters.json +16 -7
  177. package/extensions/ublocklite/rulesets/regex/ukr-0.json +2 -2
  178. package/extensions/ublocklite/rulesets/removeparam/adguard-spyware-url.json +10 -5
  179. package/extensions/ublocklite/rulesets/removeparam/rus-0.json +1 -1
  180. package/extensions/ublocklite/rulesets/removeparam/rus-1.json +6 -6
  181. package/extensions/ublocklite/rulesets/ruleset-details.json +244 -244
  182. package/extensions/ublocklite/rulesets/scripting/declarative/adguard-mobile.js +3 -3
  183. package/extensions/ublocklite/rulesets/scripting/declarative/annoyances-cookies.js +1 -1
  184. package/extensions/ublocklite/rulesets/scripting/declarative/annoyances-others.js +3 -3
  185. package/extensions/ublocklite/rulesets/scripting/declarative/annoyances-overlays.js +1 -1
  186. package/extensions/ublocklite/rulesets/scripting/declarative/chn-0.js +3 -3
  187. package/extensions/ublocklite/rulesets/scripting/declarative/easylist.js +3 -3
  188. package/extensions/ublocklite/rulesets/scripting/declarative/fin-0.js +3 -3
  189. package/extensions/ublocklite/rulesets/scripting/declarative/fra-0.js +3 -3
  190. package/extensions/ublocklite/rulesets/scripting/declarative/idn-0.js +3 -3
  191. package/extensions/ublocklite/rulesets/scripting/declarative/jpn-1.js +3 -3
  192. package/extensions/ublocklite/rulesets/scripting/declarative/kor-1.js +3 -3
  193. package/extensions/ublocklite/rulesets/scripting/declarative/rus-0.js +3 -3
  194. package/extensions/ublocklite/rulesets/scripting/declarative/rus-1.js +3 -3
  195. package/extensions/ublocklite/rulesets/scripting/declarative/spa-1.js +3 -3
  196. package/extensions/ublocklite/rulesets/scripting/declarative/swe-1.js +3 -3
  197. package/extensions/ublocklite/rulesets/scripting/declarative/tur-0.js +3 -3
  198. package/extensions/ublocklite/rulesets/scripting/declarative/ublock-filters.js +3 -3
  199. package/extensions/ublocklite/rulesets/scripting/declarative/ukr-0.js +3 -3
  200. package/extensions/ublocklite/rulesets/scripting/declarative/vie-1.js +1 -1
  201. package/extensions/ublocklite/rulesets/scripting/generic/annoyances-cookies.js +3 -3
  202. package/extensions/ublocklite/rulesets/scripting/generic/annoyances-social.js +3 -3
  203. package/extensions/ublocklite/rulesets/scripting/generic/chn-0.js +3 -3
  204. package/extensions/ublocklite/rulesets/scripting/generic/easylist.js +3 -3
  205. package/extensions/ublocklite/rulesets/scripting/generic/fin-0.js +2 -2
  206. package/extensions/ublocklite/rulesets/scripting/generic/fra-0.js +2 -2
  207. package/extensions/ublocklite/rulesets/scripting/generic/hrv-0.js +1 -1
  208. package/extensions/ublocklite/rulesets/scripting/generic/idn-0.js +2 -2
  209. package/extensions/ublocklite/rulesets/scripting/generic/ind-0.js +2 -2
  210. package/extensions/ublocklite/rulesets/scripting/generic/jpn-1.js +3 -3
  211. package/extensions/ublocklite/rulesets/scripting/generic/nor-0.js +1 -1
  212. package/extensions/ublocklite/rulesets/scripting/generic/rus-0.js +2 -2
  213. package/extensions/ublocklite/rulesets/scripting/generic/spa-1.js +2 -2
  214. package/extensions/ublocklite/rulesets/scripting/generic/tur-0.js +2 -2
  215. package/extensions/ublocklite/rulesets/scripting/generic/ublock-badware.js +1 -1
  216. package/extensions/ublocklite/rulesets/scripting/generic/ublock-filters.js +3 -3
  217. package/extensions/ublocklite/rulesets/scripting/generic/ukr-0.js +3 -3
  218. package/extensions/ublocklite/rulesets/scripting/generichigh/annoyances-cookies.css +0 -1
  219. package/extensions/ublocklite/rulesets/scripting/generichigh/annoyances-social.css +0 -1
  220. package/extensions/ublocklite/rulesets/scripting/generichigh/chn-0.css +1 -4
  221. package/extensions/ublocklite/rulesets/scripting/generichigh/cze-0.css +0 -1
  222. package/extensions/ublocklite/rulesets/scripting/generichigh/easylist.css +5 -9
  223. package/extensions/ublocklite/rulesets/scripting/generichigh/idn-0.css +12 -0
  224. package/extensions/ublocklite/rulesets/scripting/generichigh/kor-1.css +0 -2
  225. package/extensions/ublocklite/rulesets/scripting/generichigh/ublock-filters.css +0 -4
  226. package/extensions/ublocklite/rulesets/scripting/procedural/adguard-mobile.js +3 -3
  227. package/extensions/ublocklite/rulesets/scripting/procedural/annoyances-cookies.js +1 -1
  228. package/extensions/ublocklite/rulesets/scripting/procedural/annoyances-overlays.js +3 -3
  229. package/extensions/ublocklite/rulesets/scripting/procedural/chn-0.js +3 -3
  230. package/extensions/ublocklite/rulesets/scripting/procedural/deu-0.js +3 -3
  231. package/extensions/ublocklite/rulesets/scripting/procedural/easylist.js +3 -3
  232. package/extensions/ublocklite/rulesets/scripting/procedural/fin-0.js +3 -3
  233. package/extensions/ublocklite/rulesets/scripting/procedural/fra-0.js +3 -3
  234. package/extensions/ublocklite/rulesets/scripting/procedural/jpn-1.js +3 -3
  235. package/extensions/ublocklite/rulesets/scripting/procedural/kor-1.js +3 -3
  236. package/extensions/ublocklite/rulesets/scripting/procedural/nor-0.js +3 -3
  237. package/extensions/ublocklite/rulesets/scripting/procedural/rou-1.js +3 -3
  238. package/extensions/ublocklite/rulesets/scripting/procedural/rus-0.js +3 -3
  239. package/extensions/ublocklite/rulesets/scripting/procedural/rus-1.js +1 -1
  240. package/extensions/ublocklite/rulesets/scripting/procedural/spa-0.js +3 -3
  241. package/extensions/ublocklite/rulesets/scripting/procedural/spa-1.js +3 -3
  242. package/extensions/ublocklite/rulesets/scripting/procedural/swe-1.js +3 -3
  243. package/extensions/ublocklite/rulesets/scripting/procedural/ublock-filters.js +3 -3
  244. package/extensions/ublocklite/rulesets/scripting/procedural/ukr-0.js +3 -3
  245. package/extensions/ublocklite/rulesets/scripting/scriptlet/adguard-mobile.abort-current-script.js +2 -2
  246. package/extensions/ublocklite/rulesets/scripting/scriptlet/adguard-mobile.set-cookie-reload.js +11 -2
  247. package/extensions/ublocklite/rulesets/scripting/scriptlet/adguard-mobile.set-cookie.js +10 -1
  248. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-cookie.js +12 -3
  249. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.set-local-storage-item.js +2 -2
  250. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-click-element.js +2 -2
  251. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-cookie-reload.js +10 -1
  252. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-cookie.js +12 -3
  253. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-cookies.trusted-set-local-storage-item.js +2 -2
  254. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-others.remove-cookie.js +19 -9
  255. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-others.set-cookie.js +10 -1
  256. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.abort-current-script.js +1 -1
  257. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.abort-on-property-read.js +1 -1
  258. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.addEventListener-defuser.js +2 -2
  259. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.prevent-fetch.js +2 -2
  260. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.remove-attr.js +2 -2
  261. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.remove-class.js +1 -1
  262. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.remove-cookie.js +17 -7
  263. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.remove-node-text.js +2 -2
  264. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.set-constant.js +1 -1
  265. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.set-cookie.js +12 -3
  266. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.set-local-storage-item.js +2 -2
  267. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.trusted-set-cookie.js +10 -1
  268. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-overlays.trusted-set-local-storage-item.js +2 -2
  269. package/extensions/ublocklite/rulesets/scripting/scriptlet/annoyances-social.set-cookie.js +10 -1
  270. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.abort-current-script.js +2 -2
  271. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.abort-on-property-read.js +2 -2
  272. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.evaldata-prune.js +1 -1
  273. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.json-prune.js +3 -3
  274. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.prevent-setTimeout.js +1 -1
  275. package/extensions/ublocklite/rulesets/scripting/scriptlet/chn-0.set-constant.js +1 -1
  276. package/extensions/ublocklite/rulesets/scripting/scriptlet/cze-0.json-prune-xhr-response.js +597 -0
  277. package/extensions/ublocklite/rulesets/scripting/scriptlet/cze-0.remove-cookie.js +17 -7
  278. package/extensions/ublocklite/rulesets/scripting/scriptlet/easyprivacy.set-cookie.js +10 -1
  279. package/extensions/ublocklite/rulesets/scripting/scriptlet/fin-0.set-cookie.js +10 -1
  280. package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.abort-on-property-read.js +2 -2
  281. package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.adjust-setTimeout.js +2 -2
  282. package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.set-constant.js +1 -1
  283. package/extensions/ublocklite/rulesets/scripting/scriptlet/fra-0.set-cookie.js +10 -1
  284. package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.prevent-fetch.js +2 -2
  285. package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.remove-cookie.js +17 -7
  286. package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.set-constant.js +1 -1
  287. package/extensions/ublocklite/rulesets/scripting/scriptlet/hun-0.set-cookie.js +10 -1
  288. package/extensions/ublocklite/rulesets/scripting/scriptlet/irn-0.remove-cookie.js +17 -7
  289. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.abort-current-script.js +2 -2
  290. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.addEventListener-defuser.js +2 -2
  291. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.adjust-setTimeout.js +1 -1
  292. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.prevent-fetch.js +1 -1
  293. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.prevent-setTimeout.js +2 -2
  294. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.prevent-window-open.js +1 -1
  295. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.remove-node-text.js +1 -1
  296. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.set-constant.js +2 -2
  297. package/extensions/ublocklite/rulesets/scripting/scriptlet/jpn-1.set-cookie-reload.js +10 -1
  298. package/extensions/ublocklite/rulesets/scripting/scriptlet/kor-1.prevent-setTimeout.js +2 -2
  299. package/extensions/ublocklite/rulesets/scripting/scriptlet/kor-1.set-constant.js +2 -2
  300. package/extensions/ublocklite/rulesets/scripting/scriptlet/kor-1.set-cookie.js +10 -1
  301. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.abort-on-property-read.js +1 -1
  302. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.abort-on-stack-trace.js +1 -1
  303. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.addEventListener-defuser.js +1 -1
  304. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.href-sanitizer.js +1 -1
  305. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.prevent-setTimeout.js +1 -1
  306. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.prevent-xhr.js +1 -1
  307. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.remove-cookie.js +19 -9
  308. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.remove-node-text.js +1 -1
  309. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-attr.js +1 -1
  310. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-constant.js +3 -3
  311. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-cookie-reload.js +11 -2
  312. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-0.set-cookie.js +12 -3
  313. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.addEventListener-defuser.js +1 -1
  314. package/extensions/ublocklite/rulesets/scripting/scriptlet/rus-1.remove-cookie.js +19 -9
  315. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.abort-current-script.js +2 -2
  316. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.abort-on-property-read.js +2 -2
  317. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.abort-on-stack-trace.js +1 -1
  318. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.addEventListener-defuser.js +2 -2
  319. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.adjust-setInterval.js +2 -2
  320. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.adjust-setTimeout.js +2 -2
  321. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-fetch.js +2 -2
  322. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-setTimeout.js +1 -1
  323. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-window-open.js +2 -2
  324. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.prevent-xhr.js +1 -1
  325. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.remove-attr.js +2 -2
  326. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.remove-class.js +2 -2
  327. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.set-constant.js +2 -2
  328. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.set-cookie-reload.js +10 -1
  329. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.set-cookie.js +12 -3
  330. package/extensions/ublocklite/rulesets/scripting/scriptlet/spa-1.set-local-storage-item.js +1 -1
  331. package/extensions/ublocklite/rulesets/scripting/scriptlet/swe-1.addEventListener-defuser.js +2 -2
  332. package/extensions/ublocklite/rulesets/scripting/scriptlet/swe-1.remove-cookie.js +17 -7
  333. package/extensions/ublocklite/rulesets/scripting/scriptlet/swe-1.set-cookie.js +10 -1
  334. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.abort-current-script.js +2 -2
  335. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.abort-on-property-read.js +2 -2
  336. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.addEventListener-defuser.js +1 -1
  337. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.adjust-setInterval.js +2 -2
  338. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.prevent-setTimeout.js +2 -2
  339. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.prevent-window-open.js +1 -1
  340. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.remove-attr.js +2 -2
  341. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.remove-node-text.js +2 -2
  342. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.set-constant.js +2 -2
  343. package/extensions/ublocklite/rulesets/scripting/scriptlet/tur-0.set-cookie.js +10 -1
  344. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-badware.abort-current-script.js +2 -2
  345. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-current-script.js +3 -3
  346. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-property-read.js +2 -2
  347. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-property-write.js +1 -1
  348. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.abort-on-stack-trace.js +2 -2
  349. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.addEventListener-defuser.js +3 -3
  350. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setInterval.js +3 -3
  351. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.adjust-setTimeout.js +2 -2
  352. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.alert-buster.js +1 -1
  353. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.href-sanitizer.js +1 -1
  354. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.json-edit.js +30 -7
  355. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.json-prune-xhr-response.js +3 -3
  356. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.json-prune.js +3 -3
  357. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.jsonl-edit-xhr-response.js +19 -2
  358. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.noeval-if.js +3 -3
  359. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-fetch.js +2 -2
  360. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-refresh.js +1 -1
  361. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-setTimeout.js +2 -2
  362. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-window-open.js +2 -2
  363. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.prevent-xhr.js +3 -3
  364. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-attr.js +2 -2
  365. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-cookie.js +19 -9
  366. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.remove-node-text.js +3 -3
  367. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-constant.js +3 -3
  368. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-cookie.js +12 -3
  369. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-local-storage-item.js +2 -2
  370. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.set-session-storage-item.js +2 -2
  371. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-argument.js +2 -2
  372. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-node-text.js +3 -3
  373. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-replace-outbound-text.js +2 -2
  374. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-constant.js +2 -2
  375. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-cookie-reload.js +10 -1
  376. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-cookie.js +13 -4
  377. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-set-local-storage-item.js +399 -0
  378. package/extensions/ublocklite/rulesets/scripting/scriptlet/ublock-filters.trusted-suppress-native-method.js +2 -2
  379. package/extensions/ublocklite/rulesets/scripting/scriptlet/ubol-tests.jsonl-edit-fetch-response.js +19 -2
  380. package/extensions/ublocklite/rulesets/scripting/scriptlet/ubol-tests.jsonl-edit-xhr-response.js +19 -2
  381. package/extensions/ublocklite/rulesets/scripting/scriptlet/ukr-0.abort-current-script.js +2 -2
  382. package/extensions/ublocklite/rulesets/scripting/scriptlet/ukr-0.abort-on-property-read.js +1 -1
  383. package/extensions/ublocklite/rulesets/scripting/scriptlet/ukr-0.abort-on-property-write.js +1 -1
  384. package/extensions/ublocklite/rulesets/scripting/scriptlet/ukr-0.remove-class.js +1 -1
  385. package/extensions/ublocklite/rulesets/scripting/scriptlet/ukr-0.set-constant.js +2 -2
  386. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-current-script.js +1 -1
  387. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.abort-on-property-read.js +2 -2
  388. package/extensions/ublocklite/rulesets/scripting/scriptlet/vie-1.set-constant.js +2 -2
  389. package/extensions/ublocklite/rulesets/scripting/specific/adguard-mobile.js +3 -3
  390. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-cookies.js +3 -3
  391. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-others.js +3 -3
  392. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-overlays.js +3 -3
  393. package/extensions/ublocklite/rulesets/scripting/specific/annoyances-social.js +3 -3
  394. package/extensions/ublocklite/rulesets/scripting/specific/chn-0.js +3 -3
  395. package/extensions/ublocklite/rulesets/scripting/specific/deu-0.js +3 -3
  396. package/extensions/ublocklite/rulesets/scripting/specific/easylist.js +3 -3
  397. package/extensions/ublocklite/rulesets/scripting/specific/fin-0.js +3 -3
  398. package/extensions/ublocklite/rulesets/scripting/specific/fra-0.js +3 -3
  399. package/extensions/ublocklite/rulesets/scripting/specific/grc-0.js +3 -3
  400. package/extensions/ublocklite/rulesets/scripting/specific/hun-0.js +3 -3
  401. package/extensions/ublocklite/rulesets/scripting/specific/idn-0.js +3 -3
  402. package/extensions/ublocklite/rulesets/scripting/specific/ind-0.js +3 -3
  403. package/extensions/ublocklite/rulesets/scripting/specific/isr-0.js +1 -1
  404. package/extensions/ublocklite/rulesets/scripting/specific/ita-0.js +3 -3
  405. package/extensions/ublocklite/rulesets/scripting/specific/jpn-1.js +3 -3
  406. package/extensions/ublocklite/rulesets/scripting/specific/kor-1.js +3 -3
  407. package/extensions/ublocklite/rulesets/scripting/specific/ltu-0.js +3 -3
  408. package/extensions/ublocklite/rulesets/scripting/specific/nld-0.js +3 -3
  409. package/extensions/ublocklite/rulesets/scripting/specific/nor-0.js +3 -3
  410. package/extensions/ublocklite/rulesets/scripting/specific/rou-1.js +3 -3
  411. package/extensions/ublocklite/rulesets/scripting/specific/rus-0.js +3 -3
  412. package/extensions/ublocklite/rulesets/scripting/specific/spa-0.js +3 -3
  413. package/extensions/ublocklite/rulesets/scripting/specific/spa-1.js +3 -3
  414. package/extensions/ublocklite/rulesets/scripting/specific/swe-1.js +3 -3
  415. package/extensions/ublocklite/rulesets/scripting/specific/tur-0.js +3 -3
  416. package/extensions/ublocklite/rulesets/scripting/specific/ublock-badware.js +3 -3
  417. package/extensions/ublocklite/rulesets/scripting/specific/ublock-filters.js +3 -3
  418. package/extensions/ublocklite/rulesets/scripting/specific/ukr-0.js +3 -3
  419. package/extensions/ublocklite/rulesets/scripting/specific/vie-1.js +3 -3
  420. package/extensions/ublocklite/rulesets/scriptlet-details.json +202 -186
  421. package/extensions/ublocklite/rulesets/strictblock/adguard-mobile.json +1 -34
  422. package/extensions/ublocklite/rulesets/strictblock/chn-0.json +262 -248
  423. package/extensions/ublocklite/rulesets/strictblock/dpollock-0.json +2 -0
  424. package/extensions/ublocklite/rulesets/strictblock/easylist.json +3 -2
  425. package/extensions/ublocklite/rulesets/strictblock/easyprivacy.json +44 -2
  426. package/extensions/ublocklite/rulesets/strictblock/fra-0.json +304 -62
  427. package/extensions/ublocklite/rulesets/strictblock/grc-0.json +1 -7
  428. package/extensions/ublocklite/rulesets/strictblock/ita-0.json +1 -0
  429. package/extensions/ublocklite/rulesets/strictblock/jpn-1.json +159 -155
  430. package/extensions/ublocklite/rulesets/strictblock/kor-1.json +1 -0
  431. package/extensions/ublocklite/rulesets/strictblock/nor-0.json +1 -1
  432. package/extensions/ublocklite/rulesets/strictblock/pgl.json +12 -18
  433. package/extensions/ublocklite/rulesets/strictblock/spa-1.json +0 -11
  434. package/extensions/ublocklite/rulesets/strictblock/stevenblack-hosts.json +9228 -1275
  435. package/extensions/ublocklite/rulesets/strictblock/tur-0.json +0 -13
  436. package/extensions/ublocklite/rulesets/strictblock/ublock-badware.json +635 -635
  437. package/extensions/ublocklite/rulesets/strictblock/ublock-filters.json +1 -1
  438. package/extensions/ublocklite/rulesets/strictblock/ukr-0.json +0 -75
  439. package/extensions/ublocklite/rulesets/strictblock/urlhaus-full.json +587 -976
  440. package/extensions/ublocklite/rulesets/strictblock/vie-1.json +3 -2
  441. package/extensions/ublocklite/rulesets/urlskip/ublock-filters.json +19 -0
  442. package/extensions/ublocklite/ublock.zip +0 -0
  443. package/package.json +25 -20
  444. package/src/routes/management/tests/management.spec.ts +25 -0
  445. package/src/server.ts +4 -5
  446. package/src/shared/utils/enjoi-resolver.ts +565 -0
  447. package/static/docs/swagger.json +10 -10
  448. package/static/docs/swagger.min.json +9 -9
  449. package/static/function/client.js +86 -39
  450. package/static/function/index.html +86 -39
  451. /package/extensions/ublocklite/lib/codemirror/{LICENSE → codemirror-quickstart.LICENSE} +0 -0
@@ -19,871 +19,613 @@
19
19
  Home: https://github.com/gorhill/uBlock
20
20
  */
21
21
 
22
- import {
23
- browser,
24
- localRead,
25
- localRemove,
26
- localWrite,
27
- sendMessage,
28
- } from './ext.js';
29
- import { dom, qs$ } from './dom.js';
30
- import { rulesFromText, textFromRules } from './dnr-parser.js';
31
- import { dnr } from './ext-compat.js';
32
- import { i18n$ } from './i18n.js';
22
+ import { dom, qs$, qsa$ } from './dom.js';
23
+ import { localRead, localWrite, sendMessage } from './ext.js';
24
+ import { faIconsInit } from './fa-icons.js';
25
+ import { i18n } from './i18n.js';
33
26
 
34
27
  /******************************************************************************/
35
28
 
36
- // Details of YAML document(s) intersecting with a text span. If the text span
37
- // starts on a YAML document divider, the previous YAML document will be
38
- // included. If the text span ends on a YAML document divider, the next YAML
39
- // document will be included.
29
+ class Editor {
30
+ constructor() {
31
+ this.lastSavedText = '';
32
+ this.view = null;
33
+ this.reYamlDocSeparator = /^(?:---|...)\s*$/;
34
+ this.modifiedRange = { start: 0, end: 0 };
35
+ this.updateTimer = undefined;
36
+ this.ioPanel = self.cm6.createViewPanel();
37
+ this.summaryPanel = self.cm6.createViewPanel();
38
+ this.panels = [];
39
+ this.editors = {};
40
+ }
40
41
 
41
- function snapToYamlDocument(doc, start, end) {
42
- let yamlDocStart = doc.lineAt(start).number;
43
- if ( reYamlDocSeparator.test(doc.line(yamlDocStart).text) ) {
44
- if ( yamlDocStart > 1 ) {
45
- yamlDocStart -= 1;
42
+ async init() {
43
+ await Promise.all([
44
+ import('./mode-editor.js').then(module => {
45
+ this.editors['modes'] = new module.ModeEditor(this);
46
+ }),
47
+ import('./ro-dnr-editor.js').then(module => {
48
+ this.editors['dnr.ro'] = new module.ReadOnlyDNREditor(this);
49
+ }),
50
+ import('./rw-dnr-editor.js').then(module => {
51
+ this.editors['dnr.rw'] = new module.ReadWriteDNREditor(this);
52
+ }),
53
+ ]);
54
+ const rulesetDetails = await sendMessage({ what: 'getRulesetDetails' });
55
+ const parent = qs$('#editors optgroup');
56
+ for ( const details of rulesetDetails ) {
57
+ const option = document.createElement('option');
58
+ option.value = `dnr.ro.${details.id}`;
59
+ option.textContent = details.name;
60
+ parent.append(option);
46
61
  }
62
+ this.validModes = Array.from(qsa$('#editors option')).map(a => a.value);
63
+ const mode = await localRead('dashboard.develop.editor');
64
+ this.editorFromMode(mode);
65
+ const text = this.normalizeEditorText(await this.editor.getText(this.mode));
66
+ const viewConfig = {
67
+ text,
68
+ yamlLike: true,
69
+ oneDark: dom.cl.has(':root', 'dark'),
70
+ updateListener: info => { this.viewUpdateListener(info); },
71
+ saveListener: ( ) => { this.saveEditorText(); },
72
+ lineError: true,
73
+ spanError: true,
74
+ // https://codemirror.net/examples/autocompletion/
75
+ autocompletion: {
76
+ override: [
77
+ context => {
78
+ return this.autoComplete(context);
79
+ },
80
+ ],
81
+ activateOnCompletion: ( ) => true,
82
+ },
83
+ gutterClick: (view, info) => {
84
+ return this.gutterClick(view, info);
85
+ },
86
+ hoverTooltip: (view, pos, side) => {
87
+ return this.hoverTooltip(view, pos, side);
88
+ },
89
+ streamParser: this.streamParser,
90
+ foldService: (state, from) => {
91
+ return this.foldService(state, from);
92
+ },
93
+ readOnly: this.isReadOnly(),
94
+ };
95
+ viewConfig.panels = [ this.ioPanel, this.summaryPanel, ...this.panels ];
96
+ this.view = self.cm6.createEditorView(viewConfig, qs$('#cm-container'));
97
+ this.lastSavedText = text;
98
+ self.cm6.foldAll(this.view);
99
+ self.cm6.resetUndoRedo(this.view);
100
+ this.updateIOPanel();
101
+ this.editor.on?.(this);
102
+ this.modifiedRange.start = 1;
103
+ this.modifiedRange.end = this.view.state.doc.lines;
104
+ this.updateViewAsync();
47
105
  }
48
- while ( yamlDocStart > 1 ) {
49
- const line = doc.line(yamlDocStart);
50
- if ( reYamlDocSeparator.test(line.text) ) { break; }
51
- yamlDocStart -= 1;
106
+
107
+ normalizeEditorText(text) {
108
+ text ||= '';
109
+ text = text.trim();
110
+ if ( text !== '' ) { text += '\n'; }
111
+ return text;
52
112
  }
53
- const lastLine = doc.lines;
54
- let yamlDocEnd = doc.lineAt(end).number;
55
- if ( reYamlDocSeparator.test(doc.line(yamlDocEnd).text) ) {
56
- if ( yamlDocEnd < lastLine ) {
57
- yamlDocEnd += 1;
113
+
114
+ setEditorText(text, saved = false) {
115
+ text = this.normalizeEditorText(text);
116
+ if ( saved ) {
117
+ this.lastSavedText = text;
58
118
  }
119
+ this.view.dispatch({
120
+ changes: {
121
+ from: 0, to: this.view.state.doc.length,
122
+ insert: text,
123
+ },
124
+ });
125
+ this.view.focus();
59
126
  }
60
- while ( yamlDocEnd < lastLine ) {
61
- const line = doc.line(yamlDocEnd);
62
- if ( reYamlDocSeparator.test(line.text) ) { break; }
63
- yamlDocEnd += 1;
64
- }
65
- return { yamlDocStart, yamlDocEnd };
66
- }
67
-
68
- function rangeFromTransaction(transaction) {
69
- let from, to;
70
- transaction.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
71
- if ( from === undefined || fromB < from ) { from = fromB; }
72
- if ( to === undefined || toB > to ) { to = toB; }
73
- });
74
- return { from, to };
75
- }
76
127
 
77
- function addToModifiedRange(transaction) {
78
- const { from, to } = rangeFromTransaction(transaction);
79
- if ( from === undefined || to === undefined ) { return; }
80
- const { newDoc } = transaction;
81
- const { yamlDocStart, yamlDocEnd } = snapToYamlDocument(newDoc, from, to);
82
- if ( modifiedRange.start === -1 || yamlDocStart < modifiedRange.start ) {
83
- modifiedRange.start = yamlDocStart;
84
- }
85
- if ( modifiedRange.end === -1 || yamlDocEnd > modifiedRange.end ) {
86
- modifiedRange.end = yamlDocEnd;
128
+ getEditorText() {
129
+ return this.view.state.doc.toString();
87
130
  }
88
- }
89
131
 
90
- const reYamlDocSeparator = /^(?:---|...)\s*$/;
91
- const modifiedRange = { start: -1, end: -1 };
92
-
93
- /******************************************************************************/
94
-
95
- function rulesFromJSON(json) {
96
- let content = json.trim();
97
- if ( /^[[{]/.test(content) === false ) {
98
- const match = /^[^[{]+/.exec(content);
99
- if ( match === null ) { return; }
100
- content = content.slice(match[0].length);
101
- }
102
- const firstChar = content.charAt(0);
103
- const expectedLastChar = firstChar === '[' ? ']' : '}';
104
- if ( content.at(-1) !== expectedLastChar ) {
105
- const re = new RegExp(`\\${expectedLastChar}[^\\${expectedLastChar}]+$`);
106
- const match = re.exec(content);
107
- if ( match === null ) { return; }
108
- content = content.slice(0, match.index+1);
109
- }
110
- if ( content.startsWith('{') && content.endsWith('}') ) {
111
- content = `[${content}]`;
112
- }
113
- try {
114
- const rules = JSON.parse(content);
115
- if ( Array.isArray(rules) ) { return rules; }
116
- }
117
- catch {
132
+ editorTextChanged() {
133
+ const text = this.normalizeEditorText(this.getEditorText());
134
+ return text !== this.lastSavedText;
118
135
  }
119
- }
120
-
121
- /******************************************************************************/
122
136
 
123
- function lineIndentAt(line) {
124
- const match = /^(?: {2})*/.exec(line.text);
125
- const indent = match !== null ? match[0].length : -1;
126
- if ( indent === -1 || (indent & 1) !== 0 ) { return -1; }
127
- return indent / 2;
128
- }
137
+ async selectEditor(mode) {
138
+ if ( mode === this.mode ) { return; }
139
+ this.editorFromMode(mode);
140
+ const text = await this.editor.getText(this.mode);
141
+ this.setEditorText(text);
142
+ this.lastSavedText = this.getEditorText();
143
+ self.cm6.foldAll(this.view)
144
+ self.cm6.resetUndoRedo(this.view);
145
+ self.cm6.toggleReadOnly(this.view, this.isReadOnly());
146
+ this.updateIOPanel();
147
+ this.editor.on?.(this);
148
+ this.modifiedRange.start = 1;
149
+ this.modifiedRange.end = this.view.state.doc.lines;
150
+ this.updateViewAsync();
151
+ }
129
152
 
130
- function getScopeAt(from) {
131
- const { doc } = cmRules.state;
132
- const lineFrom = doc.lineAt(from);
133
- let depth = lineIndentAt(lineFrom);
134
- if ( depth === -1 ) { return; }
135
- const text = lineFrom.text.trim();
136
- if ( text.startsWith('#') ) { return; }
137
- const path = [];
138
- const pos = text.indexOf(':');
139
- if ( pos !== -1 ) {
140
- path.push(text.slice(0, pos+1));
141
- }
142
- let lineNo = lineFrom.number;
143
- while ( depth > 0 && lineNo > 1 ) {
144
- lineNo -= 1;
145
- const lineBefore = doc.line(lineNo);
146
- const text = lineBefore.text.trim();
147
- if ( text.startsWith('#') ) { continue; }
148
- if ( lineIndentAt(lineBefore) > (depth-1) ) { continue; }
149
- const match = /^- ([^:]+:)/.exec(text);
150
- if ( match !== null ) {
151
- path.unshift(match[1]);
153
+ editorFromMode(mode) {
154
+ if ( this.validModes.includes(mode) === false ) {
155
+ mode = 'modes';
156
+ }
157
+ if ( mode === this.mode ) { return mode; }
158
+ let editor;
159
+ if ( mode === 'modes' ) {
160
+ editor = this.editors['modes'];
161
+ } else if ( mode.startsWith('dnr.rw.') ) {
162
+ editor = this.editors['dnr.rw'];
163
+ } else if ( mode.startsWith('dnr.ro.') ) {
164
+ editor = this.editors['dnr.ro'];
152
165
  } else {
153
- path.unshift(text);
166
+ return;
154
167
  }
155
- depth -= 1;
168
+ this.editor?.off?.(this);
169
+ this.editor = editor;
170
+ this.mode = mode;
171
+ const select = qs$('#editors');
172
+ select.value = mode;
156
173
  }
157
- return path.join('');
158
- }
159
174
 
160
- function getAutocompleteCandidates(from) {
161
- const scope = getScopeAt(from);
162
- switch ( scope ) {
163
- case '':
164
- return {
165
- before: /^$/,
166
- candidates: [
167
- { token: 'action:', after: '\n ' },
168
- { token: 'condition:', after: '\n ' },
169
- { token: 'priority:', after: ' ' },
170
- { token: '---', after: '\n' },
171
- ]
172
- };
173
- case 'action:':
174
- return {
175
- before: /^ {2}$/,
176
- candidates: [
177
- { token: 'type:', after: ' ' },
178
- { token: 'redirect:', after: '\n ' },
179
- { token: 'requestHeaders:', after: '\n - header: ' },
180
- { token: 'responseHeaders:', after: '\n - header: ' },
181
- ],
182
- };
183
- case 'action:type:':
184
- return {
185
- before: /: $/,
186
- candidates: [
187
- { token: 'block', after: '\n ' },
188
- { token: 'redirect', after: '\n ' },
189
- { token: 'allow', after: '\n ' },
190
- { token: 'modifyHeaders', after: '\n ' },
191
- { token: 'upgradeScheme', after: '\n ' },
192
- { token: 'allowAllRequest', after: '\n ' },
193
- ],
194
- };
195
- case 'action:redirect:':
196
- return {
197
- before: /^ {4}$/,
198
- candidates: [
199
- { token: 'extensionPath:', after: ' ' },
200
- { token: 'regexSubstitution:', after: ' ' },
201
- { token: 'transform:', after: '\n ' },
202
- { token: 'url:', after: ' ' },
203
- ],
204
- };
205
- case 'action:redirect:transform:':
206
- return {
207
- before: /^ {6}$/,
208
- candidates: [
209
- { token: 'fragment:', after: ' ' },
210
- { token: 'host:', after: ' ' },
211
- { token: 'path:', after: ' ' },
212
- { token: 'port:', after: ' ' },
213
- { token: 'query:', after: ' ' },
214
- { token: 'scheme:', after: ' ' },
215
- { token: 'queryTransform:', after: '\n ' },
216
- ],
217
- };
218
- case 'action:redirect:transform:queryTransform:':
219
- return {
220
- before: /^ {8}$/,
221
- candidates: [
222
- { token: 'addOrReplaceParams:', after: '\n - ' },
223
- { token: 'removeParams:', after: '\n - ' },
224
- ],
225
- };
226
- case 'action:responseHeaders:':
227
- case 'action:requestHeaders:':
228
- return {
229
- before: /^ {4}- $/,
230
- candidates: [
231
- { token: 'header:', after: ' ' },
232
- ],
233
- };
234
- case 'action:responseHeaders:header:':
235
- case 'action:requestHeaders:header:':
236
- return {
237
- before: /^ {6}$/,
238
- candidates: [
239
- { token: 'operation:', after: ' ' },
240
- { token: 'value:', after: ' ' },
241
- ],
242
- };
243
- case 'action:responseHeaders:header:operation:':
244
- case 'action:requestHeaders:header:operation:':
245
- return {
246
- before: /: $/,
247
- candidates: [
248
- { token: 'append', after: '\n value: ' },
249
- { token: 'set', after: '\n value: ' },
250
- { token: 'remove', after: '\n ' },
251
- ],
252
- };
253
- case 'condition:':
254
- return {
255
- before: /^ {2}$/,
256
- candidates: [
257
- { token: 'domainType:', after: ' ' },
258
- { token: 'isUrlFilterCaseSensitive:', after: ' ' },
259
- { token: 'regexFilter:', after: ' ' },
260
- { token: 'urlFilter:', after: ' ' },
261
- { token: 'initiatorDomains:', after: '\n - ' },
262
- { token: 'excludedInitiatorDomains:', after: '\n - ' },
263
- { token: 'requestDomains:', after: '\n - ' },
264
- { token: 'excludedRequestDomains:', after: '\n - ' },
265
- { token: 'resourceTypes:', after: '\n - ' },
266
- { token: 'excludedResourceTypes:', after: '\n - ' },
267
- { token: 'requestMethods:', after: '\n - ' },
268
- { token: 'excludedRequestMethods:', after: '\n - ' },
269
- { token: 'responseHeaders:', after: '\n - ' },
270
- { token: 'excludedResponseHeaders:', after: '\n - ' },
271
- ],
272
- };
273
- case 'condition:domainType:':
274
- return {
275
- before: /: $/,
276
- candidates: [
277
- { token: 'firstParty', after: '\n ' },
278
- { token: 'thirdParty', after: '\n ' },
279
- ],
280
- };
281
- case 'condition:isUrlFilterCaseSensitive:':
282
- return {
283
- before: /: $/,
284
- candidates: [
285
- { token: 'true', after: '\n ' },
286
- { token: 'false', after: '\n ' },
287
- ],
288
- };
289
- case 'condition:requestMethods:':
290
- case 'condition:excludedRequestMethods:':
291
- return {
292
- before: /^ {4}- $/,
293
- candidates: [
294
- { token: 'connect', after: '\n - ' },
295
- { token: 'delete', after: '\n - ' },
296
- { token: 'get', after: '\n - ' },
297
- { token: 'head', after: '\n - ' },
298
- { token: 'options', after: '\n - ' },
299
- { token: 'patch', after: '\n - ' },
300
- { token: 'post', after: '\n - ' },
301
- { token: 'put', after: '\n - ' },
302
- { token: 'other', after: '\n ' },
303
- ],
304
- };
305
- case 'condition:resourceTypes:':
306
- case 'condition:excludedResourceTypes:':
307
- return {
308
- before: /^ {4}- $/,
309
- candidates: [
310
- { token: 'main_frame', after: '\n - ' },
311
- { token: 'sub_frame', after: '\n - ' },
312
- { token: 'stylesheet', after: '\n - ' },
313
- { token: 'script', after: '\n - ' },
314
- { token: 'image', after: '\n - ' },
315
- { token: 'font', after: '\n - ' },
316
- { token: 'object', after: '\n - ' },
317
- { token: 'xmlhttprequest', after: '\n - ' },
318
- { token: 'ping', after: '\n - ' },
319
- { token: 'csp_report', after: '\n - ' },
320
- { token: 'media', after: '\n - ' },
321
- { token: 'websocket', after: '\n - ' },
322
- { token: 'webtransport', after: '\n - ' },
323
- { token: 'webbundle', after: '\n - ' },
324
- { token: 'other', after: '\n ' },
325
- ],
326
- };
175
+ isReadOnly() {
176
+ return typeof this.editor.saveEditorText !== 'function';
327
177
  }
328
- }
329
178
 
330
- function autoComplete(context) {
331
- const match = context.matchBefore(/[\w-]*/);
332
- if ( match === undefined ) { return null; }
333
- const result = getAutocompleteCandidates(match.from);
334
- if ( result === undefined ) { return null; }
335
- if ( result.before !== undefined ) {
336
- const { doc } = context.state;
337
- const line = doc.lineAt(context.pos);
338
- const before = doc.sliceString(line.from, match.from);
339
- if ( result.before.test(before) === false ) { return null; }
340
- }
341
- const filtered = result.candidates.filter(e =>
342
- e.token !== match.text || e.after !== '\n'
343
- );
344
- return {
345
- from: match.from,
346
- options: filtered.map(e => ({ label: e.token, apply: `${e.token}${e.after}` })),
347
- validFor: /\w*/,
348
- };
349
- }
350
-
351
- /******************************************************************************/
352
-
353
- function setEditorText(text) {
354
- if ( text === undefined ) { return; }
355
- if ( text !== '' ) { text += '\n'; }
356
- cmRules.dispatch({
357
- changes: {
358
- from: 0, to: cmRules.state.doc.length,
359
- insert: text,
360
- },
361
- });
362
- cmRules.focus();
363
- }
364
-
365
- function getEditorText() {
366
- return cmRules.state.doc.toString();
367
- }
368
-
369
- /******************************************************************************/
370
-
371
- function saveEditorText() {
372
- const text = getEditorText().trim();
373
- const promise = text.length !== 0
374
- ? localWrite('userDnrRules', text)
375
- : localRemove('userDnrRules');
376
- promise.then(( ) => {
377
- lastSavedText = text;
378
- updateView();
379
- }).then(( ) =>
380
- sendMessage({ what: 'updateUserDnrRules' })
381
- ).then(result => {
382
- if ( result instanceof Object === false ) { return; }
383
- updateFeedbackPanel(result);
384
- });
385
- }
386
-
387
- /******************************************************************************/
388
-
389
- async function validateRegexes(regexes) {
390
- if ( regexes.length === 0 ) { return; }
391
- const promises = regexes.map(regex => validateRegex(regex));
392
- await Promise.all(promises);
393
- for ( const regex of regexes ) {
394
- const i = validatedRegexes.regexes.indexOf(regex);
395
- if ( i === -1 ) { continue; }
396
- const reason = validatedRegexes.results[i];
397
- if ( reason === true ) { continue; }
398
- const entries = self.cm6.findAll(cmRules,
399
- `(?<=\\bregexFilter: )${RegExp.escape(regex)}`
400
- );
401
- for ( const entry of entries ) {
402
- self.cm6.spanErrorAdd(cmRules, entry.from, entry.to, reason);
179
+ viewUpdateListener(info) {
180
+ if ( info.docChanged === false ) { return; }
181
+ for ( const transaction of info.transactions ) {
182
+ if ( transaction.docChanged === false ) { continue; }
183
+ this.addToModifiedRange(transaction);
184
+ if ( transaction.isUserEvent('delete.backward') ) {
185
+ this.smartBackspace(transaction);
186
+ } else if ( transaction.isUserEvent('input.paste') ) {
187
+ if ( this.editor.importFromPaste ) {
188
+ this.editor.importFromPaste(this, transaction);
189
+ }
190
+ } else if ( transaction.isUserEvent('input') ) {
191
+ if ( this.smartReturn(transaction) ) { continue; }
192
+ this.smartSpacebar(transaction);
193
+ }
403
194
  }
195
+ this.updateViewAsync();
404
196
  }
405
- }
406
197
 
407
- async function validateRegex(regex) {
408
- const details = await dnr.isRegexSupported({ regex });
409
- const result = details.isSupported || details.reason;
410
- if ( validatedRegexes.regexes.length > 32 ) {
411
- validatedRegexes.regexes.pop();
412
- validatedRegexes.results.pop();
198
+ updateViewAsync() {
199
+ if ( this.updateTimer !== undefined ) { return; }
200
+ this.updateTimer = self.setTimeout(( ) => {
201
+ this.updateTimer = undefined;
202
+ this.updateView();
203
+ }, 71);
413
204
  }
414
- validatedRegexes.regexes.unshift(regex);
415
- validatedRegexes.results.unshift(result);
416
- }
417
-
418
- const validatedRegexes = {
419
- regexes: [],
420
- results: [],
421
- };
422
205
 
423
- /******************************************************************************/
206
+ updateView() {
207
+ const { doc } = this.view.state;
208
+ const changed = this.editorTextChanged();
209
+ dom.attr('#apply', 'disabled', changed ? null : '');
210
+ dom.attr('#revert', 'disabled', changed ? null : '');
211
+ if ( typeof this.editor.updateView !== 'function' ) { return; }
212
+ let { start, end } = this.modifiedRange;
213
+ if ( start === 0 || end === 0 ) { return; }
214
+ this.modifiedRange.start = this.modifiedRange.end = 0;
215
+ if ( start > doc.lines ) { start = doc.lines; }
216
+ if ( end > doc.lines ) { end = doc.lines; }
217
+ self.cm6.lineErrorClear(this.view, start, end);
218
+ self.cm6.spanErrorClear(this.view, start, end);
219
+ const firstLine = doc.line(start);
220
+ const lastLine = doc.line(end);
221
+ this.editor.updateView(this, firstLine, lastLine);
222
+ }
424
223
 
425
- function updateView() {
426
- const { doc } = cmRules.state;
427
- const changed = doc.toString().trim() !==
428
- lastSavedText.trim();
429
- dom.attr('#dnrRulesApply', 'disabled', changed ? null : '');
430
- dom.attr('#dnrRulesRevert', 'disabled', changed ? null : '');
431
- const { start, end } = modifiedRange;
432
- if ( start === -1 || end === -1 ) { return; }
433
- modifiedRange.start = modifiedRange.end = -1;
434
- self.cm6.lineErrorClear(cmRules, start, end);
435
- self.cm6.spanErrorClear(cmRules, start, end);
436
- const firstLine = doc.line(start);
437
- const lastLine = doc.line(end);
438
- const text = doc.sliceString(firstLine.from, lastLine.to);
439
- const { bad } = rulesFromText(text);
440
- if ( Array.isArray(bad) && bad.length !== 0 ) {
441
- self.cm6.lineErrorAdd(cmRules, bad.map(i => i + start));
442
- }
443
- const entries = self.cm6.findAll(
444
- cmRules,
445
- '\\bregexFilter: (\\S+)',
446
- firstLine.from,
447
- lastLine.to
448
- );
449
- const regexes = [];
450
- for ( const entry of entries ) {
451
- const regex = entry.match[1];
452
- const i = validatedRegexes.regexes.indexOf(regex);
453
- if ( i !== -1 ) {
454
- const reason = validatedRegexes.results[i];
455
- if ( reason === true ) { continue; }
456
- self.cm6.spanErrorAdd(cmRules, entry.from+13, entry.to, reason);
457
- } else {
458
- regexes.push(regex);
224
+ updateIOPanel() {
225
+ const ioButtons = [];
226
+ if ( this.editor.saveEditorText ) {
227
+ ioButtons.push('apply', 'revert');
228
+ }
229
+ if ( this.editor.importFromFile ) {
230
+ ioButtons.push('import');
231
+ }
232
+ if ( this.editor.exportToFile ) {
233
+ ioButtons.push('export');
459
234
  }
235
+ if ( ioButtons.length === 0 ) {
236
+ return this.ioPanel.render(this.view, null);
237
+ }
238
+ const template = document.querySelector('template.io-panel');
239
+ const fragment = template.content.cloneNode(true);
240
+ const root = fragment.querySelector('.io-panel');
241
+ i18n.render(root);
242
+ faIconsInit(root);
243
+ root.dataset.io = ioButtons.join(' ');
244
+ const config = {
245
+ dom: root,
246
+ mount: ( ) => {
247
+ dom.on('#apply', 'click', ( ) => {
248
+ this.saveEditorText();
249
+ });
250
+ dom.on('#revert', 'click', ( ) => {
251
+ this.revertEditorText();
252
+ });
253
+ dom.on('#import', 'click', ( ) => {
254
+ this.importFromFile()
255
+ });
256
+ dom.on('#export', 'click', ( ) => {
257
+ this.exportToFile();
258
+ });
259
+ }
260
+ };
261
+ this.ioPanel.render(this.view, config);
460
262
  }
461
- validateRegexes(regexes);
462
- }
463
263
 
464
- function updateViewAsync() {
465
- if ( updateViewAsync.timer !== undefined ) { return; }
466
- updateViewAsync.timer = self.setTimeout(( ) => {
467
- updateViewAsync.timer = undefined;
468
- updateView();
469
- }, 71);
470
- }
264
+ updateSummaryPanel(dom) {
265
+ if ( dom instanceof Object ) {
266
+ if ( this.updateSummaryPanel.timer !== undefined ) {
267
+ self.clearTimeout(this.updateSummaryPanel.timer);
268
+ this.updateSummaryPanel.timer = undefined;
269
+ }
270
+ return this.summaryPanel.render(this.view, { dom });
271
+ }
272
+ if ( this.updateSummaryPanel.timer !== undefined ) { return; }
273
+ this.updateSummaryPanel.timer = self.setTimeout(( ) => {
274
+ this.updateSummaryPanel.timer = undefined;
275
+ this.summaryPanel.render(this.view, null);
276
+ }, 157);
277
+ }
471
278
 
472
- /******************************************************************************/
279
+ autoComplete(context) {
280
+ if ( typeof this.editor.autoComplete !== 'function' ) { return null; }
281
+ return this.editor.autoComplete(this, context);
282
+ }
473
283
 
474
- function updateSummaryPanel(info) {
475
- self.cm6.showSummaryPanel(cmRules, {
476
- template: '.summary-panel',
477
- text: i18n$('dnrRulesCountInfo')
478
- .replace('{count}', (info.userDnrRuleCount || 0).toLocaleString()),
479
- });
480
- }
284
+ hoverTooltip(view, pos, side) {
285
+ if ( typeof this.editor.createTooltipWidget !== 'function' ) { return null; }
286
+ const details = view.domAtPos(pos);
287
+ const textNode = details.node;
288
+ if ( textNode.nodeType !== 3 ) { return null; }
289
+ const { parentElement } = textNode;
290
+ const targetElement = parentElement.closest('[data-tooltip]');
291
+ if ( targetElement === null ) { return null; }
292
+ const tooltipText = targetElement.getAttribute('data-tooltip');
293
+ if ( Boolean(tooltipText) === false ) { return null; }
294
+ const start = pos - details.offset;
295
+ const end = start + textNode.nodeValue.length;
296
+ if ( start === pos && side < 0 || end === pos && side > 0 ) { return null; }
297
+ return {
298
+ above: true,
299
+ pos: start,
300
+ end,
301
+ create: ( ) => {
302
+ return { dom: this.editor.createTooltipWidget(tooltipText) };
303
+ },
304
+ };
305
+ }
481
306
 
482
- function updateFeedbackPanel(info) {
483
- const errors = [];
484
- if ( Array.isArray(info.errors) ) {
485
- info.errors.forEach(e => errors.push(e));
307
+ foldService(state, from) {
308
+ if ( typeof this.editor.foldService !== 'function' ) { return null; }
309
+ return this.editor.foldService(state, from);
486
310
  }
487
- const text = errors.join('\n');
488
- self.cm6.showFeedbackPanel(cmRules, { template: '.feedback-panel', text });
489
- }
490
311
 
491
- /******************************************************************************/
312
+ // Details of YAML document(s) intersecting with a text span. If the text span
313
+ // starts on a YAML document divider, the previous YAML document will be
314
+ // included. If the text span ends on a YAML document divider, the next YAML
315
+ // document will be included.
492
316
 
493
- function importRulesFromFile() {
494
- const input = qs$('input[type="file"]');
495
- input.onchange = ev => {
496
- input.onchange = null;
497
- const file = ev.target.files[0];
498
- if ( file === undefined || file.name === '' ) { return; }
499
- if ( file.type !== 'application/json' ) { return; }
500
- const fr = new FileReader();
501
- fr.onload = ( ) => {
502
- if ( typeof fr.result !== 'string' ) { return; }
503
- const rules = rulesFromJSON(fr.result);
504
- if ( rules === undefined ) { return; }
505
- const text = textFromRules(rules);
506
- if ( text === undefined ) { return; }
507
- const { doc } = cmRules.state;
508
- const lastChars = doc.toString().trimEnd().slice(-4);
509
- const lastLine = doc.line(doc.lines);
510
- let from = lastLine.to;
511
- let prepend = '';
512
- if ( lastLine.text !== '' ) {
513
- prepend = '\n';
514
- } else {
515
- from = lastLine.from;
317
+ snapToYamlDocument(doc, start, end) {
318
+ let yamlDocStart = doc.lineAt(start).number;
319
+ if ( this.reYamlDocSeparator.test(doc.line(yamlDocStart).text) ) {
320
+ if ( yamlDocStart > 1 ) {
321
+ yamlDocStart -= 1;
516
322
  }
517
- if ( /(?:^|\n)---$/.test(lastChars) === false ) {
518
- prepend = `${prepend}---\n`;
323
+ }
324
+ while ( yamlDocStart > 1 ) {
325
+ const line = doc.line(yamlDocStart);
326
+ if ( this.reYamlDocSeparator.test(line.text) ) { break; }
327
+ yamlDocStart -= 1;
328
+ }
329
+ const lastLine = doc.lines;
330
+ let yamlDocEnd = doc.lineAt(end).number;
331
+ if ( this.reYamlDocSeparator.test(doc.line(yamlDocEnd).text) ) {
332
+ if ( yamlDocEnd < lastLine ) {
333
+ yamlDocEnd += 1;
519
334
  }
520
- cmRules.dispatch({ changes: { from, insert: `${prepend}${text}` } });
521
- self.cm6.foldAll(cmRules);
522
- cmRules.focus();
523
- };
524
- fr.readAsText(file);
525
- };
526
- // Reset to empty string, this will ensure a change event is properly
527
- // triggered if the user pick a file, even if it's the same as the last
528
- // one picked.
529
- input.value = '';
530
- input.click();
531
- }
532
-
533
- /******************************************************************************/
534
-
535
- function exportRulesToFile() {
536
- const text = getEditorText();
537
- const { rules } = rulesFromText(text);
538
- if ( Array.isArray(rules) === false ) { return; }
539
- let ruleId = 1;
540
- for ( const rule of rules ) {
541
- rule.id = ruleId++;
542
- }
543
- const filename = 'my-ubol-dnr-rules.json';
544
- const a = document.createElement('a');
545
- a.href = `data:application/json;charset=utf-8,${JSON.stringify(rules, null, 2)}`;
546
- dom.attr(a, 'download', filename || '');
547
- dom.attr(a, 'type', 'application/json');
548
- a.click();
549
- }
550
-
551
- /******************************************************************************/
552
-
553
- function importRulesFromPaste(transaction) {
554
- const { from, to } = rangeFromTransaction(transaction);
555
- if ( from === undefined || to === undefined ) { return; }
556
- // Paste position must match start of a line
557
- const { newDoc } = transaction;
558
- const lineFrom = newDoc.lineAt(from);
559
- if ( lineFrom.from !== from ) { return; }
560
- // Paste position must match a rule boundary
561
- if ( lineFrom.number !== 1 ) {
562
- const lineBefore = newDoc.line(lineFrom.number-1);
563
- if ( /^---\s*$/.test(lineBefore.text) === false ) { return; }
564
- }
565
- const pastedText = newDoc.sliceString(from, to);
566
- const rules = rulesFromJSON(pastedText);
567
- if ( rules === undefined ) { return; }
568
- const yamlText = textFromRules(rules);
569
- if ( yamlText === undefined ) { return; }
570
- cmRules.dispatch({ changes: { from, to, insert: yamlText } });
571
- self.cm6.foldAll(cmRules);
572
- return true;
573
- }
574
-
575
- /******************************************************************************/
576
-
577
- function foldService(state, from) {
578
- const { doc } = state;
579
- const lineFrom = doc.lineAt(from);
580
- if ( reFoldable.test(lineFrom.text) === false ) { return null; }
581
- if ( lineFrom.number <= 5 ) { return null ; }
582
- const lineBlockStart = doc.line(lineFrom.number - 5);
583
- if ( reFoldCandidates.test(lineBlockStart.text) === false ) { return null; }
584
- for ( let i = lineFrom.number-4; i < lineFrom.number; i++ ) {
585
- const line = doc.line(i);
586
- if ( reFoldable.test(line.text) === false ) { return null; }
587
- }
588
- let i = lineFrom.number + 1;
589
- for ( ; i <= doc.lines; i++ ) {
590
- const lineNext = doc.line(i);
591
- if ( reFoldable.test(lineNext.text) === false ) { break; }
592
- }
593
- i -= 1;
594
- if ( i === lineFrom.number ) { return null; }
595
- const lineFoldEnd = doc.line(i);
596
- return { from: lineFrom.from+6, to: lineFoldEnd.to };
597
- }
598
-
599
- const reFoldable = /^ {4}- \S/;
600
- const reFoldCandidates = new RegExp(`^(?: {2})+${[
601
- 'initiatorDomains',
602
- 'excludedInitiatorDomains',
603
- 'requestDomains',
604
- 'excludedRequestDomains',
605
- ].join('|')}:$`);
606
-
607
- /******************************************************************************/
608
-
609
- function smartBackspace(transaction) {
610
- const { from, to } = rangeFromTransaction(transaction);
611
- if ( from === undefined || to === undefined ) { return; }
612
- if ( to !== from ) { return; }
613
- const { newDoc } = transaction;
614
- const line = newDoc.lineAt(from);
615
- if ( /^(?: {2})+-$/.test(line.text) === false ) { return; }
616
- cmRules.dispatch({ changes: { from: from-3, to: from, insert: '' } });
617
- return true;
618
- }
619
-
620
- /******************************************************************************/
335
+ }
336
+ while ( yamlDocEnd < lastLine ) {
337
+ const line = doc.line(yamlDocEnd);
338
+ if ( this.reYamlDocSeparator.test(line.text) ) { break; }
339
+ yamlDocEnd += 1;
340
+ }
341
+ return { yamlDocStart, yamlDocEnd };
342
+ }
621
343
 
622
- function lineIsArrayItem(doc, lineNo) {
623
- if ( lineNo < 1 || lineNo > doc.lines ) { return false; }
624
- const line = doc.line(lineNo);
625
- return line.text.startsWith(' - ');
626
- }
344
+ rangeFromTransaction(transaction) {
345
+ let from, to;
346
+ transaction.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
347
+ if ( from === undefined || fromB < from ) { from = fromB; }
348
+ if ( to === undefined || toB > to ) { to = toB; }
349
+ });
350
+ return { from, to };
351
+ }
627
352
 
628
- /******************************************************************************/
353
+ addToModifiedRange(transaction) {
354
+ const { from, to } = this.rangeFromTransaction(transaction);
355
+ if ( from === undefined || to === undefined ) { return; }
356
+ const { newDoc } = transaction;
357
+ const { yamlDocStart, yamlDocEnd } = this.snapToYamlDocument(newDoc, from, to);
358
+ if ( this.modifiedRange.start === 0 || yamlDocStart < this.modifiedRange.start ) {
359
+ this.modifiedRange.start = yamlDocStart;
360
+ }
361
+ if ( this.modifiedRange.end === 0 || yamlDocEnd > this.modifiedRange.end ) {
362
+ this.modifiedRange.end = yamlDocEnd;
363
+ }
364
+ }
629
365
 
630
- function smartArrayItem(doc, from) {
631
- const line = doc.lineAt(from);
632
- if ( lineIsArrayItem(doc, line.number-1) === false ) {
633
- if ( lineIsArrayItem(doc, line.number+1) === false ) { return ''; }
366
+ lineIndentAt(line) {
367
+ const match = /^(?: {2})*/.exec(line.text);
368
+ const indent = match !== null ? match[0].length : -1;
369
+ if ( indent === -1 || (indent & 1) !== 0 ) { return -1; }
370
+ return indent / 2;
634
371
  }
635
- const blanks = /^ {2,4}$/.exec(line.text);
636
- if ( blanks === null ) { return ''; }
637
- const count = blanks[0].length;
638
- return `${' '.repeat(4-count)}- `;
639
- }
640
372
 
641
- /******************************************************************************/
373
+ getScopeAt(from, doc) {
374
+ doc ||= this.view.state.doc;
375
+ const lineFrom = doc.lineAt(from);
376
+ const out = {};
377
+ let depth = this.lineIndentAt(lineFrom);
378
+ if ( depth === -1 ) { return out; }
379
+ const text = lineFrom.text.trim();
380
+ if ( text.startsWith('#') ) { return out; }
381
+ const path = [];
382
+ const end = text.indexOf(':');
383
+ if ( end !== -1 ) {
384
+ const beg = text.startsWith('- ') ? 2 : 0;
385
+ path.push(text.slice(beg, end+1));
386
+ }
387
+ let lineNo = lineFrom.number;
388
+ while ( depth > 0 && lineNo > 1 ) {
389
+ lineNo -= 1;
390
+ const lineBefore = doc.line(lineNo);
391
+ const text = lineBefore.text.trim();
392
+ if ( text.startsWith('#') ) { continue; }
393
+ if ( this.lineIndentAt(lineBefore) > (depth-1) ) { continue; }
394
+ const match = /^- ([^:]+:)/.exec(text);
395
+ if ( match !== null ) {
396
+ path.unshift(match[1]);
397
+ } else {
398
+ path.unshift(text);
399
+ }
400
+ depth -= 1;
401
+ }
402
+ out.scope = path.join('');
403
+ out.depth = path.length;
404
+ return out;
405
+ }
642
406
 
643
- function smartReturn(transaction) {
644
- const { from, to } = rangeFromTransaction(transaction);
645
- if ( from === undefined || to === undefined ) { return; }
646
- const { newDoc } = transaction;
647
- const insert = smartArrayItem(newDoc, to);
648
- if ( insert === '' ) { return; }
649
- cmRules.dispatch({
650
- changes: { from: to, insert },
651
- selection: { anchor: to + insert.length },
652
- });
653
- return true;
654
- }
407
+ async saveEditorText() {
408
+ if ( typeof this.editor.saveEditorText !== 'function' ) { return; }
409
+ if ( this.editorTextChanged() === false ) { return; }
410
+ const saved = await this.editor.saveEditorText(this);
411
+ if ( saved !== true ) { return; }
412
+ this.lastSavedText = this.normalizeEditorText(this.getEditorText());
413
+ this.updateView();
414
+ }
655
415
 
656
- /******************************************************************************/
416
+ revertEditorText() {
417
+ if ( this.editorTextChanged() === false ) { return; }
418
+ this.setEditorText(this.lastSavedText);
419
+ }
657
420
 
658
- function smartSpacebar(transaction) {
659
- const { from, to } = rangeFromTransaction(transaction);
660
- if ( from === undefined || to === undefined ) { return; }
661
- if ( (to - from) !== 1 ) { return; }
662
- const { newDoc } = transaction;
663
- const line = newDoc.lineAt(to);
664
- const localTo = to - line.from;
665
- const before = line.text.slice(0, localTo);
666
- if ( /^(?: {1}| {3})$/.test(before) === false ) { return; }
667
- const insert = smartArrayItem(newDoc, to) || ' ';
668
- cmRules.dispatch({
669
- changes: { from: to, insert },
670
- selection: { anchor: to + insert.length },
671
- });
672
- return true;
673
- }
421
+ smartBackspace(transaction) {
422
+ const { from, to } = this.rangeFromTransaction(transaction);
423
+ if ( from === undefined || to === undefined ) { return; }
424
+ if ( to !== from ) { return; }
425
+ const { newDoc } = transaction;
426
+ const line = newDoc.lineAt(from);
427
+ if ( /^(?: {2})+-$/.test(line.text) === false ) { return; }
428
+ this.view.dispatch({ changes: { from: from-3, to: from, insert: '' } });
429
+ return true;
430
+ }
674
431
 
675
- /******************************************************************************/
432
+ lineIsArrayItem(doc, lineNo) {
433
+ if ( lineNo < 1 || lineNo > doc.lines ) { return false; }
434
+ const line = doc.line(lineNo);
435
+ return /^(?: {2})+- /.test(line.text);
436
+ }
676
437
 
677
- const dnryamlStreamParser = {
678
- name: 'dnryaml',
679
- startState() {
680
- return {
681
- scope: 0,
682
- reKeywords: new RegExp(`\\b(${[
683
- 'block',
684
- 'redirect',
685
- 'allow',
686
- 'modifyHeaders',
687
- 'upgradeScheme',
688
- 'allowAllRequest',
689
- 'append',
690
- 'set',
691
- 'remove',
692
- 'firstParty',
693
- 'thirdParty',
694
- 'true',
695
- 'false',
696
- 'connect',
697
- 'delete',
698
- 'get',
699
- 'head',
700
- 'options',
701
- 'patch',
702
- 'post',
703
- 'put',
704
- 'other',
705
- 'main_frame',
706
- 'sub_frame',
707
- 'stylesheet',
708
- 'script',
709
- 'image',
710
- 'font',
711
- 'object',
712
- 'xmlhttprequest',
713
- 'ping',
714
- 'csp_report',
715
- 'media',
716
- 'websocket',
717
- 'webtransport',
718
- 'webbundle',
719
- 'other',
720
- ].join('|')})\\b`),
721
- };
722
- },
723
- token(stream, state) {
724
- const c = stream.peek();
725
- if ( c === '#' ) {
726
- if ( (stream.pos === 0 || /\s/.test(stream.string.charAt(stream.pos - 1))) ) {
727
- stream.skipToEnd();
728
- return 'comment';
438
+ smartArrayItem(doc, from) {
439
+ const line = doc.lineAt(from);
440
+ if ( line.from === 0 ) { return; }
441
+ const blanks = /^ *$/.exec(line.text);
442
+ if ( blanks === null ) { return; }
443
+ if ( this.editor.newlineAssistant ) {
444
+ const { scope } = this.getScopeAt(line.from-1, doc);
445
+ const insert = this.editor.newlineAssistant[scope];
446
+ if ( insert ) {
447
+ this.view.dispatch({
448
+ changes: { from: line.from, to: line.to, insert },
449
+ selection: { anchor: line.from + insert.length },
450
+ });
451
+ return true;
729
452
  }
730
453
  }
731
- if ( stream.sol() ) {
732
- if ( stream.match('---') ) { return 'contentSeparator'; }
733
- if ( stream.match('...') ) { return 'contentSeparator'; }
734
- }
735
- if ( stream.eatSpace() ) {
736
- return null;
737
- }
738
- const { scope } = state;
739
- state.scope = 0;
740
- if ( scope === 0 && stream.match(/^[^:]+(?=:)/) ) {
741
- state.scope = 1;
742
- return 'keyword';
454
+ let targetIndent;
455
+ if ( this.lineIsArrayItem(doc, line.number-1) ) {
456
+ targetIndent = doc.line(line.number-1).text.indexOf('- ');
457
+ } else if ( this.lineIsArrayItem(doc, line.number+1) ) {
458
+ targetIndent = doc.line(line.number+1).text.indexOf('- ');
743
459
  }
744
- if ( scope === 1 && stream.match(/^:(?: |$)/) ) {
745
- return 'meta';
746
- }
747
- if ( stream.match(/^- /) ) {
748
- return 'meta';
749
- }
750
- if ( stream.match(state.reKeywords) ) {
751
- return 'literal';
752
- }
753
- if ( stream.match(/^\S+/) ) {
754
- return null;
755
- }
756
- stream.next();
757
- return null;
758
- },
759
- };
760
-
761
- /******************************************************************************/
460
+ if ( targetIndent === undefined ) { return; }
461
+ const indent = targetIndent - blanks[0].length;
462
+ if ( indent < 0 || indent > 2 ) { return; }
463
+ const insert = `${' '.repeat(indent)}- `;
464
+ this.view.dispatch({
465
+ changes: { from, insert },
466
+ selection: { anchor: from + insert.length },
467
+ });
468
+ return true;
469
+ }
762
470
 
763
- function cmUpdateListener(info) {
764
- if ( info.docChanged === false ) { return; }
765
- for ( const transaction of info.transactions ) {
766
- if ( transaction.docChanged === false ) { continue; }
767
- addToModifiedRange(transaction);
768
- if ( transaction.isUserEvent('delete.backward') ) {
769
- smartBackspace(transaction);
770
- } else if ( transaction.isUserEvent('input.paste') ) {
771
- importRulesFromPaste(transaction);
772
- } else if ( transaction.isUserEvent('input') ) {
773
- if ( smartReturn(transaction) ) { continue; }
774
- smartSpacebar(transaction);
775
- }
471
+ smartReturn(transaction) {
472
+ const { from, to } = this.rangeFromTransaction(transaction);
473
+ if ( from === undefined || to === undefined ) { return; }
474
+ const { newDoc } = transaction;
475
+ return this.smartArrayItem(newDoc, to);
776
476
  }
777
- updateViewAsync();
778
- }
779
477
 
780
- /******************************************************************************/
478
+ smartSpacebar(transaction) {
479
+ const { from, to } = this.rangeFromTransaction(transaction);
480
+ if ( from === undefined || to === undefined ) { return; }
481
+ if ( (to - from) !== 1 ) { return; }
482
+ const { newDoc } = transaction;
483
+ const line = newDoc.lineAt(to);
484
+ const localTo = to - line.from;
485
+ const before = line.text.slice(0, localTo);
486
+ if ( /^(?: {1}| {3})$/.test(before) === false ) { return; }
487
+ if ( this.smartArrayItem(newDoc, to) ) { return true; }
488
+ this.view.dispatch({
489
+ changes: { from: to, insert: ' ' },
490
+ selection: { anchor: to + 1 },
491
+ });
492
+ return true;
493
+ }
781
494
 
782
- function gutterClick(view, info) {
783
- const reSeparator = /^---\s*/;
784
- const { doc } = view.state;
785
- const lineFirst = doc.lineAt(info.from);
786
- if ( lineFirst.text === '' ) { return false; }
787
- let { from, to } = lineFirst;
788
- if ( reSeparator.test(lineFirst.text) ) {
789
- let lineNo = lineFirst.number + 1;
790
- while ( lineNo < doc.lines ) {
791
- const line = doc.line(lineNo);
792
- if ( reSeparator.test(line.text) ) { break; }
793
- to = line.to;
794
- lineNo += 1;
495
+ gutterClick(view, info) {
496
+ const reSeparator = /^(?:---|# ---)\s*/;
497
+ const { doc } = view.state;
498
+ const lineFirst = doc.lineAt(info.from);
499
+ if ( lineFirst.text === '' ) { return false; }
500
+ let { from, to } = lineFirst;
501
+ if ( reSeparator.test(lineFirst.text) ) {
502
+ let lineNo = lineFirst.number + 1;
503
+ while ( lineNo < doc.lines ) {
504
+ const line = doc.line(lineNo);
505
+ if ( reSeparator.test(line.text) ) { break; }
506
+ to = line.to;
507
+ lineNo += 1;
508
+ }
795
509
  }
510
+ view.dispatch({
511
+ selection: { anchor: from, head: to+1 }
512
+ });
513
+ view.focus();
514
+ return true;
796
515
  }
797
- view.dispatch({
798
- selection: { anchor: from, head: to+1 }
799
- });
800
- view.focus();
801
- return true;
802
- }
803
516
 
804
- /******************************************************************************/
517
+ importFromFile() {
518
+ const editor = this.editor;
519
+ if ( typeof editor.importFromFile !== 'function' ) { return; }
520
+ const input = qs$('input[type="file"]');
521
+ input.accept = editor.ioAccept || '';
522
+ input.onchange = ev => {
523
+ input.onchange = null;
524
+ const file = ev.target.files[0];
525
+ if ( file === undefined || file.name === '' ) { return; }
526
+ const fr = new FileReader();
527
+ fr.onload = ( ) => {
528
+ if ( typeof fr.result !== 'string' ) { return; }
529
+ editor.importFromFile(this, fr.result);
530
+ };
531
+ fr.readAsText(file);
532
+ };
533
+ // Reset to empty string, this will ensure a change event is properly
534
+ // triggered if the user pick a file, even if it's the same as the last
535
+ // one picked.
536
+ input.value = '';
537
+ input.click();
538
+ }
805
539
 
806
- function hoverTooltip(view, pos, side) {
807
- const details = view.domAtPos(pos);
808
- const textNode = details.node;
809
- if ( textNode.nodeType !== 3 ) { return null; }
810
- const { parentElement } = textNode;
811
- const targetElement = parentElement.closest('[data-tooltip]');
812
- if ( targetElement === null ) { return null; }
813
- const tooltipText = targetElement.getAttribute('data-tooltip');
814
- if ( Boolean(tooltipText) === false ) { return null; }
815
- const start = pos - details.offset;
816
- const end = start + textNode.nodeValue.length;
817
- if ( start === pos && side < 0 || end === pos && side > 0 ) { return null; }
818
- return {
819
- above: true,
820
- pos: start,
821
- end,
822
- create() {
823
- const template = document.querySelector('.badmark-tooltip');
824
- const fragment = template.content.cloneNode(true);
825
- const dom = fragment.querySelector('.badmark-tooltip');
826
- dom.textContent = tooltipText;
827
- return { dom };
540
+ exportToFile() {
541
+ const editor = this.editor;
542
+ if ( typeof editor.exportToFile !== 'function' ) { return; }
543
+ const text = this.getEditorText();
544
+ const result = editor.exportToFile(text);
545
+ if ( result === undefined ) { return; }
546
+ const { fname, data, mime } = result;
547
+ const a = document.createElement('a');
548
+ a.href = `data:${mime};charset=utf-8,${encodeURIComponent(data)}`;
549
+ dom.attr(a, 'download', fname || '');
550
+ dom.attr(a, 'type', mime);
551
+ a.click();
552
+ }
553
+
554
+ streamParser = {
555
+ startState: ( ) => {
556
+ return { scope: 0 };
557
+ },
558
+ token: (stream, state) => {
559
+ if ( stream.sol() ) {
560
+ if ( stream.match(/^---\s*$/) ) { return 'yamlboundary'; }
561
+ if ( stream.match(/^# ---\s*$/) ) { return 'yamlboundary comment'; }
562
+ if ( stream.match(/\.\.\.\s*$/) ) { return 'yamlboundary'; }
563
+ }
564
+ const c = stream.peek();
565
+ if ( c === '#' ) {
566
+ if ( (stream.pos === 0 || /\s/.test(stream.string.charAt(stream.pos - 1))) ) {
567
+ stream.skipToEnd();
568
+ return 'comment';
569
+ }
570
+ }
571
+ if ( stream.eatSpace() ) {
572
+ return null;
573
+ }
574
+ const { scope } = state;
575
+ state.scope = 0;
576
+ if ( scope === 0 && stream.match(/^[^:]+(?=:)/) ) {
577
+ state.scope = 1;
578
+ return 'keyword';
579
+ }
580
+ if ( scope === 1 && stream.match(/^:(?: |$)/) ) {
581
+ return 'punctuation';
582
+ }
583
+ if ( stream.match(/^- /) ) {
584
+ return 'punctuation';
585
+ }
586
+ if ( this.editor.streamParserKeywords ) {
587
+ if ( stream.match(this.editor.streamParserKeywords) ) {
588
+ return 'literal';
589
+ }
590
+ }
591
+ if ( stream.match(/^\S+/) ) {
592
+ return null;
593
+ }
594
+ stream.next();
595
+ return null;
596
+ },
597
+ languageData: {
598
+ commentTokens: { line: '#' },
828
599
  },
600
+ tokenTable: [
601
+ 'yamlboundary',
602
+ ],
829
603
  };
830
604
  }
831
605
 
832
606
  /******************************************************************************/
833
607
 
834
- let lastSavedText = '';
835
-
836
- const cmRules = await localRead('userDnrRules').then(text => {
837
- text ||= '';
838
-
839
- const view = self.cm6.createEditorView({
840
- text,
841
- dnrRules: true,
842
- oneDark: dom.cl.has(':root', 'dark'),
843
- updateListener: cmUpdateListener,
844
- saveListener: ( ) => {
845
- saveEditorText();
846
- },
847
- lineError: true,
848
- spanError: true,
849
- // https://codemirror.net/examples/autocompletion/
850
- autocompletion: {
851
- override: [ autoComplete ],
852
- activateOnCompletion: ( ) => true,
853
- },
854
- gutterClick,
855
- hoverTooltip,
856
- streamParser: dnryamlStreamParser,
857
- foldService,
858
- }, qs$('#cm-dnrRules'));
859
-
860
- lastSavedText = text;
861
- self.cm6.foldAll(view);
862
- self.cm6.resetUndoRedo(view);
863
-
864
- browser.storage.onChanged.addListener((changes, area) => {
865
- if ( area !== 'local' ) { return; }
866
- const { userDnrRuleCount } = changes;
867
- if ( userDnrRuleCount instanceof Object === false ) { return; }
868
- const { newValue } = changes.userDnrRuleCount;
869
- updateSummaryPanel({ userDnrRuleCount: newValue });
870
- });
871
-
872
- localRead('userDnrRuleCount').then(userDnrRuleCount => {
873
- updateSummaryPanel({ userDnrRuleCount })
608
+ async function start() {
609
+ const editor = new Editor();
610
+ await editor.init();
611
+ dom.on('#editors', 'change', ( ) => {
612
+ const select = qs$('#editors');
613
+ const mode = select.value;
614
+ if ( mode === editor.mode ) { return; }
615
+ editor.selectEditor(mode);
616
+ localWrite('dashboard.develop.editor', editor.mode);
874
617
  });
618
+ }
875
619
 
876
- dom.on('#dnrRulesApply', 'click', ( ) => {
877
- saveEditorText();
878
- });
879
- dom.on('#dnrRulesRevert', 'click', ( ) => {
880
- setEditorText(lastSavedText);
881
- sendMessage({ what: 'updateUserDnrRules' });
882
- });
883
- dom.on('#dnrRulesImport', 'click', importRulesFromFile);
884
- dom.on('#dnrRulesExport', 'click', exportRulesToFile);
885
-
886
- return view;
620
+ let observer = new IntersectionObserver(entries => {
621
+ for ( const entry of entries ) {
622
+ if ( entry.isIntersecting === false ) { continue; }
623
+ start();
624
+ observer.disconnect();
625
+ observer = null;
626
+ break;
627
+ }
887
628
  });
629
+ observer.observe(qs$('section[data-pane="develop"]'));
888
630
 
889
631
  /******************************************************************************/