@jackuait/blok 0.4.1-beta.1 → 0.4.1-beta.12

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 (403) hide show
  1. package/README.md +138 -17
  2. package/codemod/README.md +45 -7
  3. package/codemod/migrate-editorjs-to-blok.js +960 -92
  4. package/codemod/test.js +780 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-BU6NwVkN.mjs +13239 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-D8GzSwio.mjs +43 -0
  9. package/dist/{index-CEXLTV6f.mjs → chunks/index-C5e_WLFg.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-CLUxkCe_.mjs +1990 -0
  11. package/dist/chunks/messages-0tDXLuyH.mjs +48 -0
  12. package/dist/chunks/messages-2_xedlYw.mjs +48 -0
  13. package/dist/chunks/messages-AHESHJm_.mjs +48 -0
  14. package/dist/chunks/messages-B5hdXZwA.mjs +48 -0
  15. package/dist/chunks/messages-B5jGUnOy.mjs +48 -0
  16. package/dist/chunks/messages-B5puUm7R.mjs +48 -0
  17. package/dist/chunks/messages-B66ZSDCJ.mjs +48 -0
  18. package/dist/chunks/messages-B9Oba7sq.mjs +48 -0
  19. package/dist/chunks/messages-BA0rcTCY.mjs +48 -0
  20. package/dist/chunks/messages-BBJgd5jG.mjs +48 -0
  21. package/dist/chunks/messages-BPqWKx5Z.mjs +48 -0
  22. package/dist/chunks/messages-Bdv-IkfG.mjs +48 -0
  23. package/dist/chunks/messages-BeUhMpsr.mjs +48 -0
  24. package/dist/chunks/messages-Bf6Y3_GI.mjs +48 -0
  25. package/dist/chunks/messages-BiExzWJv.mjs +48 -0
  26. package/dist/chunks/messages-BlpqL8vG.mjs +48 -0
  27. package/dist/chunks/messages-BmKCChWZ.mjs +48 -0
  28. package/dist/chunks/messages-Bn253WWC.mjs +48 -0
  29. package/dist/chunks/messages-BrJHUxQL.mjs +48 -0
  30. package/dist/chunks/messages-C5b7hr_E.mjs +48 -0
  31. package/dist/chunks/messages-C7I_AVH2.mjs +48 -0
  32. package/dist/chunks/messages-CJoBtXU6.mjs +48 -0
  33. package/dist/chunks/messages-CQj2JU2j.mjs +48 -0
  34. package/dist/chunks/messages-CUZ1x1QD.mjs +48 -0
  35. package/dist/chunks/messages-CUy1vn-b.mjs +48 -0
  36. package/dist/chunks/messages-CVeWVKsV.mjs +48 -0
  37. package/dist/chunks/messages-CXHd9SUK.mjs +48 -0
  38. package/dist/chunks/messages-CbMyJSzS.mjs +48 -0
  39. package/dist/chunks/messages-CbhuIWRJ.mjs +48 -0
  40. package/dist/chunks/messages-CeCjVKMW.mjs +48 -0
  41. package/dist/chunks/messages-Cj-t1bdy.mjs +48 -0
  42. package/dist/chunks/messages-CkFT2gle.mjs +48 -0
  43. package/dist/chunks/messages-Cm9aLHeX.mjs +48 -0
  44. package/dist/chunks/messages-CnvW8Slp.mjs +48 -0
  45. package/dist/chunks/messages-Cr-RJ7YB.mjs +48 -0
  46. package/dist/chunks/messages-CrsJ1TEJ.mjs +48 -0
  47. package/dist/chunks/messages-Cu08aLS3.mjs +48 -0
  48. package/dist/chunks/messages-CvaqJFN-.mjs +48 -0
  49. package/dist/chunks/messages-CyDU5lz9.mjs +48 -0
  50. package/dist/chunks/messages-CySyfkMU.mjs +48 -0
  51. package/dist/chunks/messages-Cyi2AMmz.mjs +48 -0
  52. package/dist/chunks/messages-D00OjS2n.mjs +48 -0
  53. package/dist/chunks/messages-DDLgIPDF.mjs +48 -0
  54. package/dist/chunks/messages-DMQIHGRj.mjs +48 -0
  55. package/dist/chunks/messages-DOlC_Tty.mjs +48 -0
  56. package/dist/chunks/messages-DV6shA9b.mjs +48 -0
  57. package/dist/chunks/messages-DY94ykcE.mjs +48 -0
  58. package/dist/chunks/messages-DbVquYKN.mjs +48 -0
  59. package/dist/chunks/messages-DcKOuncK.mjs +48 -0
  60. package/dist/chunks/messages-Dg92dXZ5.mjs +48 -0
  61. package/dist/chunks/messages-DnbbyJT3.mjs +48 -0
  62. package/dist/chunks/messages-DteYq0rv.mjs +48 -0
  63. package/dist/chunks/messages-GC2PhgV3.mjs +48 -0
  64. package/dist/chunks/messages-JGsXAReJ.mjs +48 -0
  65. package/dist/chunks/messages-JZUhXTuV.mjs +48 -0
  66. package/dist/chunks/messages-LvFKBBPa.mjs +48 -0
  67. package/dist/chunks/messages-NP1myMGI.mjs +48 -0
  68. package/dist/chunks/messages-Q4kc_ZtL.mjs +48 -0
  69. package/dist/chunks/messages-RvMHb2Ht.mjs +48 -0
  70. package/dist/chunks/messages-ftMcCEuO.mjs +48 -0
  71. package/dist/chunks/messages-o24dK6CU.mjs +48 -0
  72. package/dist/chunks/messages-pA5TvcAj.mjs +48 -0
  73. package/dist/chunks/messages-rRSHQDCX.mjs +48 -0
  74. package/dist/chunks/messages-srxrv8Yh.mjs +48 -0
  75. package/dist/chunks/messages-wdqp4610.mjs +48 -0
  76. package/dist/chunks/messages-zS1AXZ0y.mjs +48 -0
  77. package/dist/chunks/messages-zSzDzXej.mjs +48 -0
  78. package/dist/full.mjs +50 -0
  79. package/dist/locales.mjs +228 -0
  80. package/dist/messages-0tDXLuyH.mjs +48 -0
  81. package/dist/messages-2_xedlYw.mjs +48 -0
  82. package/dist/messages-AHESHJm_.mjs +48 -0
  83. package/dist/messages-B5hdXZwA.mjs +48 -0
  84. package/dist/messages-B5jGUnOy.mjs +48 -0
  85. package/dist/messages-B5puUm7R.mjs +48 -0
  86. package/dist/messages-B66ZSDCJ.mjs +48 -0
  87. package/dist/messages-B9Oba7sq.mjs +48 -0
  88. package/dist/messages-BA0rcTCY.mjs +48 -0
  89. package/dist/messages-BBJgd5jG.mjs +48 -0
  90. package/dist/messages-BPqWKx5Z.mjs +48 -0
  91. package/dist/messages-Bdv-IkfG.mjs +48 -0
  92. package/dist/messages-BeUhMpsr.mjs +48 -0
  93. package/dist/messages-Bf6Y3_GI.mjs +48 -0
  94. package/dist/messages-BiExzWJv.mjs +48 -0
  95. package/dist/messages-BlpqL8vG.mjs +48 -0
  96. package/dist/messages-BmKCChWZ.mjs +48 -0
  97. package/dist/messages-Bn253WWC.mjs +48 -0
  98. package/dist/messages-BrJHUxQL.mjs +48 -0
  99. package/dist/messages-C5b7hr_E.mjs +48 -0
  100. package/dist/messages-C7I_AVH2.mjs +48 -0
  101. package/dist/messages-CJoBtXU6.mjs +48 -0
  102. package/dist/messages-CQj2JU2j.mjs +48 -0
  103. package/dist/messages-CUZ1x1QD.mjs +48 -0
  104. package/dist/messages-CUy1vn-b.mjs +48 -0
  105. package/dist/messages-CVeWVKsV.mjs +48 -0
  106. package/dist/messages-CXHd9SUK.mjs +48 -0
  107. package/dist/messages-CbMyJSzS.mjs +48 -0
  108. package/dist/messages-CbhuIWRJ.mjs +48 -0
  109. package/dist/messages-CeCjVKMW.mjs +48 -0
  110. package/dist/messages-Cj-t1bdy.mjs +48 -0
  111. package/dist/messages-CkFT2gle.mjs +48 -0
  112. package/dist/messages-Cm9aLHeX.mjs +48 -0
  113. package/dist/messages-CnvW8Slp.mjs +48 -0
  114. package/dist/messages-Cr-RJ7YB.mjs +48 -0
  115. package/dist/messages-CrsJ1TEJ.mjs +48 -0
  116. package/dist/messages-Cu08aLS3.mjs +48 -0
  117. package/dist/messages-CvaqJFN-.mjs +48 -0
  118. package/dist/messages-CyDU5lz9.mjs +48 -0
  119. package/dist/messages-CySyfkMU.mjs +48 -0
  120. package/dist/messages-Cyi2AMmz.mjs +48 -0
  121. package/dist/messages-D00OjS2n.mjs +48 -0
  122. package/dist/messages-DDLgIPDF.mjs +48 -0
  123. package/dist/messages-DMQIHGRj.mjs +48 -0
  124. package/dist/messages-DOlC_Tty.mjs +48 -0
  125. package/dist/messages-DV6shA9b.mjs +48 -0
  126. package/dist/messages-DY94ykcE.mjs +48 -0
  127. package/dist/messages-DbVquYKN.mjs +48 -0
  128. package/dist/messages-DcKOuncK.mjs +48 -0
  129. package/dist/messages-Dg92dXZ5.mjs +48 -0
  130. package/dist/messages-DnbbyJT3.mjs +48 -0
  131. package/dist/messages-DteYq0rv.mjs +48 -0
  132. package/dist/messages-GC2PhgV3.mjs +48 -0
  133. package/dist/messages-JGsXAReJ.mjs +48 -0
  134. package/dist/messages-JZUhXTuV.mjs +48 -0
  135. package/dist/messages-LvFKBBPa.mjs +48 -0
  136. package/dist/messages-NP1myMGI.mjs +48 -0
  137. package/dist/messages-Q4kc_ZtL.mjs +48 -0
  138. package/dist/messages-RvMHb2Ht.mjs +48 -0
  139. package/dist/messages-ftMcCEuO.mjs +48 -0
  140. package/dist/messages-o24dK6CU.mjs +48 -0
  141. package/dist/messages-pA5TvcAj.mjs +48 -0
  142. package/dist/messages-rRSHQDCX.mjs +48 -0
  143. package/dist/messages-srxrv8Yh.mjs +48 -0
  144. package/dist/messages-wdqp4610.mjs +48 -0
  145. package/dist/messages-zS1AXZ0y.mjs +48 -0
  146. package/dist/messages-zSzDzXej.mjs +48 -0
  147. package/dist/tools.mjs +3126 -0
  148. package/dist/vendor.LICENSE.txt +26 -225
  149. package/package.json +63 -24
  150. package/src/blok.ts +267 -0
  151. package/src/components/__module.ts +139 -0
  152. package/src/components/block/api.ts +155 -0
  153. package/src/components/block/index.ts +1428 -0
  154. package/src/components/block-tunes/block-tune-delete.ts +51 -0
  155. package/src/components/blocks.ts +352 -0
  156. package/src/components/constants/data-attributes.ts +344 -0
  157. package/src/components/constants.ts +76 -0
  158. package/src/components/core.ts +392 -0
  159. package/src/components/dom.ts +773 -0
  160. package/src/components/domIterator.ts +189 -0
  161. package/src/components/errors/critical.ts +5 -0
  162. package/src/components/events/BlockChanged.ts +16 -0
  163. package/src/components/events/BlockHovered.ts +21 -0
  164. package/src/components/events/BlockSettingsClosed.ts +12 -0
  165. package/src/components/events/BlockSettingsOpened.ts +12 -0
  166. package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
  167. package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
  168. package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
  169. package/src/components/events/HistoryStateChanged.ts +19 -0
  170. package/src/components/events/RedactorDomChanged.ts +14 -0
  171. package/src/components/events/index.ts +46 -0
  172. package/src/components/flipper.ts +497 -0
  173. package/src/components/i18n/i18next-loader.ts +84 -0
  174. package/src/components/i18n/lightweight-i18n.ts +86 -0
  175. package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
  176. package/src/components/i18n/locales/am/messages.json +45 -0
  177. package/src/components/i18n/locales/ar/messages.json +45 -0
  178. package/src/components/i18n/locales/az/messages.json +45 -0
  179. package/src/components/i18n/locales/bg/messages.json +45 -0
  180. package/src/components/i18n/locales/bn/messages.json +45 -0
  181. package/src/components/i18n/locales/bs/messages.json +45 -0
  182. package/src/components/i18n/locales/cs/messages.json +45 -0
  183. package/src/components/i18n/locales/da/messages.json +45 -0
  184. package/src/components/i18n/locales/de/messages.json +45 -0
  185. package/src/components/i18n/locales/dv/messages.json +45 -0
  186. package/src/components/i18n/locales/el/messages.json +45 -0
  187. package/src/components/i18n/locales/en/messages.json +45 -0
  188. package/src/components/i18n/locales/es/messages.json +45 -0
  189. package/src/components/i18n/locales/et/messages.json +45 -0
  190. package/src/components/i18n/locales/fa/messages.json +45 -0
  191. package/src/components/i18n/locales/fi/messages.json +45 -0
  192. package/src/components/i18n/locales/fil/messages.json +45 -0
  193. package/src/components/i18n/locales/fr/messages.json +45 -0
  194. package/src/components/i18n/locales/gu/messages.json +45 -0
  195. package/src/components/i18n/locales/he/messages.json +45 -0
  196. package/src/components/i18n/locales/hi/messages.json +45 -0
  197. package/src/components/i18n/locales/hr/messages.json +45 -0
  198. package/src/components/i18n/locales/hu/messages.json +45 -0
  199. package/src/components/i18n/locales/hy/messages.json +45 -0
  200. package/src/components/i18n/locales/id/messages.json +45 -0
  201. package/src/components/i18n/locales/index.ts +231 -0
  202. package/src/components/i18n/locales/it/messages.json +45 -0
  203. package/src/components/i18n/locales/ja/messages.json +45 -0
  204. package/src/components/i18n/locales/ka/messages.json +45 -0
  205. package/src/components/i18n/locales/km/messages.json +45 -0
  206. package/src/components/i18n/locales/kn/messages.json +45 -0
  207. package/src/components/i18n/locales/ko/messages.json +45 -0
  208. package/src/components/i18n/locales/ku/messages.json +45 -0
  209. package/src/components/i18n/locales/lo/messages.json +45 -0
  210. package/src/components/i18n/locales/lt/messages.json +45 -0
  211. package/src/components/i18n/locales/lv/messages.json +45 -0
  212. package/src/components/i18n/locales/mk/messages.json +45 -0
  213. package/src/components/i18n/locales/ml/messages.json +45 -0
  214. package/src/components/i18n/locales/mn/messages.json +45 -0
  215. package/src/components/i18n/locales/mr/messages.json +45 -0
  216. package/src/components/i18n/locales/ms/messages.json +45 -0
  217. package/src/components/i18n/locales/my/messages.json +45 -0
  218. package/src/components/i18n/locales/ne/messages.json +45 -0
  219. package/src/components/i18n/locales/nl/messages.json +45 -0
  220. package/src/components/i18n/locales/no/messages.json +45 -0
  221. package/src/components/i18n/locales/pa/messages.json +45 -0
  222. package/src/components/i18n/locales/pl/messages.json +45 -0
  223. package/src/components/i18n/locales/ps/messages.json +45 -0
  224. package/src/components/i18n/locales/pt/messages.json +45 -0
  225. package/src/components/i18n/locales/ro/messages.json +45 -0
  226. package/src/components/i18n/locales/ru/messages.json +45 -0
  227. package/src/components/i18n/locales/sd/messages.json +45 -0
  228. package/src/components/i18n/locales/si/messages.json +45 -0
  229. package/src/components/i18n/locales/sk/messages.json +45 -0
  230. package/src/components/i18n/locales/sl/messages.json +45 -0
  231. package/src/components/i18n/locales/sq/messages.json +45 -0
  232. package/src/components/i18n/locales/sr/messages.json +45 -0
  233. package/src/components/i18n/locales/sv/messages.json +45 -0
  234. package/src/components/i18n/locales/sw/messages.json +45 -0
  235. package/src/components/i18n/locales/ta/messages.json +45 -0
  236. package/src/components/i18n/locales/te/messages.json +45 -0
  237. package/src/components/i18n/locales/th/messages.json +45 -0
  238. package/src/components/i18n/locales/tr/messages.json +45 -0
  239. package/src/components/i18n/locales/ug/messages.json +45 -0
  240. package/src/components/i18n/locales/uk/messages.json +45 -0
  241. package/src/components/i18n/locales/ur/messages.json +45 -0
  242. package/src/components/i18n/locales/vi/messages.json +45 -0
  243. package/src/components/i18n/locales/yi/messages.json +45 -0
  244. package/src/components/i18n/locales/zh/messages.json +45 -0
  245. package/src/components/icons/index.ts +242 -0
  246. package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
  247. package/src/components/inline-tools/inline-tool-convert.ts +142 -0
  248. package/src/components/inline-tools/inline-tool-italic.ts +500 -0
  249. package/src/components/inline-tools/inline-tool-link.ts +540 -0
  250. package/src/components/modules/api/blocks.ts +377 -0
  251. package/src/components/modules/api/caret.ts +125 -0
  252. package/src/components/modules/api/events.ts +51 -0
  253. package/src/components/modules/api/history.ts +73 -0
  254. package/src/components/modules/api/i18n.ts +35 -0
  255. package/src/components/modules/api/index.ts +39 -0
  256. package/src/components/modules/api/inlineToolbar.ts +33 -0
  257. package/src/components/modules/api/listeners.ts +56 -0
  258. package/src/components/modules/api/notifier.ts +46 -0
  259. package/src/components/modules/api/readonly.ts +39 -0
  260. package/src/components/modules/api/sanitizer.ts +30 -0
  261. package/src/components/modules/api/saver.ts +52 -0
  262. package/src/components/modules/api/selection.ts +48 -0
  263. package/src/components/modules/api/styles.ts +72 -0
  264. package/src/components/modules/api/toolbar.ts +79 -0
  265. package/src/components/modules/api/tools.ts +16 -0
  266. package/src/components/modules/api/tooltip.ts +67 -0
  267. package/src/components/modules/api/ui.ts +36 -0
  268. package/src/components/modules/blockEvents.ts +1591 -0
  269. package/src/components/modules/blockManager.ts +1356 -0
  270. package/src/components/modules/blockSelection.ts +708 -0
  271. package/src/components/modules/caret.ts +853 -0
  272. package/src/components/modules/crossBlockSelection.ts +329 -0
  273. package/src/components/modules/dragManager.ts +1204 -0
  274. package/src/components/modules/history.ts +1098 -0
  275. package/src/components/modules/i18n.ts +332 -0
  276. package/src/components/modules/index.ts +139 -0
  277. package/src/components/modules/modificationsObserver.ts +147 -0
  278. package/src/components/modules/paste.ts +1092 -0
  279. package/src/components/modules/readonly.ts +136 -0
  280. package/src/components/modules/rectangleSelection.ts +711 -0
  281. package/src/components/modules/renderer.ts +155 -0
  282. package/src/components/modules/saver.ts +283 -0
  283. package/src/components/modules/toolbar/blockSettings.ts +782 -0
  284. package/src/components/modules/toolbar/index.ts +1296 -0
  285. package/src/components/modules/toolbar/inline.ts +956 -0
  286. package/src/components/modules/tools.ts +625 -0
  287. package/src/components/modules/ui.ts +1283 -0
  288. package/src/components/polyfills.ts +113 -0
  289. package/src/components/selection.ts +1179 -0
  290. package/src/components/tools/base.ts +301 -0
  291. package/src/components/tools/block.ts +339 -0
  292. package/src/components/tools/collection.ts +67 -0
  293. package/src/components/tools/factory.ts +138 -0
  294. package/src/components/tools/inline.ts +71 -0
  295. package/src/components/tools/tune.ts +33 -0
  296. package/src/components/ui/toolbox.ts +610 -0
  297. package/src/components/utils/announcer.ts +205 -0
  298. package/src/components/utils/api.ts +20 -0
  299. package/src/components/utils/bem.ts +26 -0
  300. package/src/components/utils/blocks.ts +284 -0
  301. package/src/components/utils/caret.ts +1067 -0
  302. package/src/components/utils/data-model-transform.ts +382 -0
  303. package/src/components/utils/events.ts +117 -0
  304. package/src/components/utils/keyboard.ts +60 -0
  305. package/src/components/utils/listeners.ts +296 -0
  306. package/src/components/utils/mutations.ts +39 -0
  307. package/src/components/utils/notifier/draw.ts +190 -0
  308. package/src/components/utils/notifier/index.ts +66 -0
  309. package/src/components/utils/notifier/types.ts +1 -0
  310. package/src/components/utils/notifier.ts +77 -0
  311. package/src/components/utils/placeholder.ts +140 -0
  312. package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
  313. package/src/components/utils/popover/components/hint/hint.ts +46 -0
  314. package/src/components/utils/popover/components/hint/index.ts +6 -0
  315. package/src/components/utils/popover/components/popover-header/index.ts +2 -0
  316. package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
  317. package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
  318. package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
  319. package/src/components/utils/popover/components/popover-item/index.ts +13 -0
  320. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
  321. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +680 -0
  322. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
  323. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
  324. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
  325. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
  326. package/src/components/utils/popover/components/popover-item/popover-item.ts +197 -0
  327. package/src/components/utils/popover/components/search-input/index.ts +2 -0
  328. package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
  329. package/src/components/utils/popover/components/search-input/search-input.ts +178 -0
  330. package/src/components/utils/popover/components/search-input/search-input.types.ts +59 -0
  331. package/src/components/utils/popover/index.ts +13 -0
  332. package/src/components/utils/popover/popover-abstract.ts +457 -0
  333. package/src/components/utils/popover/popover-desktop.ts +682 -0
  334. package/src/components/utils/popover/popover-inline.ts +338 -0
  335. package/src/components/utils/popover/popover-mobile.ts +201 -0
  336. package/src/components/utils/popover/popover.const.ts +81 -0
  337. package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
  338. package/src/components/utils/promise-queue.ts +43 -0
  339. package/src/components/utils/sanitizer.ts +537 -0
  340. package/src/components/utils/scroll-locker.ts +87 -0
  341. package/src/components/utils/shortcut.ts +231 -0
  342. package/src/components/utils/shortcuts.ts +113 -0
  343. package/src/components/utils/tools.ts +110 -0
  344. package/src/components/utils/tooltip.ts +591 -0
  345. package/src/components/utils/tw.ts +241 -0
  346. package/src/components/utils.ts +1081 -0
  347. package/src/env.d.ts +13 -0
  348. package/src/full.ts +69 -0
  349. package/src/locales.ts +51 -0
  350. package/src/stories/Block.stories.ts +498 -0
  351. package/src/stories/EditorModes.stories.ts +505 -0
  352. package/src/stories/Header.stories.ts +137 -0
  353. package/src/stories/InlineToolbar.stories.ts +498 -0
  354. package/src/stories/List.stories.ts +259 -0
  355. package/src/stories/Notifier.stories.ts +340 -0
  356. package/src/stories/Paragraph.stories.ts +112 -0
  357. package/src/stories/Placeholder.stories.ts +319 -0
  358. package/src/stories/Popover.stories.ts +759 -0
  359. package/src/stories/Selection.stories.ts +250 -0
  360. package/src/stories/StubBlock.stories.ts +156 -0
  361. package/src/stories/Toolbar.stories.ts +223 -0
  362. package/src/stories/Toolbox.stories.ts +166 -0
  363. package/src/stories/Tooltip.stories.ts +198 -0
  364. package/src/stories/helpers.ts +463 -0
  365. package/src/styles/main.css +126 -0
  366. package/src/tools/header/index.ts +647 -0
  367. package/src/tools/index.ts +45 -0
  368. package/src/tools/list/index.ts +1826 -0
  369. package/src/tools/paragraph/index.ts +412 -0
  370. package/src/tools/stub/index.ts +107 -0
  371. package/src/types-internal/blok-modules.d.ts +87 -0
  372. package/src/types-internal/html-janitor.d.ts +28 -0
  373. package/src/types-internal/module-config.d.ts +11 -0
  374. package/src/variants/all-locales.ts +155 -0
  375. package/src/variants/blok-maximum.ts +20 -0
  376. package/src/variants/blok-minimum.ts +243 -0
  377. package/types/api/blocks.d.ts +9 -1
  378. package/types/api/history.d.ts +7 -0
  379. package/types/api/i18n.d.ts +22 -3
  380. package/types/api/selection.d.ts +6 -0
  381. package/types/api/styles.d.ts +23 -10
  382. package/types/configs/blok-config.d.ts +29 -0
  383. package/types/configs/i18n-config.d.ts +52 -2
  384. package/types/configs/i18n-dictionary.d.ts +16 -90
  385. package/types/configs/sanitizer-config.d.ts +25 -1
  386. package/types/data-attributes.d.ts +170 -0
  387. package/types/data-formats/output-data.d.ts +15 -0
  388. package/types/full.d.ts +80 -0
  389. package/types/index.d.ts +30 -13
  390. package/types/locales.d.ts +59 -0
  391. package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
  392. package/types/tools/block-tool.d.ts +11 -2
  393. package/types/tools/header.d.ts +18 -0
  394. package/types/tools/index.d.ts +1 -0
  395. package/types/tools/list.d.ts +91 -0
  396. package/types/tools/paragraph.d.ts +71 -0
  397. package/types/tools/tool-settings.d.ts +99 -6
  398. package/types/tools/tool.d.ts +6 -0
  399. package/types/tools-entry.d.ts +49 -0
  400. package/types/utils/popover/popover-item.d.ts +24 -5
  401. package/types/utils/popover/popover.d.ts +13 -0
  402. package/dist/blok-C8XbyLHh.mjs +0 -25795
  403. package/dist/blok.umd.js +0 -181
package/dist/tools.mjs ADDED
@@ -0,0 +1,3126 @@
1
+ var nt = Object.defineProperty, rt = Object.defineProperties;
2
+ var st = Object.getOwnPropertyDescriptors;
3
+ var K = Object.getOwnPropertySymbols;
4
+ var ot = Object.prototype.hasOwnProperty, it = Object.prototype.propertyIsEnumerable;
5
+ var U = (f, t, e) => t in f ? nt(f, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : f[t] = e, w = (f, t) => {
6
+ for (var e in t || (t = {}))
7
+ ot.call(t, e) && U(f, e, t[e]);
8
+ if (K)
9
+ for (var e of K(t))
10
+ it.call(t, e) && U(f, e, t[e]);
11
+ return f;
12
+ }, P = (f, t) => rt(f, st(t));
13
+ import { t as x, D as m, a9 as et, aa as at, ab as lt, A as ct, ac as dt, ad as ut, ae as ht, af as ft, ag as pt, ah as mt, ai as G, aj as j, ak as $, f as A, al as gt, am as Et, S as H, P as Tt, an as Ct, l as At, J as yt } from "./chunks/inline-tool-convert-CLUxkCe_.mjs";
14
+ import { a0 as Dt } from "./chunks/inline-tool-convert-CLUxkCe_.mjs";
15
+ const W = [
16
+ "empty:before:pointer-events-none",
17
+ "empty:before:text-gray-text",
18
+ "empty:before:cursor-text",
19
+ "empty:before:content-[attr(data-placeholder)]",
20
+ "[&[data-blok-empty=true]]:before:pointer-events-none",
21
+ "[&[data-blok-empty=true]]:before:text-gray-text",
22
+ "[&[data-blok-empty=true]]:before:cursor-text",
23
+ "[&[data-blok-empty=true]]:before:content-[attr(data-placeholder)]"
24
+ ], bt = [
25
+ "empty:focus:before:pointer-events-none",
26
+ "empty:focus:before:text-gray-text",
27
+ "empty:focus:before:cursor-text",
28
+ "empty:focus:before:content-[attr(data-blok-placeholder-active)]",
29
+ "[&[data-empty=true]:focus]:before:pointer-events-none",
30
+ "[&[data-empty=true]:focus]:before:text-gray-text",
31
+ "[&[data-empty=true]:focus]:before:cursor-text",
32
+ "[&[data-empty=true]:focus]:before:content-[attr(data-blok-placeholder-active)]"
33
+ ], St = (f) => {
34
+ const t = f.innerHTML.trim();
35
+ return t === "" || t === "<br>";
36
+ }, Nt = (f) => {
37
+ f.innerHTML === "<br>" && (f.innerHTML = "");
38
+ const t = window.getSelection();
39
+ if (!t) return;
40
+ const e = document.createRange();
41
+ e.selectNodeContents(f), e.collapse(!0), t.removeAllRanges(), t.addRange(e);
42
+ }, Bt = (f) => {
43
+ St(f) && Nt(f);
44
+ }, V = (f, t, e = "data-placeholder") => {
45
+ f.setAttribute(e, t != null ? t : "");
46
+ const n = () => Bt(f);
47
+ f.addEventListener("focus", n), f.addEventListener("input", n);
48
+ }, kt = (f) => {
49
+ const t = document.createElement("div");
50
+ t.innerHTML = f.trim();
51
+ const e = document.createDocumentFragment();
52
+ return e.append(...Array.from(t.childNodes)), e;
53
+ }, M = class M {
54
+ /**
55
+ * Default placeholder for Paragraph Tool
56
+ *
57
+ * @returns translation key for the default placeholder
58
+ */
59
+ static get DEFAULT_PLACEHOLDER() {
60
+ return "tools.paragraph.placeholder";
61
+ }
62
+ /**
63
+ * Render plugin's main Element and fill it with saved data
64
+ *
65
+ * @param options - constructor options
66
+ * @param options.data - previously saved data
67
+ * @param options.config - user config for Tool
68
+ * @param options.api - editor.js api
69
+ * @param options.readOnly - read only mode flag
70
+ */
71
+ constructor({ data: t, config: e, api: n, readOnly: r }) {
72
+ var s, o, a;
73
+ this.api = n, this.readOnly = r, this.readOnly || (this.onKeyUp = this.onKeyUp.bind(this)), this._placeholder = (s = e == null ? void 0 : e.placeholder) != null ? s : M.DEFAULT_PLACEHOLDER, this._data = t != null ? t : { text: "" }, this._element = null, this._preserveBlank = (o = e == null ? void 0 : e.preserveBlank) != null ? o : !1, this._styles = (a = e == null ? void 0 : e.styles) != null ? a : {};
74
+ }
75
+ /**
76
+ * Check if text content is empty and set empty string to inner html.
77
+ * We need this because some browsers (e.g. Safari) insert <br> into empty contenteditable elements
78
+ *
79
+ * @param e - key up event
80
+ */
81
+ onKeyUp(t) {
82
+ if (t.code !== "Backspace" && t.code !== "Delete" || !this._element)
83
+ return;
84
+ const { textContent: e } = this._element;
85
+ e === "" && (this._element.innerHTML = "");
86
+ }
87
+ /**
88
+ * Build inline styles from style configuration
89
+ *
90
+ * @returns Partial CSSStyleDeclaration with custom styles
91
+ */
92
+ buildInlineStyles() {
93
+ const t = {};
94
+ return this._styles.size && (t.fontSize = this._styles.size), this._styles.lineHeight && (t.lineHeight = this._styles.lineHeight), this._styles.marginTop && (t.marginTop = this._styles.marginTop), this._styles.marginBottom && (t.marginBottom = this._styles.marginBottom), t;
95
+ }
96
+ drawView() {
97
+ const t = document.createElement("DIV");
98
+ t.className = x(
99
+ this.api.styles.block,
100
+ M.WRAPPER_CLASSES,
101
+ bt
102
+ ), t.setAttribute(m.tool, "paragraph"), t.contentEditable = "false";
103
+ const e = this.buildInlineStyles();
104
+ return Object.keys(e).length > 0 && Object.assign(t.style, e), this._data.text && (t.innerHTML = this._data.text), this.readOnly ? t.setAttribute("data-blok-placeholder-active", this.api.i18n.t(this._placeholder)) : (t.contentEditable = "true", t.addEventListener("keyup", this.onKeyUp), V(t, this.api.i18n.t(this._placeholder), "data-blok-placeholder-active")), t;
105
+ }
106
+ /**
107
+ * Return Tool's view
108
+ *
109
+ * @returns HTMLDivElement
110
+ */
111
+ render() {
112
+ return this._element = this.drawView(), this._element;
113
+ }
114
+ /**
115
+ * Method that specified how to merge two Text blocks.
116
+ * Called by Editor by backspace at the beginning of the Block
117
+ *
118
+ * @param data - saved data to merge with current block
119
+ */
120
+ merge(t) {
121
+ if (!this._element)
122
+ return;
123
+ this._data.text += t.text;
124
+ const e = kt(t.text);
125
+ this._element.appendChild(e), this._element.normalize();
126
+ }
127
+ /**
128
+ * Validate Paragraph block data:
129
+ * - check for emptiness
130
+ *
131
+ * @param savedData - data received after saving
132
+ * @returns false if saved data is not correct, otherwise true
133
+ */
134
+ validate(t) {
135
+ return !(t.text.trim() === "" && !this._preserveBlank);
136
+ }
137
+ /**
138
+ * Extract Tool's data from the view
139
+ *
140
+ * @param toolsContent - Paragraph tools rendered view
141
+ * @returns saved data
142
+ */
143
+ save(t) {
144
+ return {
145
+ text: et(t.innerHTML)
146
+ };
147
+ }
148
+ /**
149
+ * On paste callback fired from Editor.
150
+ *
151
+ * @param event - event with pasted data
152
+ */
153
+ onPaste(t) {
154
+ const e = t.detail;
155
+ if (!("data" in e))
156
+ return;
157
+ const r = {
158
+ text: e.data.innerHTML
159
+ };
160
+ this._data = r, window.requestAnimationFrame(() => {
161
+ this._element && (this._element.innerHTML = this._data.text || "");
162
+ });
163
+ }
164
+ /**
165
+ * Enable Conversion Toolbar. Paragraph can be converted to/from other tools
166
+ *
167
+ * @returns ConversionConfig
168
+ */
169
+ static get conversionConfig() {
170
+ return {
171
+ export: "text",
172
+ // to convert Paragraph to other block, use 'text' property of saved data
173
+ import: "text"
174
+ // to convert other block's exported string to Paragraph, fill 'text' property of tool data
175
+ };
176
+ }
177
+ /**
178
+ * Sanitizer rules
179
+ *
180
+ * @returns SanitizerConfig
181
+ */
182
+ static get sanitize() {
183
+ return {
184
+ text: {
185
+ br: !0
186
+ }
187
+ };
188
+ }
189
+ /**
190
+ * Returns true to notify the core that read-only mode is supported
191
+ *
192
+ * @returns true
193
+ */
194
+ static get isReadOnlySupported() {
195
+ return !0;
196
+ }
197
+ /**
198
+ * Used by Editor paste handling API.
199
+ * Provides configuration to handle P tags.
200
+ *
201
+ * @returns PasteConfig
202
+ */
203
+ static get pasteConfig() {
204
+ return {
205
+ tags: ["P"]
206
+ };
207
+ }
208
+ /**
209
+ * Icon and title for displaying at the Toolbox
210
+ *
211
+ * @returns ToolboxConfig
212
+ */
213
+ static get toolbox() {
214
+ return {
215
+ icon: at,
216
+ title: "Text",
217
+ titleKey: "text",
218
+ searchTerms: ["p", "paragraph", "plain"]
219
+ };
220
+ }
221
+ };
222
+ M.WRAPPER_CLASSES = [
223
+ "leading-[1.6em]",
224
+ "outline-none",
225
+ "mt-[2px]",
226
+ "mb-px",
227
+ "[&>p:first-of-type]:mt-0",
228
+ "[&>p:last-of-type]:mb-0"
229
+ ];
230
+ let X = M;
231
+ const L = class L {
232
+ /**
233
+ * Render plugin's main Element and fill it with saved data
234
+ *
235
+ * @param options - constructor options
236
+ * @param options.data - previously saved data
237
+ * @param options.config - user config for Tool
238
+ * @param options.api - Editor API
239
+ * @param options.readOnly - read only mode flag
240
+ */
241
+ constructor({ data: t, config: e, api: n, readOnly: r }) {
242
+ this.api = n, this.readOnly = r, this._settings = e || {}, this._data = this.normalizeData(t), this._element = this.getTag();
243
+ }
244
+ /**
245
+ * Styles
246
+ * @deprecated Use data-blok-tool attribute instead (DATA_ATTR.tool)
247
+ */
248
+ get _CSS() {
249
+ return {
250
+ block: this.api.styles.block,
251
+ wrapper: ""
252
+ };
253
+ }
254
+ /**
255
+ * Check if data is valid HeaderData
256
+ *
257
+ * @param data - data to check
258
+ * @returns true if data is HeaderData
259
+ */
260
+ isHeaderData(t) {
261
+ return typeof t == "object" && t !== null && "text" in t;
262
+ }
263
+ /**
264
+ * Normalize input data
265
+ *
266
+ * @param data - saved data to process
267
+ * @returns normalized HeaderData
268
+ */
269
+ normalizeData(t) {
270
+ if (!this.isHeaderData(t))
271
+ return { text: "", level: this.defaultLevel.number };
272
+ const e = parseInt(String(t.level)), n = t.level !== void 0 && !isNaN(e);
273
+ return {
274
+ text: t.text || "",
275
+ level: n ? e : this.defaultLevel.number
276
+ };
277
+ }
278
+ /**
279
+ * Return Tool's view
280
+ *
281
+ * @returns HTMLHeadingElement
282
+ */
283
+ render() {
284
+ return this._element;
285
+ }
286
+ /**
287
+ * Returns header block tunes config
288
+ *
289
+ * @returns MenuConfig array
290
+ */
291
+ renderSettings() {
292
+ const t = this._settings._toolboxEntries, e = (t == null ? void 0 : t.length) === 1 && t[0].data === void 0 && (t[0].title === void 0 || t[0].title === "Heading");
293
+ return t !== void 0 && t.length > 0 && !e ? this.buildSettingsFromToolboxEntries(t) : this.levels.map((n) => {
294
+ const r = this.api.i18n.t(n.nameKey), s = r !== n.nameKey ? r : n.name;
295
+ return {
296
+ icon: n.icon,
297
+ title: s,
298
+ onActivate: () => this.setLevel(n.number),
299
+ closeOnActivate: !0,
300
+ isActive: this.currentLevel.number === n.number,
301
+ dataset: {
302
+ "blok-header-level": String(n.number)
303
+ }
304
+ };
305
+ });
306
+ }
307
+ /**
308
+ * Build settings menu items from toolbox entries.
309
+ * This allows users to customize which levels appear in block settings
310
+ * by configuring the toolbox.
311
+ *
312
+ * @param entries - Merged toolbox entries from user config
313
+ * @returns MenuConfig array
314
+ */
315
+ buildSettingsFromToolboxEntries(t) {
316
+ return t.map((e) => {
317
+ var d, c, h, p;
318
+ const n = e.data, r = (d = n == null ? void 0 : n.level) != null ? d : this.defaultLevel.number, s = L.DEFAULT_LEVELS.find((g) => g.number === r), o = (c = s == null ? void 0 : s.name) != null ? c : `Heading ${r}`, a = this.resolveToolboxEntryTitle(e, o);
319
+ return {
320
+ icon: (p = (h = e.icon) != null ? h : s == null ? void 0 : s.icon) != null ? p : lt,
321
+ title: a,
322
+ onActivate: () => this.setLevel(r),
323
+ closeOnActivate: !0,
324
+ isActive: this.currentLevel.number === r,
325
+ dataset: {
326
+ "blok-header-level": String(r)
327
+ }
328
+ };
329
+ });
330
+ }
331
+ /**
332
+ * Resolves the title for a toolbox entry using the shared translation utility.
333
+ *
334
+ * @param entry - Toolbox entry
335
+ * @param fallback - Fallback title if no custom title or translation found
336
+ * @returns Resolved title string
337
+ */
338
+ resolveToolboxEntryTitle(t, e) {
339
+ return ct(this.api.i18n, t, e);
340
+ }
341
+ /**
342
+ * Callback for Block's settings buttons
343
+ *
344
+ * @param level - level to set
345
+ */
346
+ setLevel(t) {
347
+ this.data = {
348
+ level: t,
349
+ text: this.data.text
350
+ };
351
+ }
352
+ /**
353
+ * Method that specified how to merge two Text blocks.
354
+ * Called by Editor by backspace at the beginning of the Block
355
+ *
356
+ * @param data - saved data to merge with current block
357
+ */
358
+ merge(t) {
359
+ this._element.insertAdjacentHTML("beforeend", t.text);
360
+ }
361
+ /**
362
+ * Validate Text block data:
363
+ * - check for emptiness
364
+ *
365
+ * @param blockData - data received after saving
366
+ * @returns false if saved data is not correct, otherwise true
367
+ */
368
+ validate(t) {
369
+ return t.text.trim() !== "";
370
+ }
371
+ /**
372
+ * Extract Tool's data from the view
373
+ *
374
+ * @param toolsContent - Text tools rendered view
375
+ * @returns saved data
376
+ */
377
+ save(t) {
378
+ return {
379
+ text: t.innerHTML,
380
+ level: this.currentLevel.number
381
+ };
382
+ }
383
+ /**
384
+ * Allow Header to be converted to/from other blocks
385
+ */
386
+ static get conversionConfig() {
387
+ return {
388
+ export: "text",
389
+ // use 'text' property for other blocks
390
+ import: "text"
391
+ // fill 'text' property from other block's export string
392
+ };
393
+ }
394
+ /**
395
+ * Sanitizer Rules
396
+ */
397
+ static get sanitize() {
398
+ return {
399
+ level: !1,
400
+ text: {}
401
+ };
402
+ }
403
+ /**
404
+ * Returns true to notify core that read-only is supported
405
+ *
406
+ * @returns true
407
+ */
408
+ static get isReadOnlySupported() {
409
+ return !0;
410
+ }
411
+ /**
412
+ * Get current Tool's data
413
+ *
414
+ * @returns Current data
415
+ */
416
+ get data() {
417
+ return this._data.text = this._element.innerHTML, this._data.level = this.currentLevel.number, this._data;
418
+ }
419
+ /**
420
+ * Store data in plugin:
421
+ * - at the this._data property
422
+ * - at the HTML
423
+ *
424
+ * @param data - data to set
425
+ */
426
+ set data(t) {
427
+ if (this._data = this.normalizeData(t), t.level !== void 0 && this._element.parentNode) {
428
+ const e = this.getTag();
429
+ e.innerHTML = this._element.innerHTML, this._element.parentNode.replaceChild(e, this._element), this._element = e;
430
+ }
431
+ t.text !== void 0 && (this._element.innerHTML = this._data.text || "");
432
+ }
433
+ /**
434
+ * Get tag for target level
435
+ * By default returns second-leveled header
436
+ *
437
+ * @returns HTMLHeadingElement
438
+ */
439
+ getTag() {
440
+ const t = document.createElement(this.currentLevel.tag);
441
+ t.innerHTML = this._data.text || "", t.className = x(L.BASE_STYLES, this.currentLevel.styles, W);
442
+ const { inlineStyles: e } = this.currentLevel;
443
+ return e && Object.assign(t.style, e), t.setAttribute(m.tool, "header"), t.contentEditable = this.readOnly ? "false" : "true", this.readOnly ? t.setAttribute("data-placeholder", this.api.i18n.t(this._settings.placeholder || "")) : V(t, this.api.i18n.t(this._settings.placeholder || "")), t;
444
+ }
445
+ /**
446
+ * Get current level
447
+ *
448
+ * @returns Level object
449
+ */
450
+ get currentLevel() {
451
+ const t = this.levels.find((e) => e.number === this._data.level);
452
+ return t != null ? t : this.defaultLevel;
453
+ }
454
+ /**
455
+ * Return default level
456
+ *
457
+ * @returns Level object
458
+ */
459
+ get defaultLevel() {
460
+ if (!this._settings.defaultLevel)
461
+ return this.levels[1];
462
+ const t = this.levels.find((e) => e.number === this._settings.defaultLevel);
463
+ return t || (console.warn("(ง'̀-'́)ง Heading Tool: the default level specified was not found in available levels"), this.levels[1]);
464
+ }
465
+ /**
466
+ * Available header levels
467
+ *
468
+ * @returns Level array
469
+ */
470
+ get levels() {
471
+ const t = this._settings.levelOverrides || {}, e = L.DEFAULT_LEVELS.map((n) => {
472
+ var o;
473
+ const r = t[n.number] || {}, s = {};
474
+ return r.size && (s.fontSize = r.size), r.marginTop && (s.marginTop = r.marginTop), r.marginBottom && (s.marginBottom = r.marginBottom), {
475
+ number: n.number,
476
+ tag: ((o = r.tag) == null ? void 0 : o.toUpperCase()) || n.tag,
477
+ nameKey: n.nameKey,
478
+ name: r.name || n.name,
479
+ icon: n.icon,
480
+ styles: n.styles,
481
+ inlineStyles: s
482
+ };
483
+ });
484
+ return this._settings.levels ? e.filter((n) => this._settings.levels.includes(n.number)) : e;
485
+ }
486
+ /**
487
+ * Handle H1-H6 tags on paste to substitute it with header Tool
488
+ *
489
+ * @param event - event with pasted content
490
+ */
491
+ onPaste(t) {
492
+ var a;
493
+ const e = t.detail;
494
+ if (!("data" in e))
495
+ return;
496
+ const n = e.data, s = (a = {
497
+ H1: 1,
498
+ H2: 2,
499
+ H3: 3,
500
+ H4: 4,
501
+ H5: 5,
502
+ H6: 6
503
+ }[n.tagName]) != null ? a : this.defaultLevel.number, o = this._settings.levels ? this._settings.levels.reduce((l, d) => Math.abs(d - s) < Math.abs(l - s) ? d : l) : s;
504
+ this.data = {
505
+ level: o,
506
+ text: n.innerHTML
507
+ };
508
+ }
509
+ /**
510
+ * Used by Editor paste handling API.
511
+ * Provides configuration to handle H1-H6 tags.
512
+ *
513
+ * @returns PasteConfig
514
+ */
515
+ static get pasteConfig() {
516
+ return {
517
+ tags: ["H1", "H2", "H3", "H4", "H5", "H6"]
518
+ };
519
+ }
520
+ /**
521
+ * Get Tool toolbox settings
522
+ * Returns an array of all 6 heading levels, each with its own icon and title.
523
+ * The BlockToolAdapter will filter these based on the `levels` config if specified.
524
+ *
525
+ * @returns ToolboxConfig array with entries for H1-H6
526
+ */
527
+ static get toolbox() {
528
+ return L.DEFAULT_LEVELS.map((t) => ({
529
+ icon: t.icon,
530
+ title: t.name,
531
+ titleKey: t.nameKey,
532
+ name: `header-${t.number}`,
533
+ data: { level: t.number },
534
+ searchTerms: [`h${t.number}`, "title", "header", "heading"],
535
+ shortcut: "#".repeat(t.number)
536
+ }));
537
+ }
538
+ };
539
+ L.BASE_STYLES = "py-[3px] px-[2px] m-0 !leading-[1.3] outline-none [&_p]:!p-0 [&_p]:!m-0 [&_div]:!p-0 [&_div]:!m-0", L.DEFAULT_LEVELS = [
540
+ { number: 1, tag: "H1", nameKey: "tools.header.heading1", name: "Heading 1", icon: dt, styles: "text-4xl font-bold mt-8 mb-1" },
541
+ { number: 2, tag: "H2", nameKey: "tools.header.heading2", name: "Heading 2", icon: ut, styles: "text-3xl font-semibold mt-6 mb-px" },
542
+ { number: 3, tag: "H3", nameKey: "tools.header.heading3", name: "Heading 3", icon: ht, styles: "text-2xl font-semibold mt-4 mb-px" },
543
+ { number: 4, tag: "H4", nameKey: "tools.header.heading4", name: "Heading 4", icon: ft, styles: "text-xl font-semibold mt-3 mb-px" },
544
+ { number: 5, tag: "H5", nameKey: "tools.header.heading5", name: "Heading 5", icon: pt, styles: "text-base font-semibold mt-3 mb-px" },
545
+ { number: 6, tag: "H6", nameKey: "tools.header.heading6", name: "Heading 6", icon: mt, styles: "text-sm font-semibold mt-3 mb-px" }
546
+ ];
547
+ let Y = L;
548
+ const u = class u {
549
+ constructor({ data: t, config: e, api: n, readOnly: r, block: s }) {
550
+ this._element = null, this.handleBlockChanged = (o) => {
551
+ var l;
552
+ const a = o;
553
+ ((l = a == null ? void 0 : a.event) == null ? void 0 : l.type) === "block-removed" && (u.pendingMarkerUpdate || (u.pendingMarkerUpdate = !0, requestAnimationFrame(() => {
554
+ u.pendingMarkerUpdate = !1, this.updateAllOrderedListMarkers();
555
+ })));
556
+ }, this.api = n, this.readOnly = r, this._settings = e || {}, this._data = this.normalizeData(t), s && (this.blockId = s.id), this._data.style === "ordered" && this.api.events.on("block changed", this.handleBlockChanged);
557
+ }
558
+ normalizeData(t) {
559
+ var n;
560
+ const e = this._settings.defaultStyle || "unordered";
561
+ return !t || typeof t != "object" ? {
562
+ text: "",
563
+ style: e,
564
+ checked: !1,
565
+ depth: 0
566
+ } : w({
567
+ text: t.text || "",
568
+ style: t.style || e,
569
+ checked: !!t.checked,
570
+ depth: (n = t.depth) != null ? n : 0
571
+ }, t.start !== void 0 && t.start !== 1 ? { start: t.start } : {});
572
+ }
573
+ get currentStyleConfig() {
574
+ return u.STYLE_CONFIGS.find((t) => t.style === this._data.style) || u.STYLE_CONFIGS[0];
575
+ }
576
+ get availableStyles() {
577
+ const t = this._settings.styles;
578
+ return !t || t.length === 0 ? u.STYLE_CONFIGS : u.STYLE_CONFIGS.filter((e) => t.includes(e.style));
579
+ }
580
+ get itemColor() {
581
+ return this._settings.itemColor;
582
+ }
583
+ get itemSize() {
584
+ return this._settings.itemSize;
585
+ }
586
+ get placeholder() {
587
+ return this.api.i18n.t(u.PLACEHOLDER_KEY);
588
+ }
589
+ applyItemStyles(t) {
590
+ const e = t.style;
591
+ this.itemColor && (e.color = this.itemColor), this.itemSize && (e.fontSize = this.itemSize);
592
+ }
593
+ setupItemPlaceholder(t) {
594
+ this.readOnly || V(t, this.placeholder);
595
+ }
596
+ render() {
597
+ return this._element = this.createItemElement(), this._element;
598
+ }
599
+ /**
600
+ * Called after block content is added to the page.
601
+ * Updates the marker with the correct index now that we know our position,
602
+ * and also updates all sibling list items since their indices may have changed.
603
+ */
604
+ rendered() {
605
+ this.updateMarkersAfterPositionChange();
606
+ }
607
+ /**
608
+ * Called after block was moved.
609
+ * Validates and adjusts depth to follow list formation rules,
610
+ * then updates the marker to reflect the new position.
611
+ */
612
+ moved(t) {
613
+ this.validateAndAdjustDepthAfterMove(t.detail.toIndex), this.updateMarkersAfterPositionChange();
614
+ }
615
+ /**
616
+ * Updates this block's marker and all sibling ordered list markers.
617
+ * Called after this block's position may have changed (rendered, moved).
618
+ */
619
+ updateMarkersAfterPositionChange() {
620
+ this._data.style !== "ordered" || !this._element || (this.updateMarker(), this.updateSiblingListMarkers());
621
+ }
622
+ /**
623
+ * Validates and adjusts the depth of this list item after a drag-and-drop move.
624
+ * Ensures the depth follows list formation rules:
625
+ * 1. First item (index 0) must be at depth 0
626
+ * 2. Item depth cannot exceed previousItem.depth + 1
627
+ * 3. When dropped between nested items, adopt the sibling's depth
628
+ *
629
+ * @param newIndex - The new index where the block was moved to
630
+ */
631
+ validateAndAdjustDepthAfterMove(t) {
632
+ const e = this.getDepth(), n = this.calculateMaxAllowedDepth(t), r = this.calculateTargetDepthForPosition(t, n);
633
+ e !== r && this.adjustDepthTo(r);
634
+ }
635
+ /**
636
+ * Calculates the target depth for a list item dropped at the given index.
637
+ * When dropping into a nested context, the item should match the sibling's depth.
638
+ *
639
+ * @param blockIndex - The index where the block was dropped
640
+ * @param maxAllowedDepth - The maximum allowed depth at this position
641
+ * @returns The target depth for the dropped item
642
+ */
643
+ calculateTargetDepthForPosition(t, e) {
644
+ const n = this.getDepth();
645
+ if (n > e)
646
+ return e;
647
+ const r = this.api.blocks.getBlockByIndex(t + 1), s = r && r.name === u.TOOL_NAME, o = s ? this.getBlockDepth(r) : 0;
648
+ if (s && o > n && o <= e)
649
+ return o;
650
+ const l = t > 0 ? this.api.blocks.getBlockByIndex(t - 1) : null, d = l && l.name === u.TOOL_NAME, c = d ? this.getBlockDepth(l) : 0;
651
+ return d && !s && c > n && c <= e ? c : n;
652
+ }
653
+ /**
654
+ * Calculates the maximum allowed depth for a list item at the given index.
655
+ *
656
+ * Rules:
657
+ * 1. First item (index 0) must be at depth 0
658
+ * 2. For other items: maxDepth = previousListItem.depth + 1
659
+ * 3. If previous block is not a list item, maxDepth = 0
660
+ *
661
+ * @param blockIndex - The index of the block
662
+ * @returns The maximum allowed depth (0 or more)
663
+ */
664
+ calculateMaxAllowedDepth(t) {
665
+ if (t === 0)
666
+ return 0;
667
+ const e = this.api.blocks.getBlockByIndex(t - 1);
668
+ return !e || e.name !== u.TOOL_NAME ? 0 : this.getBlockDepth(e) + 1;
669
+ }
670
+ /**
671
+ * Adjusts the depth of this list item to the specified value.
672
+ * Updates internal data and the DOM element's indentation.
673
+ *
674
+ * @param newDepth - The new depth value
675
+ */
676
+ adjustDepthTo(t) {
677
+ var n;
678
+ this._data.depth = t, this._element && this._element.setAttribute("data-list-depth", String(t));
679
+ const e = (n = this._element) == null ? void 0 : n.querySelector('[role="listitem"]');
680
+ e instanceof HTMLElement && (e.style.marginLeft = t > 0 ? `${t * u.INDENT_PER_LEVEL}px` : "");
681
+ }
682
+ /**
683
+ * Called when this block is about to be removed.
684
+ * Updates sibling ordered list markers to renumber correctly after removal.
685
+ */
686
+ removed() {
687
+ this._data.style === "ordered" && (this.api.events.off("block changed", this.handleBlockChanged), requestAnimationFrame(() => {
688
+ this.updateAllOrderedListMarkers();
689
+ }));
690
+ }
691
+ /**
692
+ * Update markers on all ordered list items in the editor.
693
+ * Called when a list item is removed to ensure correct renumbering.
694
+ */
695
+ updateAllOrderedListMarkers() {
696
+ const t = this.api.blocks.getBlocksCount();
697
+ Array.from({ length: t }, (e, n) => n).forEach((e) => {
698
+ const n = this.api.blocks.getBlockByIndex(e);
699
+ if (!n || n.name !== u.TOOL_NAME)
700
+ return;
701
+ const r = n.holder;
702
+ r != null && r.querySelector('[data-list-style="ordered"]') && this.updateBlockMarker(n);
703
+ });
704
+ }
705
+ /**
706
+ * Update marker if this is an ordered list item.
707
+ */
708
+ updateMarkerIfOrdered() {
709
+ this._data.style !== "ordered" || !this._element || this.updateMarker();
710
+ }
711
+ /**
712
+ * Update the marker element with the correct index.
713
+ * Called after the block is rendered and positioned.
714
+ */
715
+ updateMarker() {
716
+ var s;
717
+ const t = (s = this._element) == null ? void 0 : s.querySelector("[data-list-marker]");
718
+ if (!t)
719
+ return;
720
+ const e = this.getDepth(), n = this.getSiblingIndex(), r = this.getOrderedMarkerText(n, e);
721
+ t.textContent = r;
722
+ }
723
+ /**
724
+ * Update markers on all sibling ordered list items.
725
+ * Called when this block is moved to ensure all list numbers are correct.
726
+ * Respects style boundaries - only updates items with the same style.
727
+ */
728
+ updateSiblingListMarkers() {
729
+ var o;
730
+ const t = this.blockId ? (o = this.api.blocks.getBlockIndex(this.blockId)) != null ? o : this.api.blocks.getCurrentBlockIndex() : this.api.blocks.getCurrentBlockIndex(), e = this.getDepth(), n = this._data.style, r = this.api.blocks.getBlocksCount(), s = this.findListGroupStartIndex(t, e, n);
731
+ this.updateMarkersInRange(s, r, t, e, n);
732
+ }
733
+ /**
734
+ * Find the starting index of a list group by walking backwards.
735
+ * Stops at style boundaries at the same depth (when encountering a different list style).
736
+ * Items at deeper depths are skipped regardless of their style.
737
+ */
738
+ findListGroupStartIndex(t, e, n) {
739
+ const r = (s, o) => {
740
+ if (s < 0)
741
+ return o;
742
+ const a = this.api.blocks.getBlockByIndex(s);
743
+ if (!a || a.name !== u.TOOL_NAME)
744
+ return o;
745
+ const l = this.getBlockDepth(a);
746
+ if (l < e)
747
+ return o;
748
+ if (l > e)
749
+ return r(s - 1, o);
750
+ const d = this.getBlockStyle(a);
751
+ return n !== void 0 && d !== n ? o : r(s - 1, s);
752
+ };
753
+ return r(t - 1, t);
754
+ }
755
+ /**
756
+ * Update markers for all list items in a range at the given depth.
757
+ * Stops at style boundaries at the same depth (when encountering a different list style).
758
+ * Items at deeper depths are skipped regardless of their style.
759
+ */
760
+ updateMarkersInRange(t, e, n, r, s) {
761
+ const o = (a) => {
762
+ if (a >= e)
763
+ return;
764
+ if (a === n) {
765
+ o(a + 1);
766
+ return;
767
+ }
768
+ const l = this.api.blocks.getBlockByIndex(a);
769
+ if (!l || l.name !== u.TOOL_NAME)
770
+ return;
771
+ const d = this.getBlockDepth(l);
772
+ if (d < r)
773
+ return;
774
+ if (d > r) {
775
+ o(a + 1);
776
+ return;
777
+ }
778
+ const c = this.getBlockStyle(l);
779
+ s !== void 0 && c !== s || (this.updateBlockMarker(l), o(a + 1));
780
+ };
781
+ o(t);
782
+ }
783
+ /**
784
+ * Get the depth of a block by reading from its DOM
785
+ */
786
+ getBlockDepth(t) {
787
+ if (!t)
788
+ return 0;
789
+ const e = t.holder, n = e == null ? void 0 : e.querySelector('[role="listitem"]'), r = n == null ? void 0 : n.getAttribute("style"), s = r == null ? void 0 : r.match(/margin-left:\s*(\d+)px/);
790
+ return s ? Math.round(parseInt(s[1], 10) / u.INDENT_PER_LEVEL) : 0;
791
+ }
792
+ /**
793
+ * Get the style of a block by reading from its DOM
794
+ */
795
+ getBlockStyle(t) {
796
+ if (!t)
797
+ return null;
798
+ const e = t.holder, n = e == null ? void 0 : e.querySelector("[data-list-style]");
799
+ return (n == null ? void 0 : n.getAttribute("data-list-style")) || null;
800
+ }
801
+ /**
802
+ * Update the marker of a specific block by finding its marker element and recalculating
803
+ */
804
+ updateBlockMarker(t) {
805
+ if (!t)
806
+ return;
807
+ const e = t.holder, n = e == null ? void 0 : e.querySelector('[data-list-style="ordered"]');
808
+ if (!n)
809
+ return;
810
+ const r = n.querySelector("[data-list-marker]");
811
+ if (!r)
812
+ return;
813
+ const s = this.api.blocks.getBlockIndex(t.id);
814
+ if (s == null)
815
+ return;
816
+ const o = this.getBlockDepth(t), a = this.getBlockStyle(t) || "ordered", l = this.countPrecedingSiblingsAtDepth(s, o, a), c = this.getListStartValueForBlock(s, o, l, a) + l, h = this.formatOrderedMarker(c, o);
817
+ r.textContent = h;
818
+ }
819
+ /**
820
+ * Format an ordered list marker based on the number and depth
821
+ */
822
+ formatOrderedMarker(t, e) {
823
+ const n = e % 3;
824
+ return n === 1 ? `${this.numberToLowerAlpha(t)}.` : n === 2 ? `${this.numberToLowerRoman(t)}.` : `${t}.`;
825
+ }
826
+ /**
827
+ * Count preceding list items at the same depth and style for a given block index
828
+ */
829
+ countPrecedingSiblingsAtDepth(t, e, n) {
830
+ return t <= 0 ? 0 : this.countPrecedingListItemsAtDepthFromIndex(t - 1, e, n);
831
+ }
832
+ /**
833
+ * Recursively count preceding list items at the given depth and style starting from index.
834
+ * Stops at style boundaries at the same depth (when encountering a different list style).
835
+ * Items at deeper depths are skipped regardless of their style.
836
+ */
837
+ countPrecedingListItemsAtDepthFromIndex(t, e, n) {
838
+ if (t < 0)
839
+ return 0;
840
+ const r = this.api.blocks.getBlockByIndex(t);
841
+ if (!r || r.name !== u.TOOL_NAME)
842
+ return 0;
843
+ const s = this.getBlockDepth(r);
844
+ if (s < e)
845
+ return 0;
846
+ if (s > e)
847
+ return this.countPrecedingListItemsAtDepthFromIndex(t - 1, e, n);
848
+ const o = this.getBlockStyle(r);
849
+ return n !== void 0 && o !== n ? 0 : 1 + this.countPrecedingListItemsAtDepthFromIndex(t - 1, e, n);
850
+ }
851
+ /**
852
+ * Get the list start value for a block at a given index and depth
853
+ */
854
+ getListStartValueForBlock(t, e, n, r) {
855
+ if (n === 0)
856
+ return this.getBlockStartValue(t);
857
+ const s = this.findFirstListItemIndexFromBlock(t - 1, e, n, r);
858
+ return s === null ? 1 : this.getBlockStartValue(s);
859
+ }
860
+ /**
861
+ * Get the start value from a block's data-list-start attribute
862
+ */
863
+ getBlockStartValue(t) {
864
+ const e = this.api.blocks.getBlockByIndex(t);
865
+ if (!e)
866
+ return 1;
867
+ const n = e.holder, r = n == null ? void 0 : n.querySelector("[data-list-style]"), s = r == null ? void 0 : r.getAttribute("data-list-start");
868
+ return s ? parseInt(s, 10) : 1;
869
+ }
870
+ /**
871
+ * Find the first list item in a consecutive group.
872
+ * Stops at style boundaries at the same depth (when encountering a different list style).
873
+ * Items at deeper depths are skipped regardless of their style.
874
+ */
875
+ findFirstListItemIndexFromBlock(t, e, n, r) {
876
+ if (t < 0 || n <= 0)
877
+ return t + 1;
878
+ const s = this.api.blocks.getBlockByIndex(t);
879
+ if (!s || s.name !== u.TOOL_NAME)
880
+ return t + 1;
881
+ const o = this.getBlockDepth(s);
882
+ if (o < e)
883
+ return t + 1;
884
+ if (o > e)
885
+ return this.findFirstListItemIndexFromBlock(t - 1, e, n, r);
886
+ const a = this.getBlockStyle(s);
887
+ return r !== void 0 && a !== r ? t + 1 : this.findFirstListItemIndexFromBlock(t - 1, e, n - 1, r);
888
+ }
889
+ createItemElement() {
890
+ const { style: t } = this._data, e = document.createElement("div");
891
+ e.className = u.BASE_STYLES, e.setAttribute(m.tool, u.TOOL_NAME), e.setAttribute("data-list-style", t), e.setAttribute("data-list-depth", String(this.getDepth())), this._data.start !== void 0 && this._data.start !== 1 && e.setAttribute("data-list-start", String(this._data.start));
892
+ const n = t === "checklist" ? this.createChecklistContent() : this.createStandardContent();
893
+ return e.appendChild(n), this.readOnly || e.addEventListener("keydown", this.handleKeyDown.bind(this)), e;
894
+ }
895
+ createStandardContent() {
896
+ const t = document.createElement("div");
897
+ t.setAttribute("role", "listitem"), t.className = x(u.ITEM_STYLES, "flex", ...W), this.applyItemStyles(t);
898
+ const e = this.getDepth();
899
+ e > 0 && (t.style.marginLeft = `${e * u.INDENT_PER_LEVEL}px`);
900
+ const n = this.createListMarker();
901
+ n.setAttribute("data-list-marker", "true"), t.appendChild(n);
902
+ const r = document.createElement("div");
903
+ return r.className = x("flex-1 min-w-0 outline-none", ...W), r.contentEditable = this.readOnly ? "false" : "true", r.innerHTML = this._data.text, this.setupItemPlaceholder(r), t.appendChild(r), t;
904
+ }
905
+ createChecklistContent() {
906
+ const t = document.createElement("div");
907
+ t.setAttribute("role", "listitem"), t.className = u.CHECKLIST_ITEM_STYLES, this.applyItemStyles(t);
908
+ const e = this.getDepth();
909
+ e > 0 && (t.style.marginLeft = `${e * u.INDENT_PER_LEVEL}px`);
910
+ const n = document.createElement("input");
911
+ n.type = "checkbox", n.className = u.CHECKBOX_STYLES, n.checked = !!this._data.checked, n.disabled = this.readOnly;
912
+ const r = document.createElement("div");
913
+ return r.className = x(
914
+ "flex-1 outline-none leading-[1.6em]",
915
+ this._data.checked ? "line-through opacity-60" : "",
916
+ ...W
917
+ ), r.contentEditable = this.readOnly ? "false" : "true", r.innerHTML = this._data.text, this.setupItemPlaceholder(r), this.readOnly || n.addEventListener("change", () => {
918
+ this._data.checked = n.checked, r.classList.toggle("line-through", n.checked), r.classList.toggle("opacity-60", n.checked);
919
+ }), t.appendChild(n), t.appendChild(r), t;
920
+ }
921
+ /**
922
+ * Create the marker element (bullet or number) for a list item
923
+ */
924
+ createListMarker() {
925
+ const t = document.createElement("span");
926
+ t.className = "flex-shrink-0 select-none", t.setAttribute("aria-hidden", "true"), t.contentEditable = "false";
927
+ const e = this.getDepth();
928
+ if (this._data.style === "ordered") {
929
+ const n = this.getSiblingIndex(), r = this.getOrderedMarkerText(n, e);
930
+ t.textContent = r, t.className = x(t.className, "text-right"), t.style.paddingRight = "11px", t.style.minWidth = "fit-content";
931
+ } else {
932
+ const n = this.getBulletCharacter(e);
933
+ t.textContent = n, t.className = x(t.className, "w-6 text-center flex justify-center"), t.style.paddingLeft = "1px", t.style.paddingRight = "13px", t.style.fontSize = "24px", t.style.fontFamily = "Arial";
934
+ }
935
+ return t;
936
+ }
937
+ /**
938
+ * Calculate the index of this ListItem among consecutive siblings with the same style.
939
+ * This is used to determine the correct number for ordered lists.
940
+ */
941
+ getSiblingIndex() {
942
+ var n;
943
+ const t = this.blockId ? (n = this.api.blocks.getBlockIndex(this.blockId)) != null ? n : this.api.blocks.getCurrentBlockIndex() : this.api.blocks.getCurrentBlockIndex();
944
+ if (t <= 0)
945
+ return 0;
946
+ const e = this.getDepth();
947
+ return this.countPrecedingListItemsAtDepth(t - 1, e);
948
+ }
949
+ /**
950
+ * Recursively count consecutive preceding list blocks at the same depth and style.
951
+ * Stops when encountering a block that's not a list, a list at a shallower depth (parent),
952
+ * or a list with a different style at the same depth (treating style changes as list boundaries).
953
+ * Items at deeper depths are skipped regardless of their style.
954
+ */
955
+ countPrecedingListItemsAtDepth(t, e) {
956
+ var c;
957
+ if (t < 0)
958
+ return 0;
959
+ const n = this.api.blocks.getBlockByIndex(t);
960
+ if (!n || n.name !== u.TOOL_NAME)
961
+ return 0;
962
+ const r = n.holder, s = r == null ? void 0 : r.querySelector("[data-list-style]"), o = (c = s == null ? void 0 : s.querySelector('[role="listitem"]')) == null ? void 0 : c.getAttribute("style"), a = o == null ? void 0 : o.match(/margin-left:\s*(\d+)px/), l = a ? Math.round(parseInt(a[1], 10) / u.INDENT_PER_LEVEL) : 0;
963
+ return l < e ? 0 : l > e ? this.countPrecedingListItemsAtDepth(t - 1, e) : (s == null ? void 0 : s.getAttribute("data-list-style")) !== this._data.style ? 0 : 1 + this.countPrecedingListItemsAtDepth(t - 1, e);
964
+ }
965
+ /**
966
+ * Get the depth of this item in the hierarchy (0 = root level)
967
+ */
968
+ getDepth() {
969
+ var t;
970
+ return (t = this._data.depth) != null ? t : 0;
971
+ }
972
+ /**
973
+ * Get the appropriate bullet character based on nesting depth
974
+ */
975
+ getBulletCharacter(t) {
976
+ const e = ["•", "◦", "▪"];
977
+ return e[t % e.length];
978
+ }
979
+ /**
980
+ * Get the ordered list marker text based on depth and index
981
+ */
982
+ getOrderedMarkerText(t, e) {
983
+ const r = this.getListStartValue(t, e) + t;
984
+ switch (e % 3) {
985
+ case 0:
986
+ return `${r}.`;
987
+ case 1:
988
+ return `${this.numberToLowerAlpha(r)}.`;
989
+ case 2:
990
+ return `${this.numberToLowerRoman(r)}.`;
991
+ default:
992
+ return `${r}.`;
993
+ }
994
+ }
995
+ /**
996
+ * Get the starting number for this list group.
997
+ * Looks up the first item in the consecutive list group to find its start value.
998
+ */
999
+ getListStartValue(t, e) {
1000
+ var d, c;
1001
+ if (t === 0)
1002
+ return (d = this._data.start) != null ? d : 1;
1003
+ const n = this.blockId ? (c = this.api.blocks.getBlockIndex(this.blockId)) != null ? c : this.api.blocks.getCurrentBlockIndex() : this.api.blocks.getCurrentBlockIndex(), r = this.findFirstListItemIndex(n - 1, e, t);
1004
+ if (r === null)
1005
+ return 1;
1006
+ const s = this.api.blocks.getBlockByIndex(r);
1007
+ if (!s)
1008
+ return 1;
1009
+ const o = s.holder, a = o == null ? void 0 : o.querySelector("[data-list-style]"), l = a == null ? void 0 : a.getAttribute("data-list-start");
1010
+ return l ? parseInt(l, 10) : 1;
1011
+ }
1012
+ /**
1013
+ * Find the index of the first list item in this consecutive group.
1014
+ * Walks backwards through the blocks counting items at the same depth and style.
1015
+ * Stops at style boundaries at the same depth (when encountering a different list style).
1016
+ * Items at deeper depths are skipped regardless of their style.
1017
+ */
1018
+ findFirstListItemIndex(t, e, n) {
1019
+ var h;
1020
+ if (t < 0 || n <= 0)
1021
+ return t + 1;
1022
+ const r = this.api.blocks.getBlockByIndex(t);
1023
+ if (!r || r.name !== u.TOOL_NAME)
1024
+ return t + 1;
1025
+ const s = r.holder, o = s == null ? void 0 : s.querySelector("[data-list-style]"), a = (h = o == null ? void 0 : o.querySelector('[role="listitem"]')) == null ? void 0 : h.getAttribute("style"), l = a == null ? void 0 : a.match(/margin-left:\s*(\d+)px/), d = l ? Math.round(parseInt(l[1], 10) / u.INDENT_PER_LEVEL) : 0;
1026
+ return d < e ? t + 1 : d > e ? this.findFirstListItemIndex(t - 1, e, n) : (o == null ? void 0 : o.getAttribute("data-list-style")) !== this._data.style ? t + 1 : this.findFirstListItemIndex(t - 1, e, n - 1);
1027
+ }
1028
+ numberToLowerAlpha(t) {
1029
+ const e = (n) => {
1030
+ if (n <= 0) return "";
1031
+ const r = n - 1;
1032
+ return e(Math.floor(r / 26)) + String.fromCharCode(97 + r % 26);
1033
+ };
1034
+ return e(t);
1035
+ }
1036
+ numberToLowerRoman(t) {
1037
+ const e = [
1038
+ [1e3, "m"],
1039
+ [900, "cm"],
1040
+ [500, "d"],
1041
+ [400, "cd"],
1042
+ [100, "c"],
1043
+ [90, "xc"],
1044
+ [50, "l"],
1045
+ [40, "xl"],
1046
+ [10, "x"],
1047
+ [9, "ix"],
1048
+ [5, "v"],
1049
+ [4, "iv"],
1050
+ [1, "i"]
1051
+ ], n = (r, s) => {
1052
+ if (r <= 0 || s >= e.length) return "";
1053
+ const [o, a] = e[s];
1054
+ return r >= o ? a + n(r - o, s) : n(r, s + 1);
1055
+ };
1056
+ return n(t, 0);
1057
+ }
1058
+ handleKeyDown(t) {
1059
+ if (t.key === "Enter" && !t.shiftKey) {
1060
+ t.preventDefault(), this.handleEnter();
1061
+ return;
1062
+ }
1063
+ if (t.key === "Backspace") {
1064
+ this.handleBackspace(t);
1065
+ return;
1066
+ }
1067
+ if (!(t.key !== "Tab" || document.querySelectorAll('[data-blok-selected="true"]').length > 1)) {
1068
+ if (t.preventDefault(), t.shiftKey) {
1069
+ this.handleOutdent();
1070
+ return;
1071
+ }
1072
+ this.handleIndent();
1073
+ }
1074
+ }
1075
+ async handleEnter() {
1076
+ const t = window.getSelection();
1077
+ if (!t || !this._element) return;
1078
+ const e = this.getContentElement();
1079
+ if (!e) return;
1080
+ const n = e.innerHTML.trim();
1081
+ if (n === "" || n === "<br>") {
1082
+ await this.exitListOrOutdent();
1083
+ return;
1084
+ }
1085
+ const r = t.getRangeAt(0), { beforeContent: s, afterContent: o } = this.splitContentAtCursor(e, r);
1086
+ e.innerHTML = s, this._data.text = s;
1087
+ const a = this.api.blocks.getCurrentBlockIndex(), l = this.api.blocks.insert(u.TOOL_NAME, {
1088
+ text: o,
1089
+ style: this._data.style,
1090
+ checked: !1,
1091
+ depth: this._data.depth
1092
+ }, void 0, a + 1, !0);
1093
+ this.setCaretToBlockContent(l, "start");
1094
+ }
1095
+ async exitListOrOutdent() {
1096
+ const t = this.api.blocks.getCurrentBlockIndex();
1097
+ if (this.getDepth() > 0) {
1098
+ await this.handleOutdent();
1099
+ return;
1100
+ }
1101
+ await this.api.blocks.delete(t);
1102
+ const n = this.api.blocks.insert("paragraph", { text: "" }, void 0, t, !0);
1103
+ this.setCaretToBlockContent(n, "start");
1104
+ }
1105
+ async handleBackspace(t) {
1106
+ const e = window.getSelection();
1107
+ if (!e || !this._element) return;
1108
+ const n = e.getRangeAt(0), r = this.getContentElement();
1109
+ if (!r) return;
1110
+ this.syncContentFromDOM();
1111
+ const s = this.api.blocks.getCurrentBlockIndex(), o = this._data.text, a = this.getDepth();
1112
+ if (this.isEntireContentSelected(r, n) && !e.isCollapsed) {
1113
+ t.preventDefault(), r.innerHTML = "", this._data.text = "";
1114
+ const c = document.createRange();
1115
+ c.setStart(r, 0), c.collapse(!0), e.removeAllRanges(), e.addRange(c);
1116
+ return;
1117
+ }
1118
+ if (!this.isAtStart(r, n)) return;
1119
+ t.preventDefault(), await this.api.blocks.delete(s);
1120
+ const d = this.api.blocks.insert(
1121
+ "paragraph",
1122
+ { text: o },
1123
+ void 0,
1124
+ s,
1125
+ !0
1126
+ );
1127
+ a > 0 && requestAnimationFrame(() => {
1128
+ const c = d.holder;
1129
+ c && (c.style.marginLeft = `${a * u.INDENT_PER_LEVEL}px`, c.setAttribute("data-blok-depth", String(a)));
1130
+ }), this.setCaretToBlockContent(d, "start");
1131
+ }
1132
+ /**
1133
+ * Collect all text nodes from an element
1134
+ * @param node - Node to collect text nodes from
1135
+ * @returns Array of text nodes
1136
+ */
1137
+ collectTextNodes(t) {
1138
+ var e;
1139
+ return t.nodeType === Node.TEXT_NODE ? [t] : (e = t.hasChildNodes) != null && e.call(t) ? Array.from(t.childNodes).flatMap((n) => this.collectTextNodes(n)) : [];
1140
+ }
1141
+ /**
1142
+ * Find the text node and offset for a given character position
1143
+ * @param textNodes - Array of text nodes to search through
1144
+ * @param targetPosition - Character position to find
1145
+ * @returns Object with node and offset, or null if not found
1146
+ */
1147
+ findCaretPosition(t, e) {
1148
+ const n = t.reduce(
1149
+ (r, s) => {
1150
+ var a, l;
1151
+ if (r.found) return r;
1152
+ const o = (l = (a = s.textContent) == null ? void 0 : a.length) != null ? l : 0;
1153
+ return r.charCount + o >= e ? {
1154
+ found: !0,
1155
+ charCount: r.charCount,
1156
+ node: s,
1157
+ offset: e - r.charCount
1158
+ } : P(w({}, r), {
1159
+ charCount: r.charCount + o
1160
+ });
1161
+ },
1162
+ { found: !1, charCount: 0, node: null, offset: 0 }
1163
+ );
1164
+ return n.node ? { node: n.node, offset: n.offset } : null;
1165
+ }
1166
+ /**
1167
+ * Sync the current DOM content to the data model
1168
+ */
1169
+ syncContentFromDOM() {
1170
+ var n;
1171
+ const t = this.getContentElement();
1172
+ if (t && (this._data.text = t.innerHTML), this._data.style !== "checklist")
1173
+ return;
1174
+ const e = (n = this._element) == null ? void 0 : n.querySelector('input[type="checkbox"]');
1175
+ e && (this._data.checked = e.checked);
1176
+ }
1177
+ /**
1178
+ * Get the depth of the parent list item by walking backwards through preceding items.
1179
+ * A parent is the first preceding list item with a depth less than the current item.
1180
+ * @param blockIndex - The index of the current block
1181
+ * @returns The parent's depth, or -1 if no parent exists (at root level)
1182
+ */
1183
+ getParentDepth(t) {
1184
+ const e = this.getDepth(), n = (r) => {
1185
+ if (r < 0)
1186
+ return -1;
1187
+ const s = this.api.blocks.getBlockByIndex(r);
1188
+ if (!s || s.name !== u.TOOL_NAME)
1189
+ return -1;
1190
+ const o = this.getBlockDepth(s);
1191
+ return o < e ? o : n(r - 1);
1192
+ };
1193
+ return n(t - 1);
1194
+ }
1195
+ async handleIndent() {
1196
+ const t = this.api.blocks.getCurrentBlockIndex();
1197
+ if (t === 0) return;
1198
+ const e = this.api.blocks.getBlockByIndex(t - 1);
1199
+ if (!e || e.name !== u.TOOL_NAME) return;
1200
+ const n = this.getDepth(), r = this.getBlockDepth(e);
1201
+ if (n > r) return;
1202
+ this.syncContentFromDOM();
1203
+ const s = n + 1;
1204
+ this._data.depth = s;
1205
+ const o = await this.api.blocks.update(this.blockId || "", P(w({}, this._data), {
1206
+ depth: s
1207
+ }));
1208
+ this.setCaretToBlockContent(o);
1209
+ }
1210
+ async handleOutdent() {
1211
+ const t = this.getDepth();
1212
+ if (t === 0) return;
1213
+ this.syncContentFromDOM();
1214
+ const e = t - 1;
1215
+ this._data.depth = e;
1216
+ const n = await this.api.blocks.update(this.blockId || "", P(w({}, this._data), {
1217
+ depth: e
1218
+ }));
1219
+ this.setCaretToBlockContent(n);
1220
+ }
1221
+ getContentElement() {
1222
+ return this._element ? this._data.style === "checklist" ? this._element.querySelector("[contenteditable]") : this._element.querySelector("div.flex-1") : null;
1223
+ }
1224
+ /**
1225
+ * Sets caret to the content element of a block after ensuring DOM is ready.
1226
+ * Uses requestAnimationFrame to wait for the browser to process DOM updates.
1227
+ * @param block - BlockAPI to set caret to
1228
+ * @param position - 'start' or 'end' position (defaults to 'end')
1229
+ */
1230
+ setCaretToBlockContent(t, e = "end") {
1231
+ requestAnimationFrame(() => {
1232
+ const n = t.holder;
1233
+ if (!n) return;
1234
+ const r = n.querySelector('[contenteditable="true"]');
1235
+ if (!r) {
1236
+ this.api.caret.setToBlock(t, e);
1237
+ return;
1238
+ }
1239
+ r.focus();
1240
+ const s = window.getSelection();
1241
+ if (!s) return;
1242
+ const o = document.createRange();
1243
+ e === "start" ? (o.setStart(r, 0), o.collapse(!0)) : (o.selectNodeContents(r), o.collapse(!1)), s.removeAllRanges(), s.addRange(o);
1244
+ });
1245
+ }
1246
+ isAtStart(t, e) {
1247
+ const n = document.createRange();
1248
+ return n.selectNodeContents(t), n.setEnd(e.startContainer, e.startOffset), n.toString().length === 0;
1249
+ }
1250
+ /**
1251
+ * Check if the entire content of an element is selected
1252
+ * @param element - The content element to check
1253
+ * @param range - The current selection range
1254
+ * @returns true if the entire content is selected
1255
+ */
1256
+ isEntireContentSelected(t, e) {
1257
+ const n = document.createRange();
1258
+ n.selectNodeContents(t), n.setEnd(e.startContainer, e.startOffset);
1259
+ const r = n.toString().length === 0, s = document.createRange();
1260
+ s.selectNodeContents(t), s.setStart(e.endContainer, e.endOffset);
1261
+ const o = s.toString().length === 0;
1262
+ return r && o;
1263
+ }
1264
+ splitContentAtCursor(t, e) {
1265
+ const n = document.createRange();
1266
+ n.setStart(t, 0), n.setEnd(e.startContainer, e.startOffset);
1267
+ const r = document.createRange();
1268
+ return r.setStart(e.endContainer, e.endOffset), r.setEndAfter(t.lastChild || t), {
1269
+ beforeContent: this.getFragmentHTML(n.cloneContents()),
1270
+ afterContent: this.getFragmentHTML(r.cloneContents())
1271
+ };
1272
+ }
1273
+ getFragmentHTML(t) {
1274
+ const e = document.createElement("div");
1275
+ return e.appendChild(t), e.innerHTML;
1276
+ }
1277
+ renderSettings() {
1278
+ return this.availableStyles.map((t) => ({
1279
+ icon: t.icon,
1280
+ label: this.api.i18n.t(`toolNames.${t.name}`),
1281
+ onActivate: () => this.setStyle(t.style),
1282
+ closeOnActivate: !0,
1283
+ isActive: this._data.style === t.style
1284
+ }));
1285
+ }
1286
+ setStyle(t) {
1287
+ const e = this._data.style;
1288
+ this._data.style = t, this.rerender(), e !== t && requestAnimationFrame(() => {
1289
+ this.updateAllOrderedListMarkers();
1290
+ });
1291
+ }
1292
+ rerender() {
1293
+ if (!this._element) return;
1294
+ const t = this._element.parentNode;
1295
+ if (!t) return;
1296
+ const e = this.createItemElement();
1297
+ t.replaceChild(e, this._element), this._element = e;
1298
+ }
1299
+ validate(t) {
1300
+ return typeof t.text == "string";
1301
+ }
1302
+ save() {
1303
+ if (!this._element) return this._data;
1304
+ const t = this.getContentElement(), n = {
1305
+ text: t ? et(t.innerHTML) : this._data.text,
1306
+ style: this._data.style,
1307
+ checked: this._data.checked
1308
+ };
1309
+ return this._data.start !== void 0 && this._data.start !== 1 && (n.start = this._data.start), this._data.depth !== void 0 && this._data.depth > 0 && (n.depth = this._data.depth), n;
1310
+ }
1311
+ merge(t) {
1312
+ if (!this._element)
1313
+ return;
1314
+ this._data.text += t.text;
1315
+ const e = this.getContentElement();
1316
+ if (e && t.text) {
1317
+ const n = this.parseHtml(t.text);
1318
+ e.appendChild(n), e.normalize();
1319
+ }
1320
+ }
1321
+ /**
1322
+ * Parse HTML string into a DocumentFragment
1323
+ * @param html - HTML string to parse
1324
+ * @returns DocumentFragment with parsed nodes
1325
+ */
1326
+ parseHtml(t) {
1327
+ const e = document.createElement("div");
1328
+ e.innerHTML = t.trim();
1329
+ const n = document.createDocumentFragment();
1330
+ return n.append(...Array.from(e.childNodes)), n;
1331
+ }
1332
+ static get conversionConfig() {
1333
+ return {
1334
+ export: (t) => t.text,
1335
+ import: (t) => ({
1336
+ text: t,
1337
+ style: "unordered",
1338
+ checked: !1
1339
+ })
1340
+ };
1341
+ }
1342
+ static get sanitize() {
1343
+ return {
1344
+ text: {
1345
+ br: !0,
1346
+ a: {
1347
+ href: !0,
1348
+ target: "_blank",
1349
+ rel: "nofollow"
1350
+ },
1351
+ b: !0,
1352
+ i: !0,
1353
+ mark: !0
1354
+ }
1355
+ };
1356
+ }
1357
+ static get pasteConfig() {
1358
+ return { tags: ["LI"] };
1359
+ }
1360
+ onPaste(t) {
1361
+ const e = t.detail;
1362
+ if (!("data" in e)) return;
1363
+ const n = e.data, r = n.innerHTML || n.textContent || "", s = n.querySelector('input[type="checkbox"]'), o = (s == null ? void 0 : s.checked) || !1;
1364
+ this._data = {
1365
+ text: r,
1366
+ style: this.detectStyleFromPastedContent(n),
1367
+ checked: o
1368
+ }, this.rerender();
1369
+ }
1370
+ /**
1371
+ * Detect list style from pasted content based on parent element
1372
+ */
1373
+ detectStyleFromPastedContent(t) {
1374
+ const e = t.parentElement;
1375
+ return e ? e.tagName === "OL" ? "ordered" : e.tagName !== "UL" ? this._data.style : t.querySelector('input[type="checkbox"]') ? "checklist" : "unordered" : this._data.style;
1376
+ }
1377
+ static get isReadOnlySupported() {
1378
+ return !0;
1379
+ }
1380
+ /**
1381
+ * Returns the horizontal offset of the content at the hovered element.
1382
+ * Used by the toolbar to position itself closer to nested list items.
1383
+ *
1384
+ * @param hoveredElement - The element that is currently being hovered
1385
+ * @returns Object with left offset in pixels based on the list item's depth
1386
+ */
1387
+ getContentOffset(t) {
1388
+ var r;
1389
+ const e = (r = t.closest('[role="listitem"]')) != null ? r : t.querySelector('[role="listitem"]'), n = this.getMarginLeftFromElement(e);
1390
+ return n !== void 0 ? n : this.getOffsetFromDepthAttribute(t);
1391
+ }
1392
+ /**
1393
+ * Extracts the margin-left value from an element's inline style
1394
+ * @param element - The element to extract margin-left from
1395
+ * @returns Object with left offset if valid margin-left found, undefined otherwise
1396
+ */
1397
+ getMarginLeftFromElement(t) {
1398
+ if (!t)
1399
+ return;
1400
+ const n = (t.getAttribute("style") || "").match(/margin-left:\s*(\d+)px/);
1401
+ if (!n)
1402
+ return;
1403
+ const r = parseInt(n[1], 10);
1404
+ return r > 0 ? { left: r } : void 0;
1405
+ }
1406
+ /**
1407
+ * Gets the offset from the data-list-depth attribute
1408
+ * @param hoveredElement - The element to start searching from
1409
+ * @returns Object with left offset based on depth, undefined if depth is 0 or not found
1410
+ */
1411
+ getOffsetFromDepthAttribute(t) {
1412
+ const e = t.closest("[data-list-depth]");
1413
+ if (!e)
1414
+ return;
1415
+ const n = e.getAttribute("data-list-depth");
1416
+ if (n === null)
1417
+ return;
1418
+ const r = parseInt(n, 10);
1419
+ return r > 0 ? { left: r * u.INDENT_PER_LEVEL } : void 0;
1420
+ }
1421
+ static get toolbox() {
1422
+ return [
1423
+ {
1424
+ icon: G,
1425
+ title: "Bulleted list",
1426
+ titleKey: "bulletedList",
1427
+ data: { style: "unordered" },
1428
+ name: "bulleted-list",
1429
+ searchTerms: ["ul", "bullet", "unordered", "list"],
1430
+ shortcut: "-"
1431
+ },
1432
+ {
1433
+ icon: j,
1434
+ title: "Numbered list",
1435
+ titleKey: "numberedList",
1436
+ data: { style: "ordered" },
1437
+ name: "numbered-list",
1438
+ searchTerms: ["ol", "ordered", "number", "list"],
1439
+ shortcut: "1."
1440
+ },
1441
+ {
1442
+ icon: $,
1443
+ title: "To-do list",
1444
+ titleKey: "todoList",
1445
+ data: { style: "checklist" },
1446
+ name: "check-list",
1447
+ searchTerms: ["checkbox", "task", "todo", "check", "list"],
1448
+ shortcut: "[]"
1449
+ }
1450
+ ];
1451
+ }
1452
+ };
1453
+ u.BASE_STYLES = "outline-none", u.ITEM_STYLES = "outline-none py-0.5 pl-0.5 leading-[1.6em]", u.CHECKLIST_ITEM_STYLES = "flex items-start py-0.5 pl-0.5", u.CHECKBOX_STYLES = "mt-1 w-4 mr-2 h-4 cursor-pointer accent-current", u.STYLE_CONFIGS = [
1454
+ { style: "unordered", name: "bulletedList", icon: G },
1455
+ { style: "ordered", name: "numberedList", icon: j },
1456
+ { style: "checklist", name: "todoList", icon: $ }
1457
+ ], u.pendingMarkerUpdate = !1, u.PLACEHOLDER_KEY = "tools.list.placeholder", u.INDENT_PER_LEVEL = 24, u.TOOL_NAME = "list";
1458
+ let J = u;
1459
+ const i = class i {
1460
+ /**
1461
+ * Sanitizer Rule
1462
+ * Leave <strong> tags
1463
+ * @returns {object}
1464
+ */
1465
+ static get sanitize() {
1466
+ return {
1467
+ strong: {},
1468
+ b: {}
1469
+ };
1470
+ }
1471
+ /**
1472
+ * Normalize any remaining legacy <b> tags within the blok wrapper
1473
+ */
1474
+ static normalizeAllBoldTags() {
1475
+ if (typeof document == "undefined")
1476
+ return;
1477
+ const t = `${A(m.interface)} b, ${A(m.editor)} b`;
1478
+ document.querySelectorAll(t).forEach((e) => {
1479
+ i.ensureStrongElement(e);
1480
+ });
1481
+ }
1482
+ /**
1483
+ * Normalize bold tags within a mutated node if it belongs to the blok
1484
+ * @param node - The node affected by mutation
1485
+ */
1486
+ static normalizeBoldInNode(t) {
1487
+ var r;
1488
+ const e = t.nodeType === Node.ELEMENT_NODE ? t : t.parentElement;
1489
+ !e || typeof e.closest != "function" || !e.closest(`${A(m.interface)}, ${A(m.editor)}`) || (e.tagName === "B" && i.ensureStrongElement(e), (r = e.querySelectorAll) == null || r.call(e, "b").forEach((s) => {
1490
+ i.ensureStrongElement(s);
1491
+ }));
1492
+ }
1493
+ /**
1494
+ *
1495
+ */
1496
+ constructor() {
1497
+ typeof document != "undefined" && (i.instances.add(this), i.initializeGlobalListeners());
1498
+ }
1499
+ /**
1500
+ * Ensure global event listeners are registered once per document
1501
+ */
1502
+ static initializeGlobalListeners() {
1503
+ return typeof document == "undefined" ? !1 : (i.shortcutListenerRegistered || (document.addEventListener("keydown", i.handleShortcut, !0), i.shortcutListenerRegistered = !0), i.selectionListenerRegistered || (document.addEventListener("selectionchange", i.handleGlobalSelectionChange, !0), i.selectionListenerRegistered = !0), i.inputListenerRegistered || (document.addEventListener("input", i.handleGlobalInput, !0), i.inputListenerRegistered = !0), i.beforeInputListenerRegistered || (document.addEventListener("beforeinput", i.handleBeforeInput, !0), i.beforeInputListenerRegistered = !0), i.ensureMutationObserver(), !0);
1504
+ }
1505
+ /**
1506
+ * Ensure that text typed after exiting a collapsed bold selection stays outside of the bold element
1507
+ */
1508
+ static maintainCollapsedExitState() {
1509
+ var t, e, n, r, s;
1510
+ if (typeof document != "undefined")
1511
+ for (const o of Array.from(i.collapsedExitRecords)) {
1512
+ const a = i.resolveBoundary(o);
1513
+ if (!a) {
1514
+ i.collapsedExitRecords.delete(o);
1515
+ continue;
1516
+ }
1517
+ o.boundary = a.boundary, o.boldElement = a.boldElement;
1518
+ const l = a.boundary, d = a.boldElement, c = o.allowedLength, h = (t = d.textContent) != null ? t : "";
1519
+ if (h.length > c) {
1520
+ const b = h.slice(0, c), S = h.slice(c);
1521
+ d.textContent = b, l.textContent = ((e = l.textContent) != null ? e : "") + S;
1522
+ }
1523
+ const p = (n = l.textContent) != null ? n : "";
1524
+ p.length > 1 && p.startsWith("​") && (l.textContent = p.slice(1));
1525
+ const g = window.getSelection();
1526
+ i.ensureCaretAtBoundary(g, l), i.scheduleBoundaryCaretAdjustment(l);
1527
+ const y = (r = l.textContent) != null ? r : "", E = y.replace(/\u200B/g, ""), C = E.match(/^\s+/), T = /\S/.test(E), N = y.startsWith("​");
1528
+ C && (o.hasLeadingSpace = !0, o.leadingWhitespace = C[0]), T && (o.hasTypedContent = !0);
1529
+ const B = /^\s/.test(E), v = o.hasTypedContent && !N && ((s = d.textContent) != null ? s : "").length <= c, R = o.hasLeadingSpace && o.hasTypedContent && !B;
1530
+ if (v && R) {
1531
+ const b = y.replace(/^[\u200B\s]+/, ""), S = o.leadingWhitespace || " ";
1532
+ l.textContent = `${S}${b}`, i.ensureCaretAtBoundary(g, l);
1533
+ }
1534
+ v && i.collapsedExitRecords.delete(o);
1535
+ }
1536
+ }
1537
+ /**
1538
+ * Ensure the caret remains at the end of the boundary text node when exiting bold
1539
+ * @param selection - Current document selection
1540
+ * @param boundary - Text node following the bold element
1541
+ */
1542
+ static ensureCaretAtBoundary(t, e) {
1543
+ !t || !t.isCollapsed || i.setCaretToBoundaryEnd(t, e);
1544
+ }
1545
+ /**
1546
+ * Ensure the caret remains at the end of the boundary text node after the current microtask queue is flushed
1547
+ * @param boundary - Boundary text node that should keep the caret at its end
1548
+ */
1549
+ static scheduleBoundaryCaretAdjustment(t) {
1550
+ i.pendingBoundaryCaretAdjustments.has(t) || (i.pendingBoundaryCaretAdjustments.add(t), setTimeout(() => {
1551
+ var s, o, a;
1552
+ i.pendingBoundaryCaretAdjustments.delete(t);
1553
+ const e = (s = t.ownerDocument) != null ? s : typeof document != "undefined" ? document : null;
1554
+ if (!e)
1555
+ return;
1556
+ const n = e.getSelection();
1557
+ if (!n || !n.isCollapsed || n.anchorNode !== t)
1558
+ return;
1559
+ const r = (a = (o = t.textContent) == null ? void 0 : o.length) != null ? a : 0;
1560
+ n.anchorOffset !== r && i.setCaret(n, t, r);
1561
+ }, 0));
1562
+ }
1563
+ /**
1564
+ * Ensure there is a text node immediately following the provided bold element.
1565
+ * Creates one when necessary.
1566
+ * @param boldElement - Bold element that precedes the boundary
1567
+ * @returns The text node following the bold element or null if it cannot be created
1568
+ */
1569
+ static ensureTextNodeAfter(t) {
1570
+ var o;
1571
+ const e = t.nextSibling;
1572
+ if ((e == null ? void 0 : e.nodeType) === Node.TEXT_NODE)
1573
+ return e;
1574
+ const n = t.parentNode;
1575
+ if (!n)
1576
+ return null;
1577
+ const r = (o = t.ownerDocument) != null ? o : typeof document != "undefined" ? document : null;
1578
+ if (!r)
1579
+ return null;
1580
+ const s = r.createTextNode("");
1581
+ return n.insertBefore(s, e), s;
1582
+ }
1583
+ /**
1584
+ * Resolve the boundary text node tracked for a collapsed exit record.
1585
+ * @param record - Collapsed exit tracking record
1586
+ * @returns The aligned boundary text node or null when it cannot be determined
1587
+ */
1588
+ static resolveBoundary(t) {
1589
+ if (!t.boldElement.isConnected)
1590
+ return null;
1591
+ const e = i.ensureStrongElement(t.boldElement), n = t.boundary, s = n.isConnected && n.previousSibling === e ? n : i.ensureTextNodeAfter(e);
1592
+ return s ? {
1593
+ boundary: s,
1594
+ boldElement: e
1595
+ } : null;
1596
+ }
1597
+ /**
1598
+ * Move caret to the end of the provided boundary text node
1599
+ * @param selection - Current selection to update
1600
+ * @param boundary - Boundary text node that hosts the caret
1601
+ */
1602
+ static setCaretToBoundaryEnd(t, e) {
1603
+ var s, o;
1604
+ const n = document.createRange(), r = (o = (s = e.textContent) == null ? void 0 : s.length) != null ? o : 0;
1605
+ n.setStart(e, r), n.collapse(!0), t.removeAllRanges(), t.addRange(n);
1606
+ }
1607
+ /**
1608
+ * Recursively check if a node or any of its parents is a bold tag (<strong>)
1609
+ * @param node - The node to check
1610
+ */
1611
+ static hasBoldParent(t) {
1612
+ return t ? t.nodeType === Node.ELEMENT_NODE && i.isBoldTag(t) ? !0 : i.hasBoldParent(t.parentNode) : !1;
1613
+ }
1614
+ /**
1615
+ * Recursively find a bold element (<strong>) in the parent chain
1616
+ * @param node - The node to start searching from
1617
+ */
1618
+ static findBoldElement(t) {
1619
+ return t ? t.nodeType === Node.ELEMENT_NODE && i.isBoldTag(t) ? i.ensureStrongElement(t) : i.findBoldElement(t.parentNode) : null;
1620
+ }
1621
+ /**
1622
+ * Check if an element is a bold tag (<strong> for conversion)
1623
+ * @param node - The element to check
1624
+ */
1625
+ static isBoldTag(t) {
1626
+ const e = t.tagName;
1627
+ return e === "B" || e === "STRONG";
1628
+ }
1629
+ /**
1630
+ * Ensure an element is a <strong> tag, converting from <b> if needed
1631
+ * @param element - The element to ensure is a strong tag
1632
+ */
1633
+ static ensureStrongElement(t) {
1634
+ if (t.tagName === "STRONG")
1635
+ return t;
1636
+ const e = document.createElement("strong");
1637
+ for (t.hasAttributes() && Array.from(t.attributes).forEach((n) => {
1638
+ e.setAttribute(n.name, n.value);
1639
+ }); t.firstChild; )
1640
+ e.appendChild(t.firstChild);
1641
+ return t.replaceWith(e), e;
1642
+ }
1643
+ /**
1644
+ * Merge two strong elements by moving children from right to left
1645
+ * @param left - The left strong element to merge into
1646
+ * @param right - The right strong element to merge from
1647
+ */
1648
+ static mergeStrongNodes(t, e) {
1649
+ const n = i.ensureStrongElement(t), r = i.ensureStrongElement(e);
1650
+ for (; r.firstChild; )
1651
+ n.appendChild(r.firstChild);
1652
+ return r.remove(), n;
1653
+ }
1654
+ /**
1655
+ * Create button for Inline Toolbar
1656
+ */
1657
+ render() {
1658
+ return {
1659
+ icon: gt,
1660
+ name: "bold",
1661
+ onActivate: () => {
1662
+ this.toggleBold();
1663
+ },
1664
+ isActive: () => {
1665
+ const t = window.getSelection();
1666
+ return t ? this.isSelectionVisuallyBold(t) : !1;
1667
+ }
1668
+ };
1669
+ }
1670
+ /**
1671
+ * Apply or remove bold formatting using modern Selection API
1672
+ */
1673
+ toggleBold() {
1674
+ const t = window.getSelection();
1675
+ if (!t || t.rangeCount === 0)
1676
+ return;
1677
+ const e = t.getRangeAt(0);
1678
+ if (e.collapsed) {
1679
+ this.toggleCollapsedSelection();
1680
+ return;
1681
+ }
1682
+ this.isRangeBold(e, { ignoreWhitespace: !0 }) ? this.unwrapBoldTags(e) : this.wrapWithBold(e);
1683
+ }
1684
+ /**
1685
+ * Check if current selection is within a bold tag (<strong>)
1686
+ * @param selection - The Selection object to check
1687
+ */
1688
+ isSelectionVisuallyBold(t) {
1689
+ if (!t || t.rangeCount === 0)
1690
+ return !1;
1691
+ const e = t.getRangeAt(0);
1692
+ return this.isRangeBold(e, { ignoreWhitespace: !0 });
1693
+ }
1694
+ /**
1695
+ * Wrap selection with <strong> tag
1696
+ * @param range - The Range object containing the selection to wrap
1697
+ */
1698
+ wrapWithBold(t) {
1699
+ const e = this.getRangeHtmlWithoutBold(t), n = this.replaceRangeWithHtml(t, `<strong>${e}</strong>`), r = window.getSelection();
1700
+ r && n && (r.removeAllRanges(), r.addRange(n)), i.normalizeAllBoldTags();
1701
+ const s = this.findBoldElementFromRangeOrSelection(n, r);
1702
+ if (!s) {
1703
+ this.notifySelectionChange();
1704
+ return;
1705
+ }
1706
+ const o = this.mergeAdjacentBold(s);
1707
+ this.normalizeWhitespaceAround(o), this.selectElementContents(o), i.normalizeBoldTagsWithinBlok(window.getSelection()), i.replaceNbspInBlock(window.getSelection()), this.notifySelectionChange();
1708
+ }
1709
+ /**
1710
+ * Remove bold tags (<strong>) while preserving content
1711
+ * @param range - The Range object containing the selection to unwrap
1712
+ */
1713
+ unwrapBoldTags(t) {
1714
+ const e = this.collectBoldAncestors(t), n = window.getSelection();
1715
+ if (!n)
1716
+ return;
1717
+ const r = document.createElement("span"), s = t.extractContents();
1718
+ r.setAttribute("data-blok-bold-marker", `unwrap-${i.markerSequence++}`), r.appendChild(s), this.removeNestedBold(r), t.insertNode(r);
1719
+ const o = document.createRange();
1720
+ for (o.selectNodeContents(r), n.removeAllRanges(), n.addRange(o); ; ) {
1721
+ const c = i.findBoldElement(r);
1722
+ if (!c)
1723
+ break;
1724
+ this.moveMarkerOutOfBold(r, c);
1725
+ }
1726
+ const a = r.firstChild, l = r.lastChild;
1727
+ this.unwrapElement(r);
1728
+ const d = a && l ? (() => {
1729
+ const c = document.createRange();
1730
+ return c.setStartBefore(a), c.setEndAfter(l), n.removeAllRanges(), n.addRange(c), c;
1731
+ })() : void 0;
1732
+ d || n.removeAllRanges(), this.replaceNbspWithinRange(d), i.normalizeBoldTagsWithinBlok(n), i.replaceNbspInBlock(n), i.removeEmptyBoldElements(n), e.forEach((c) => {
1733
+ i.isElementEmpty(c) && c.remove();
1734
+ }), this.notifySelectionChange();
1735
+ }
1736
+ /**
1737
+ * Replace the current range contents with provided HTML snippet
1738
+ * @param range - Range to replace
1739
+ * @param html - HTML string to insert
1740
+ * @returns range spanning inserted content
1741
+ */
1742
+ replaceRangeWithHtml(t, e) {
1743
+ var a, l;
1744
+ const n = i.createFragmentFromHtml(e), r = (a = n.firstChild) != null ? a : null, s = (l = n.lastChild) != null ? l : null;
1745
+ if (t.deleteContents(), !r || !s)
1746
+ return;
1747
+ t.insertNode(n);
1748
+ const o = document.createRange();
1749
+ return o.setStartBefore(r), o.setEndAfter(s), o;
1750
+ }
1751
+ /**
1752
+ * Move a temporary marker element outside of a bold ancestor while preserving content order
1753
+ * @param marker - Marker element wrapping the selection contents
1754
+ * @param boldElement - Bold ancestor containing the marker
1755
+ */
1756
+ moveMarkerOutOfBold(t, e) {
1757
+ const n = e.parentNode;
1758
+ if (!n)
1759
+ return;
1760
+ if (Array.from(e.childNodes).forEach((l) => {
1761
+ var d;
1762
+ l.nodeType === Node.TEXT_NODE && ((d = l.textContent) != null ? d : "").length === 0 && l.remove();
1763
+ }), e.childNodes.length === 1 && e.firstChild === t) {
1764
+ e.replaceWith(t);
1765
+ return;
1766
+ }
1767
+ if (e.firstChild === t) {
1768
+ n.insertBefore(t, e);
1769
+ return;
1770
+ }
1771
+ if (e.lastChild === t) {
1772
+ n.insertBefore(t, e.nextSibling);
1773
+ return;
1774
+ }
1775
+ const a = e.cloneNode(!1);
1776
+ for (; t.nextSibling; )
1777
+ a.appendChild(t.nextSibling);
1778
+ n.insertBefore(a, e.nextSibling), n.insertBefore(t, a);
1779
+ }
1780
+ /**
1781
+ * Select all contents of an element
1782
+ * @param element - The element whose contents should be selected
1783
+ */
1784
+ selectElementContents(t) {
1785
+ const e = window.getSelection();
1786
+ if (!e)
1787
+ return;
1788
+ const n = document.createRange();
1789
+ n.selectNodeContents(t), e.removeAllRanges(), e.addRange(n);
1790
+ }
1791
+ /**
1792
+ * Check if a range contains bold text
1793
+ * @param range - The range to check
1794
+ * @param options - Options for checking bold status
1795
+ * @param options.ignoreWhitespace - Whether to ignore whitespace-only text nodes
1796
+ */
1797
+ isRangeBold(t, e) {
1798
+ var s;
1799
+ if (t.collapsed)
1800
+ return !!i.findBoldElement(t.startContainer);
1801
+ const n = document.createTreeWalker(
1802
+ t.commonAncestorContainer,
1803
+ NodeFilter.SHOW_TEXT,
1804
+ {
1805
+ acceptNode: (o) => {
1806
+ try {
1807
+ return t.intersectsNode(o) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
1808
+ } catch (a) {
1809
+ const l = document.createRange();
1810
+ l.selectNodeContents(o);
1811
+ const d = t.compareBoundaryPoints(Range.END_TO_START, l) > 0, c = t.compareBoundaryPoints(Range.START_TO_END, l) < 0;
1812
+ return d && c ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
1813
+ }
1814
+ }
1815
+ }
1816
+ ), r = [];
1817
+ for (; n.nextNode(); ) {
1818
+ const o = n.currentNode, a = (s = o.textContent) != null ? s : "";
1819
+ e.ignoreWhitespace && a.trim().length === 0 || a.length !== 0 && r.push(o);
1820
+ }
1821
+ return r.length === 0 ? !!i.findBoldElement(t.startContainer) : r.every((o) => i.hasBoldParent(o));
1822
+ }
1823
+ /**
1824
+ * Remove nested bold tags from a root node
1825
+ * @param root - The root node to process
1826
+ */
1827
+ removeNestedBold(t) {
1828
+ var n;
1829
+ const e = (n = t.querySelectorAll) == null ? void 0 : n.call(t, "b,strong");
1830
+ e && e.forEach((r) => {
1831
+ this.unwrapElement(r);
1832
+ });
1833
+ }
1834
+ /**
1835
+ * Unwrap an element by moving its children to the parent
1836
+ * @param element - The element to unwrap
1837
+ */
1838
+ unwrapElement(t) {
1839
+ const e = t.parentNode;
1840
+ if (!e) {
1841
+ t.remove();
1842
+ return;
1843
+ }
1844
+ for (; t.firstChild; )
1845
+ e.insertBefore(t.firstChild, t);
1846
+ e.removeChild(t);
1847
+ }
1848
+ /**
1849
+ * Find bold element from an inserted range or fall back to selection
1850
+ * @param insertedRange - Range spanning inserted content
1851
+ * @param selection - Current selection as fallback
1852
+ */
1853
+ findBoldElementFromRangeOrSelection(t, e) {
1854
+ if (!t)
1855
+ return e ? i.findBoldElement(e.focusNode) : null;
1856
+ const n = i.findBoldElement(t.startContainer);
1857
+ if (n)
1858
+ return n;
1859
+ const r = i.findBoldElement(t.commonAncestorContainer);
1860
+ return r || (t.startContainer.nodeType === Node.ELEMENT_NODE && i.isBoldTag(t.startContainer) ? t.startContainer : null);
1861
+ }
1862
+ /**
1863
+ * Merge adjacent bold elements into a single element
1864
+ * @param element - The bold element to merge with adjacent elements
1865
+ */
1866
+ mergeAdjacentBold(t) {
1867
+ const e = i.ensureStrongElement(t), n = e.previousSibling, r = n && n.nodeType === Node.ELEMENT_NODE && i.isBoldTag(n) ? i.mergeStrongNodes(n, e) : e, s = r.nextSibling;
1868
+ return s && s.nodeType === Node.ELEMENT_NODE && i.isBoldTag(s) ? i.mergeStrongNodes(r, s) : r;
1869
+ }
1870
+ /**
1871
+ * Toggle bold formatting for a collapsed selection (caret position)
1872
+ * Exits bold if caret is inside a bold element, otherwise starts a new bold element
1873
+ */
1874
+ toggleCollapsedSelection() {
1875
+ const t = window.getSelection();
1876
+ if (!t || t.rangeCount === 0)
1877
+ return;
1878
+ const e = t.getRangeAt(0), n = i.findBoldElement(e.startContainer), r = (() => {
1879
+ if (n && n.getAttribute(i.DATA_ATTR_COLLAPSED_ACTIVE) !== "true")
1880
+ return i.exitCollapsedBold(t, n);
1881
+ const s = n != null ? n : i.getBoundaryBold(e);
1882
+ return s ? i.exitCollapsedBold(t, s) : this.startCollapsedBold(e);
1883
+ })();
1884
+ document.dispatchEvent(new Event("selectionchange")), r && (t.removeAllRanges(), t.addRange(r)), i.normalizeBoldTagsWithinBlok(t), i.replaceNbspInBlock(t), i.removeEmptyBoldElements(t), this.notifySelectionChange();
1885
+ }
1886
+ /**
1887
+ * Insert a bold wrapper at the caret so newly typed text becomes bold
1888
+ * @param range - Current collapsed range
1889
+ */
1890
+ startCollapsedBold(t) {
1891
+ if (!t.collapsed)
1892
+ return;
1893
+ const e = document.createElement("strong"), n = document.createTextNode("");
1894
+ e.appendChild(n), e.setAttribute(i.DATA_ATTR_COLLAPSED_ACTIVE, "true");
1895
+ const r = t.startContainer, s = t.startOffset;
1896
+ if (!(r.nodeType === Node.TEXT_NODE ? this.insertCollapsedBoldIntoText(r, e, s) : r.nodeType === Node.ELEMENT_NODE ? (this.insertCollapsedBoldIntoElement(r, e, s), !0) : !1))
1897
+ return;
1898
+ const a = window.getSelection(), l = document.createRange();
1899
+ l.setStart(n, 0), l.collapse(!0);
1900
+ const d = this.mergeAdjacentBold(e);
1901
+ return i.normalizeBoldTagsWithinBlok(a), i.replaceNbspInBlock(a), i.removeEmptyBoldElements(a), a && (a.removeAllRanges(), a.addRange(l)), this.notifySelectionChange(), d.firstChild instanceof Text ? (() => {
1902
+ var h, p;
1903
+ const c = document.createRange();
1904
+ return c.setStart(d.firstChild, (p = (h = d.firstChild.textContent) == null ? void 0 : h.length) != null ? p : 0), c.collapse(!0), c;
1905
+ })() : l;
1906
+ }
1907
+ /**
1908
+ * Insert a collapsed bold wrapper when the caret resides inside a text node
1909
+ * @param text - Text node containing the caret
1910
+ * @param strong - Strong element to insert
1911
+ * @param offset - Caret offset within the text node
1912
+ * @returns true when insertion succeeded
1913
+ */
1914
+ insertCollapsedBoldIntoText(t, e, n) {
1915
+ var c;
1916
+ const r = t, s = r.parentNode;
1917
+ if (!s)
1918
+ return !1;
1919
+ const o = (c = r.textContent) != null ? c : "", a = o.slice(0, n), l = o.slice(n);
1920
+ r.textContent = a;
1921
+ const d = l.length ? document.createTextNode(l) : null;
1922
+ return d && s.insertBefore(d, r.nextSibling), s.insertBefore(e, d != null ? d : r.nextSibling), e.setAttribute(i.DATA_ATTR_PREV_LENGTH, a.length.toString()), !0;
1923
+ }
1924
+ /**
1925
+ * Insert a collapsed bold wrapper directly into an element container
1926
+ * @param element - Container element
1927
+ * @param strong - Strong element to insert
1928
+ * @param offset - Index at which to insert the strong element
1929
+ */
1930
+ insertCollapsedBoldIntoElement(t, e, n) {
1931
+ var s;
1932
+ const r = (s = t.childNodes[n]) != null ? s : null;
1933
+ t.insertBefore(e, r), e.setAttribute(i.DATA_ATTR_PREV_LENGTH, "0");
1934
+ }
1935
+ /**
1936
+ * Check if an element is empty (has no text content)
1937
+ * @param element - The element to check
1938
+ */
1939
+ static isElementEmpty(t) {
1940
+ var e;
1941
+ return ((e = t.textContent) != null ? e : "").length === 0;
1942
+ }
1943
+ /**
1944
+ *
1945
+ */
1946
+ notifySelectionChange() {
1947
+ i.enforceCollapsedBoldLengths(window.getSelection()), document.dispatchEvent(new Event("selectionchange")), this.updateToolbarButtonState();
1948
+ }
1949
+ /**
1950
+ * Ensure inline toolbar button reflects the actual bold state after programmatic toggles
1951
+ */
1952
+ updateToolbarButtonState() {
1953
+ const t = window.getSelection();
1954
+ if (!t)
1955
+ return;
1956
+ const e = t.anchorNode, n = (e == null ? void 0 : e.nodeType) === Node.ELEMENT_NODE ? e : e == null ? void 0 : e.parentElement, r = n == null ? void 0 : n.closest(A(m.editor));
1957
+ if (!r)
1958
+ return;
1959
+ const s = r.querySelector("[data-blok-testid=inline-toolbar]");
1960
+ if (!(s instanceof HTMLElement))
1961
+ return;
1962
+ const o = s.querySelector('[data-blok-item-name="bold"]');
1963
+ if (!(o instanceof HTMLElement))
1964
+ return;
1965
+ this.isSelectionVisuallyBold(t) ? o.setAttribute("data-blok-popover-item-active", "true") : o.removeAttribute("data-blok-popover-item-active");
1966
+ }
1967
+ /**
1968
+ * Normalize whitespace around a bold element
1969
+ * @param element - The bold element to normalize whitespace around
1970
+ */
1971
+ normalizeWhitespaceAround(t) {
1972
+ i.replaceNbspWithSpace(t.previousSibling), i.replaceNbspWithSpace(t.nextSibling);
1973
+ }
1974
+ /**
1975
+ * Replace non-breaking spaces with regular spaces in a text node
1976
+ * @param node - The text node to process
1977
+ */
1978
+ static replaceNbspWithSpace(t) {
1979
+ var r;
1980
+ if (!t || t.nodeType !== Node.TEXT_NODE)
1981
+ return;
1982
+ const e = t, n = (r = e.textContent) != null ? r : "";
1983
+ n.includes(" ") && (e.textContent = n.replace(/\u00A0/g, " "));
1984
+ }
1985
+ /**
1986
+ * Restore a selection range from marker elements
1987
+ * @param markerId - The ID of the markers used to mark the selection
1988
+ */
1989
+ restoreSelectionFromMarkers(t) {
1990
+ const e = document.querySelector(`[data-blok-bold-marker="${t}-start"]`), n = document.querySelector(`[data-blok-bold-marker="${t}-end"]`);
1991
+ if (!e || !n) {
1992
+ e == null || e.remove(), n == null || n.remove();
1993
+ return;
1994
+ }
1995
+ const r = window.getSelection();
1996
+ if (!r) {
1997
+ e.remove(), n.remove();
1998
+ return;
1999
+ }
2000
+ const s = document.createRange();
2001
+ return s.setStartAfter(e), s.setEndBefore(n), r.removeAllRanges(), r.addRange(s), e.remove(), n.remove(), s;
2002
+ }
2003
+ /**
2004
+ * Replace non-breaking spaces with regular spaces within a range
2005
+ * @param range - The range to process
2006
+ */
2007
+ replaceNbspWithinRange(t) {
2008
+ if (!t)
2009
+ return;
2010
+ const e = document.createTreeWalker(
2011
+ t.commonAncestorContainer,
2012
+ NodeFilter.SHOW_TEXT,
2013
+ {
2014
+ acceptNode: (n) => {
2015
+ try {
2016
+ return t.intersectsNode(n) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2017
+ } catch (r) {
2018
+ const s = document.createRange();
2019
+ s.selectNodeContents(n);
2020
+ const o = t.compareBoundaryPoints(Range.END_TO_START, s) > 0, a = t.compareBoundaryPoints(Range.START_TO_END, s) < 0;
2021
+ return o && a ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2022
+ }
2023
+ }
2024
+ }
2025
+ );
2026
+ for (; e.nextNode(); )
2027
+ i.replaceNbspWithSpace(e.currentNode);
2028
+ }
2029
+ /**
2030
+ * Normalize all bold tags within the blok to <strong> tags
2031
+ * Converts any legacy <b> tags to <strong> tags
2032
+ * @param selection - The current selection to determine the blok context
2033
+ */
2034
+ static normalizeBoldTagsWithinBlok(t) {
2035
+ var s;
2036
+ const e = (s = t == null ? void 0 : t.anchorNode) != null ? s : t == null ? void 0 : t.focusNode;
2037
+ if (!e)
2038
+ return;
2039
+ const n = e.nodeType === Node.ELEMENT_NODE ? e : e.parentElement, r = n == null ? void 0 : n.closest(A(m.editor));
2040
+ r && r.querySelectorAll("b").forEach((o) => {
2041
+ i.ensureStrongElement(o);
2042
+ });
2043
+ }
2044
+ /**
2045
+ * Replace non-breaking spaces with regular spaces in the block containing the selection
2046
+ * @param selection - The current selection to determine the block context
2047
+ */
2048
+ static replaceNbspInBlock(t) {
2049
+ var o;
2050
+ const e = (o = t == null ? void 0 : t.anchorNode) != null ? o : t == null ? void 0 : t.focusNode;
2051
+ if (!e)
2052
+ return;
2053
+ const n = e.nodeType === Node.ELEMENT_NODE ? e : e.parentElement, r = n == null ? void 0 : n.closest('[data-blok-component="paragraph"]');
2054
+ if (!r)
2055
+ return;
2056
+ const s = document.createTreeWalker(r, NodeFilter.SHOW_TEXT);
2057
+ for (; s.nextNode(); )
2058
+ i.replaceNbspWithSpace(s.currentNode);
2059
+ r.querySelectorAll("b").forEach((a) => {
2060
+ i.ensureStrongElement(a);
2061
+ });
2062
+ }
2063
+ /**
2064
+ * Remove empty bold elements within the current block
2065
+ * @param selection - The current selection to determine the block context
2066
+ */
2067
+ static removeEmptyBoldElements(t) {
2068
+ var o, a;
2069
+ const e = (o = t == null ? void 0 : t.anchorNode) != null ? o : t == null ? void 0 : t.focusNode;
2070
+ if (!e)
2071
+ return;
2072
+ const n = e.nodeType === Node.ELEMENT_NODE ? e : e.parentElement, r = n == null ? void 0 : n.closest('[data-blok-component="paragraph"]');
2073
+ if (!r)
2074
+ return;
2075
+ const s = (a = t == null ? void 0 : t.focusNode) != null ? a : null;
2076
+ r.querySelectorAll("strong").forEach((l) => {
2077
+ var h;
2078
+ const d = l.getAttribute(i.DATA_ATTR_COLLAPSED_ACTIVE) === "true", c = l.hasAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
2079
+ d || c || ((h = l.textContent) != null ? h : "").length === 0 && !i.isNodeWithin(s, l) && l.remove();
2080
+ });
2081
+ }
2082
+ /**
2083
+ * Ensure collapsed bold placeholders absorb newly typed text
2084
+ * @param selection - The current selection to determine the blok context
2085
+ */
2086
+ static synchronizeCollapsedBold(t) {
2087
+ var o, a;
2088
+ const e = (o = t == null ? void 0 : t.anchorNode) != null ? o : t == null ? void 0 : t.focusNode, n = e && e.nodeType === Node.ELEMENT_NODE ? e : e == null ? void 0 : e.parentElement, r = (a = n == null ? void 0 : n.closest(A(m.editor))) != null ? a : n == null ? void 0 : n.ownerDocument;
2089
+ if (!r)
2090
+ return;
2091
+ const s = `strong[${i.DATA_ATTR_COLLAPSED_ACTIVE}="true"]`;
2092
+ r.querySelectorAll(s).forEach((l) => {
2093
+ var k, I, F, z, q;
2094
+ const d = l.getAttribute(i.DATA_ATTR_PREV_LENGTH), c = l.previousSibling;
2095
+ if (!d || !c || c.nodeType !== Node.TEXT_NODE)
2096
+ return;
2097
+ const h = Number(d);
2098
+ if (!Number.isFinite(h))
2099
+ return;
2100
+ const p = c, g = (k = p.textContent) != null ? k : "";
2101
+ if (g.length <= h)
2102
+ return;
2103
+ const y = g.slice(0, h), E = g.slice(h);
2104
+ p.textContent = y;
2105
+ const C = E.match(/^[\u00A0\s]+/);
2106
+ if (C && !l.hasAttribute(i.DATA_ATTR_LEADING_WHITESPACE) && l.setAttribute(i.DATA_ATTR_LEADING_WHITESPACE, C[0]), E.length === 0)
2107
+ return;
2108
+ const T = (I = l.textContent) != null ? I : "", N = T + E, B = (F = l.getAttribute(i.DATA_ATTR_LEADING_WHITESPACE)) != null ? F : "", R = B.length > 0 && T.length === 0 && !N.startsWith(B) ? B + N : N, b = document.createTextNode(R);
2109
+ for (; l.firstChild; )
2110
+ l.removeChild(l.firstChild);
2111
+ if (l.appendChild(b), !(t != null && t.isCollapsed) || !i.isNodeWithin(t.focusNode, p))
2112
+ return;
2113
+ const S = document.createRange(), _ = (q = (z = b.textContent) == null ? void 0 : z.length) != null ? q : 0;
2114
+ S.setStart(b, _), S.collapse(!0), t.removeAllRanges(), t.addRange(S);
2115
+ });
2116
+ }
2117
+ /**
2118
+ * Ensure caret is positioned after boundary bold elements when toggling collapsed selections
2119
+ * @param selection - Current selection
2120
+ */
2121
+ static moveCaretAfterBoundaryBold(t) {
2122
+ if (!t.rangeCount)
2123
+ return;
2124
+ const e = t.getRangeAt(0);
2125
+ if (!e.collapsed)
2126
+ return;
2127
+ const n = i.findBoldElement(e.startContainer);
2128
+ (n == null ? void 0 : n.getAttribute(i.DATA_ATTR_COLLAPSED_ACTIVE)) !== "true" && (i.moveCaretFromElementContainer(t, e) || i.moveCaretFromTextContainer(t, e));
2129
+ }
2130
+ /**
2131
+ * Locate a bold element adjacent to a collapsed range
2132
+ * @param range - Range to inspect
2133
+ */
2134
+ static getAdjacentBold(t) {
2135
+ const e = t.startContainer;
2136
+ return e.nodeType === Node.TEXT_NODE ? i.getBoldAdjacentToText(t, e) : e.nodeType === Node.ELEMENT_NODE ? i.getBoldAdjacentToElement(t, e) : null;
2137
+ }
2138
+ /**
2139
+ * Get bold element adjacent to a text node container
2140
+ * @param range - Current collapsed range
2141
+ * @param textNode - Text node hosting the caret
2142
+ */
2143
+ static getBoldAdjacentToText(t, e) {
2144
+ var o, a;
2145
+ const n = (a = (o = e.textContent) == null ? void 0 : o.length) != null ? a : 0, r = e.previousSibling;
2146
+ if (t.startOffset === 0 && i.isBoldElement(r))
2147
+ return r;
2148
+ if (t.startOffset !== n)
2149
+ return null;
2150
+ const s = e.nextSibling;
2151
+ return i.isBoldElement(s) ? s : null;
2152
+ }
2153
+ /**
2154
+ * Get bold element adjacent to an element container
2155
+ * @param range - Current collapsed range
2156
+ * @param element - Element containing the caret
2157
+ */
2158
+ static getBoldAdjacentToElement(t, e) {
2159
+ var s, o;
2160
+ const n = t.startOffset > 0 && (s = e.childNodes[t.startOffset - 1]) != null ? s : null;
2161
+ if (i.isBoldElement(n))
2162
+ return n;
2163
+ const r = (o = e.childNodes[t.startOffset]) != null ? o : null;
2164
+ return i.isBoldElement(r) ? r : null;
2165
+ }
2166
+ /**
2167
+ * Exit collapsed bold state when caret no longer resides within bold content
2168
+ * @param selection - Current selection
2169
+ * @param range - Collapsed range after toggling bold
2170
+ */
2171
+ static exitCollapsedIfNeeded(t, e) {
2172
+ var o;
2173
+ if (!!i.findBoldElement(e.startContainer))
2174
+ return;
2175
+ const r = (o = i.getBoundaryBold(e)) != null ? o : i.getAdjacentBold(e);
2176
+ if (!r)
2177
+ return;
2178
+ const s = i.exitCollapsedBold(t, r);
2179
+ s && (t.removeAllRanges(), t.addRange(s));
2180
+ }
2181
+ /**
2182
+ * Adjust caret when selection container is an element adjacent to bold content
2183
+ * @param selection - Current selection
2184
+ * @param range - Collapsed range to inspect
2185
+ * @returns true when caret position was updated
2186
+ */
2187
+ static moveCaretFromElementContainer(t, e) {
2188
+ if (e.startContainer.nodeType !== Node.ELEMENT_NODE)
2189
+ return !1;
2190
+ const n = e.startContainer;
2191
+ return i.moveCaretAfterPreviousBold(t, n, e.startOffset) ? !0 : i.moveCaretBeforeNextBold(t, n, e.startOffset);
2192
+ }
2193
+ /**
2194
+ * Move caret after the bold node that precedes the caret when possible
2195
+ * @param selection - Current selection
2196
+ * @param element - Container element
2197
+ * @param offset - Caret offset within the container
2198
+ */
2199
+ static moveCaretAfterPreviousBold(t, e, n) {
2200
+ var a, l, d;
2201
+ const r = n > 0 && (a = e.childNodes[n - 1]) != null ? a : null;
2202
+ if (!i.isBoldElement(r))
2203
+ return !1;
2204
+ const s = i.ensureFollowingTextNode(r, r.nextSibling);
2205
+ if (!s)
2206
+ return !1;
2207
+ const o = (d = (l = s.textContent) == null ? void 0 : l.length) != null ? d : 0;
2208
+ return i.setCaret(t, s, o), !0;
2209
+ }
2210
+ /**
2211
+ * Move caret before the bold node that follows the caret, ensuring there's a text node to receive input
2212
+ * @param selection - Current selection
2213
+ * @param element - Container element
2214
+ * @param offset - Caret offset within the container
2215
+ */
2216
+ static moveCaretBeforeNextBold(t, e, n) {
2217
+ var o;
2218
+ const r = (o = e.childNodes[n]) != null ? o : null;
2219
+ if (!i.isBoldElement(r))
2220
+ return !1;
2221
+ const s = i.ensureFollowingTextNode(r, r.nextSibling);
2222
+ return s ? (i.setCaret(t, s, 0), !0) : (i.setCaretAfterNode(t, r), !0);
2223
+ }
2224
+ /**
2225
+ * Adjust caret when selection container is a text node adjacent to bold content
2226
+ * @param selection - Current selection
2227
+ * @param range - Collapsed range to inspect
2228
+ */
2229
+ static moveCaretFromTextContainer(t, e) {
2230
+ var d, c, h;
2231
+ if (e.startContainer.nodeType !== Node.TEXT_NODE)
2232
+ return;
2233
+ const n = e.startContainer, r = n.previousSibling, s = (d = n.textContent) != null ? d : "", o = /^\s/.test(s);
2234
+ if (e.startOffset === 0 && i.isBoldElement(r) && (s.length === 0 || o)) {
2235
+ i.setCaret(t, n, s.length);
2236
+ return;
2237
+ }
2238
+ const a = i.findBoldElement(n);
2239
+ if (!a || e.startOffset !== ((h = (c = n.textContent) == null ? void 0 : c.length) != null ? h : 0))
2240
+ return;
2241
+ const l = i.ensureFollowingTextNode(a, a.nextSibling);
2242
+ if (l) {
2243
+ i.setCaret(t, l, 0);
2244
+ return;
2245
+ }
2246
+ i.setCaretAfterNode(t, a);
2247
+ }
2248
+ /**
2249
+ * Ensure caret is positioned at the end of a collapsed boundary text node before the browser processes a printable keydown
2250
+ * @param event - Keydown event fired before browser input handling
2251
+ */
2252
+ static guardCollapsedBoundaryKeydown(t) {
2253
+ var l;
2254
+ if (t.defaultPrevented || t.metaKey || t.ctrlKey || t.altKey || t.key.length !== 1)
2255
+ return;
2256
+ const n = window.getSelection();
2257
+ if (!n || !n.isCollapsed || n.rangeCount === 0)
2258
+ return;
2259
+ const r = n.getRangeAt(0);
2260
+ if (r.startContainer.nodeType !== Node.TEXT_NODE)
2261
+ return;
2262
+ const s = r.startContainer, o = (l = s.textContent) != null ? l : "";
2263
+ if (o.length === 0 || r.startOffset !== 0)
2264
+ return;
2265
+ const a = s.previousSibling;
2266
+ i.isBoldElement(a) && /^\s/.test(o) && i.setCaret(n, s, o.length);
2267
+ }
2268
+ /**
2269
+ * Determine whether a node is a bold element (<strong>/<b>)
2270
+ * @param node - Node to inspect
2271
+ */
2272
+ static isBoldElement(t) {
2273
+ return !!(t && t.nodeType === Node.ELEMENT_NODE && i.isBoldTag(t));
2274
+ }
2275
+ /**
2276
+ * Place caret at the provided offset within a text node
2277
+ * @param selection - Current selection
2278
+ * @param node - Target text node
2279
+ * @param offset - Offset within the text node
2280
+ */
2281
+ static setCaret(t, e, n) {
2282
+ const r = document.createRange();
2283
+ r.setStart(e, n), r.collapse(!0), t.removeAllRanges(), t.addRange(r);
2284
+ }
2285
+ /**
2286
+ * Position caret immediately after the provided node
2287
+ * @param selection - Current selection
2288
+ * @param node - Reference node
2289
+ */
2290
+ static setCaretAfterNode(t, e) {
2291
+ if (!e)
2292
+ return;
2293
+ const n = document.createRange();
2294
+ n.setStartAfter(e), n.collapse(!0), t.removeAllRanges(), t.addRange(n);
2295
+ }
2296
+ /**
2297
+ * Ensure there is a text node immediately following a bold element to accept new input
2298
+ * @param boldElement - Bold element after which text should be inserted
2299
+ * @param referenceNode - Node that currently follows the bold element
2300
+ */
2301
+ static ensureFollowingTextNode(t, e) {
2302
+ const n = t.parentNode;
2303
+ if (!n)
2304
+ return null;
2305
+ if (e && e.nodeType === Node.TEXT_NODE)
2306
+ return e;
2307
+ const r = document.createTextNode("");
2308
+ return n.insertBefore(r, e), r;
2309
+ }
2310
+ /**
2311
+ * Enforce length limits on collapsed bold elements
2312
+ * @param selection - The current selection to determine the blok context
2313
+ */
2314
+ static enforceCollapsedBoldLengths(t) {
2315
+ var o;
2316
+ const e = (o = t == null ? void 0 : t.anchorNode) != null ? o : t == null ? void 0 : t.focusNode;
2317
+ if (!e)
2318
+ return;
2319
+ const n = e.nodeType === Node.ELEMENT_NODE ? e : e.parentElement, r = n == null ? void 0 : n.closest(A(m.editor));
2320
+ if (!r)
2321
+ return;
2322
+ r.querySelectorAll(`strong[${i.DATA_ATTR_COLLAPSED_LENGTH}]`).forEach((a) => {
2323
+ var v, R, b, S, _;
2324
+ const l = a, d = l.getAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
2325
+ if (!d)
2326
+ return;
2327
+ const c = Number(d), h = (v = l.textContent) != null ? v : "";
2328
+ if (!Number.isFinite(c))
2329
+ return;
2330
+ const p = h.length > c, g = p ? i.splitCollapsedBoldText(l, c, h) : null, y = l.getAttribute(i.DATA_ATTR_PREV_LENGTH), E = y ? Number(y) : NaN, C = l.previousSibling, T = (C == null ? void 0 : C.nodeType) === Node.TEXT_NODE ? C : null, N = (R = T == null ? void 0 : T.textContent) != null ? R : "", B = !!(y && Number.isFinite(E) && T && N.length > E);
2331
+ if (B && T) {
2332
+ const k = N.slice(0, E), I = N.slice(E);
2333
+ T.textContent = k;
2334
+ const F = document.createTextNode(I);
2335
+ (b = l.parentNode) == null || b.insertBefore(F, l.nextSibling);
2336
+ }
2337
+ if (B && l.removeAttribute(i.DATA_ATTR_PREV_LENGTH), t != null && t.isCollapsed && g && i.isNodeWithin(t.focusNode, l)) {
2338
+ const k = document.createRange(), I = (_ = (S = g.textContent) == null ? void 0 : S.length) != null ? _ : 0;
2339
+ k.setStart(g, I), k.collapse(!0), t.removeAllRanges(), t.addRange(k);
2340
+ }
2341
+ p && l.removeAttribute(i.DATA_ATTR_COLLAPSED_LENGTH);
2342
+ });
2343
+ }
2344
+ /**
2345
+ * Split text content exceeding the allowed collapsed bold length and move the excess outside
2346
+ * @param boldEl - Bold element hosting the collapsed selection
2347
+ * @param allowedLength - Maximum allowed length for the collapsed bold
2348
+ * @param currentText - Current text content inside the bold element
2349
+ */
2350
+ static splitCollapsedBoldText(t, e, n) {
2351
+ const r = t, s = r.parentNode;
2352
+ if (!s)
2353
+ return null;
2354
+ const o = n.slice(0, e), a = n.slice(e);
2355
+ r.textContent = o;
2356
+ const l = document.createTextNode(a);
2357
+ return s.insertBefore(l, r.nextSibling), l;
2358
+ }
2359
+ /**
2360
+ * Check if a node is within the provided container
2361
+ * @param target - Node to test
2362
+ * @param container - Potential ancestor container
2363
+ */
2364
+ static isNodeWithin(t, e) {
2365
+ return t ? t === e || e.contains(t) : !1;
2366
+ }
2367
+ /**
2368
+ *
2369
+ */
2370
+ static handleGlobalSelectionChange() {
2371
+ i.refreshSelectionState("selectionchange");
2372
+ }
2373
+ /**
2374
+ *
2375
+ */
2376
+ static handleGlobalInput() {
2377
+ i.refreshSelectionState("input");
2378
+ }
2379
+ /**
2380
+ * Normalize selection state after blok input or selection updates
2381
+ * @param source - The event source triggering the refresh
2382
+ */
2383
+ static refreshSelectionState(t) {
2384
+ const e = window.getSelection();
2385
+ i.enforceCollapsedBoldLengths(e), i.maintainCollapsedExitState(), i.synchronizeCollapsedBold(e), i.normalizeBoldTagsWithinBlok(e), i.removeEmptyBoldElements(e), t === "input" && e && i.moveCaretAfterBoundaryBold(e), i.normalizeAllBoldTags();
2386
+ }
2387
+ /**
2388
+ * Ensure mutation observer is registered to convert legacy <b> tags
2389
+ */
2390
+ static ensureMutationObserver() {
2391
+ if (typeof MutationObserver == "undefined" || i.mutationObserver)
2392
+ return;
2393
+ const t = new MutationObserver((e) => {
2394
+ if (!i.isProcessingMutation) {
2395
+ i.isProcessingMutation = !0;
2396
+ try {
2397
+ e.forEach((n) => {
2398
+ n.addedNodes.forEach((r) => {
2399
+ i.normalizeBoldInNode(r);
2400
+ }), n.type === "characterData" && n.target && i.normalizeBoldInNode(n.target);
2401
+ });
2402
+ } finally {
2403
+ i.isProcessingMutation = !1;
2404
+ }
2405
+ }
2406
+ });
2407
+ t.observe(document.body, {
2408
+ subtree: !0,
2409
+ childList: !0,
2410
+ characterData: !0
2411
+ }), i.mutationObserver = t;
2412
+ }
2413
+ /**
2414
+ * Prevent the browser's native bold command to avoid <b> wrappers
2415
+ * @param event - BeforeInput event fired by the browser
2416
+ */
2417
+ static handleBeforeInput(t) {
2418
+ if (t.inputType !== "formatBold")
2419
+ return;
2420
+ const e = window.getSelection(), n = !!(e && i.isSelectionInsideBlok(e)), r = i.isEventTargetInsideBlok(t.target);
2421
+ !n && !r || (t.preventDefault(), t.stopPropagation(), t.stopImmediatePropagation(), i.normalizeAllBoldTags());
2422
+ }
2423
+ /**
2424
+ * Attempt to toggle bold via the browser's native command
2425
+ * @param selection - Current selection
2426
+ */
2427
+ /**
2428
+ * Exit a collapsed bold selection by moving the caret outside the bold element
2429
+ * @param selection - The current selection
2430
+ * @param boldElement - The bold element to exit from
2431
+ */
2432
+ static exitCollapsedBold(t, e) {
2433
+ const n = i.ensureStrongElement(e), r = n.parentNode;
2434
+ if (r)
2435
+ return i.isElementEmpty(n) ? i.removeEmptyBoldElement(t, n, r) : i.exitCollapsedBoldWithContent(t, n, r);
2436
+ }
2437
+ /**
2438
+ * Remove an empty bold element and place the caret before its position
2439
+ * @param selection - Current selection
2440
+ * @param boldElement - Bold element to remove
2441
+ * @param parent - Parent node that hosts the bold element
2442
+ */
2443
+ static removeEmptyBoldElement(t, e, n) {
2444
+ const r = document.createRange();
2445
+ return r.setStartBefore(e), r.collapse(!0), n.removeChild(e), t.removeAllRanges(), t.addRange(r), r;
2446
+ }
2447
+ /**
2448
+ * Exit a collapsed bold state when the bold element still contains text
2449
+ * @param selection - Current selection
2450
+ * @param boldElement - Bold element currently wrapping the caret
2451
+ * @param parent - Parent node that hosts the bold element
2452
+ */
2453
+ static exitCollapsedBoldWithContent(t, e, n) {
2454
+ var p, g, y, E, C, T;
2455
+ e.setAttribute(i.DATA_ATTR_COLLAPSED_LENGTH, ((g = (p = e.textContent) == null ? void 0 : p.length) != null ? g : 0).toString()), e.removeAttribute(i.DATA_ATTR_PREV_LENGTH), e.removeAttribute(i.DATA_ATTR_COLLAPSED_ACTIVE), e.removeAttribute(i.DATA_ATTR_LEADING_WHITESPACE);
2456
+ const r = e.nextSibling, s = !r || r.nodeType !== Node.TEXT_NODE, o = s ? document.createTextNode("​") : null;
2457
+ o && n.insertBefore(o, r);
2458
+ const a = o != null ? o : r;
2459
+ !s && ((y = a.textContent) != null ? y : "").length === 0 && (a.textContent = "​");
2460
+ const l = document.createRange(), c = ((E = a.textContent) != null ? E : "").startsWith("​") ? 1 : 0;
2461
+ l.setStart(a, c), l.collapse(!0), t.removeAllRanges(), t.addRange(l);
2462
+ const h = i.ensureStrongElement(e);
2463
+ return i.collapsedExitRecords.add({
2464
+ boundary: a,
2465
+ boldElement: h,
2466
+ allowedLength: (T = (C = h.textContent) == null ? void 0 : C.length) != null ? T : 0,
2467
+ hasLeadingSpace: !1,
2468
+ hasTypedContent: !1,
2469
+ leadingWhitespace: ""
2470
+ }), l;
2471
+ }
2472
+ /**
2473
+ * Get a bold element at the boundary of a collapsed range
2474
+ * @param range - The collapsed range to check
2475
+ */
2476
+ static getBoundaryBold(t) {
2477
+ const e = t.startContainer;
2478
+ return e.nodeType === Node.TEXT_NODE ? i.getBoundaryBoldForText(t, e) : e.nodeType === Node.ELEMENT_NODE ? i.getBoundaryBoldForElement(t, e) : null;
2479
+ }
2480
+ /**
2481
+ * Get boundary bold when caret resides inside a text node
2482
+ * @param range - Collapsed range
2483
+ * @param textNode - Text container
2484
+ */
2485
+ static getBoundaryBoldForText(t, e) {
2486
+ var s, o;
2487
+ const n = (o = (s = e.textContent) == null ? void 0 : s.length) != null ? o : 0;
2488
+ if (t.startOffset === n)
2489
+ return i.findBoldElement(e);
2490
+ if (t.startOffset !== 0)
2491
+ return null;
2492
+ const r = e.previousSibling;
2493
+ return i.isBoldElement(r) ? r : null;
2494
+ }
2495
+ /**
2496
+ * Get boundary bold when caret container is an element
2497
+ * @param range - Collapsed range
2498
+ * @param element - Element container
2499
+ */
2500
+ static getBoundaryBoldForElement(t, e) {
2501
+ if (t.startOffset <= 0)
2502
+ return null;
2503
+ const n = e.childNodes[t.startOffset - 1];
2504
+ return i.isBoldElement(n) ? n : null;
2505
+ }
2506
+ /**
2507
+ * Handle keyboard shortcut for bold when selection is collapsed
2508
+ * @param event - The keyboard event
2509
+ */
2510
+ static handleShortcut(t) {
2511
+ var r;
2512
+ if (i.guardCollapsedBoundaryKeydown(t), !i.isBoldShortcut(t))
2513
+ return;
2514
+ const e = window.getSelection();
2515
+ if (!e || !e.rangeCount || !i.isSelectionInsideBlok(e))
2516
+ return;
2517
+ const n = (r = i.instances.values().next().value) != null ? r : new i();
2518
+ n && (t.preventDefault(), t.stopPropagation(), t.stopImmediatePropagation(), n.toggleBold());
2519
+ }
2520
+ /**
2521
+ * Check if a keyboard event is the bold shortcut (Cmd/Ctrl+B)
2522
+ * @param event - The keyboard event to check
2523
+ */
2524
+ static isBoldShortcut(t) {
2525
+ return !((typeof navigator != "undefined" ? navigator.userAgent.toLowerCase() : "").includes("mac") ? t.metaKey : t.ctrlKey) || t.altKey ? !1 : t.key.toLowerCase() === "b";
2526
+ }
2527
+ /**
2528
+ * Check if a selection is inside the blok
2529
+ * @param selection - The selection to check
2530
+ */
2531
+ static isSelectionInsideBlok(t) {
2532
+ const e = t.anchorNode;
2533
+ if (!e)
2534
+ return !1;
2535
+ const n = e.nodeType === Node.ELEMENT_NODE ? e : e.parentElement;
2536
+ return !!(n != null && n.closest(A(m.editor)));
2537
+ }
2538
+ /**
2539
+ * Check if an event target resides inside the blok wrapper
2540
+ * @param target - Event target to inspect
2541
+ */
2542
+ static isEventTargetInsideBlok(t) {
2543
+ var n;
2544
+ if (!t || typeof Node == "undefined")
2545
+ return !1;
2546
+ if (t instanceof Element)
2547
+ return !!t.closest(A(m.editor));
2548
+ if (t instanceof Text)
2549
+ return !!((n = t.parentElement) != null && n.closest(A(m.editor)));
2550
+ if (typeof ShadowRoot != "undefined" && t instanceof ShadowRoot)
2551
+ return i.isEventTargetInsideBlok(t.host);
2552
+ if (!(t instanceof Node))
2553
+ return !1;
2554
+ const e = t.parentNode;
2555
+ return e ? e instanceof Element ? !!e.closest(A(m.editor)) : i.isEventTargetInsideBlok(e) : !1;
2556
+ }
2557
+ /**
2558
+ * Get HTML content of a range with bold tags removed
2559
+ * @param range - The range to extract HTML from
2560
+ */
2561
+ getRangeHtmlWithoutBold(t) {
2562
+ const e = t.cloneContents();
2563
+ this.removeNestedBold(e);
2564
+ const n = document.createElement("div");
2565
+ return n.appendChild(e), n.innerHTML;
2566
+ }
2567
+ /**
2568
+ * Convert an HTML snippet to a document fragment
2569
+ * @param html - HTML string to convert
2570
+ */
2571
+ static createFragmentFromHtml(t) {
2572
+ const e = document.createElement("template");
2573
+ return e.innerHTML = t, e.content;
2574
+ }
2575
+ /**
2576
+ * Collect all bold ancestor elements within a range
2577
+ * @param range - The range to search for bold ancestors
2578
+ */
2579
+ collectBoldAncestors(t) {
2580
+ const e = /* @__PURE__ */ new Set(), n = document.createTreeWalker(
2581
+ t.commonAncestorContainer,
2582
+ NodeFilter.SHOW_TEXT,
2583
+ {
2584
+ acceptNode: (r) => {
2585
+ try {
2586
+ return t.intersectsNode(r) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2587
+ } catch (s) {
2588
+ const o = document.createRange();
2589
+ o.selectNodeContents(r);
2590
+ const a = t.compareBoundaryPoints(Range.END_TO_START, o) > 0, l = t.compareBoundaryPoints(Range.START_TO_END, o) < 0;
2591
+ return a && l ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2592
+ }
2593
+ }
2594
+ }
2595
+ );
2596
+ for (; n.nextNode(); ) {
2597
+ const r = i.findBoldElement(n.currentNode);
2598
+ r && e.add(r);
2599
+ }
2600
+ return Array.from(e);
2601
+ }
2602
+ };
2603
+ i.isInline = !0, i.title = "Bold", i.titleKey = "bold", i.shortcutListenerRegistered = !1, i.selectionListenerRegistered = !1, i.inputListenerRegistered = !1, i.beforeInputListenerRegistered = !1, i.globalListenersInitialized = i.initializeGlobalListeners(), i.collapsedExitRecords = /* @__PURE__ */ new Set(), i.markerSequence = 0, i.isProcessingMutation = !1, i.DATA_ATTR_COLLAPSED_LENGTH = "data-blok-bold-collapsed-length", i.DATA_ATTR_COLLAPSED_ACTIVE = "data-blok-bold-collapsed-active", i.DATA_ATTR_PREV_LENGTH = "data-blok-bold-prev-length", i.DATA_ATTR_LEADING_WHITESPACE = "data-blok-bold-leading-ws", i.instances = /* @__PURE__ */ new Set(), i.pendingBoundaryCaretAdjustments = /* @__PURE__ */ new WeakSet(), i.shortcut = "CMD+B";
2604
+ let Z = i;
2605
+ const O = class O {
2606
+ /**
2607
+ * Sanitizer Rule
2608
+ * Leave <i> and <em> tags
2609
+ * @returns {object}
2610
+ */
2611
+ static get sanitize() {
2612
+ return {
2613
+ i: {},
2614
+ em: {}
2615
+ };
2616
+ }
2617
+ /**
2618
+ * Create button for Inline Toolbar
2619
+ */
2620
+ render() {
2621
+ return {
2622
+ icon: Et,
2623
+ name: "italic",
2624
+ onActivate: () => {
2625
+ this.toggleItalic();
2626
+ },
2627
+ isActive: () => {
2628
+ const t = window.getSelection();
2629
+ return t ? this.isSelectionVisuallyItalic(t) : !1;
2630
+ }
2631
+ };
2632
+ }
2633
+ /**
2634
+ * Apply or remove italic formatting using modern Selection API
2635
+ */
2636
+ toggleItalic() {
2637
+ const t = window.getSelection();
2638
+ if (!t || t.rangeCount === 0)
2639
+ return;
2640
+ const e = t.getRangeAt(0);
2641
+ if (e.collapsed) {
2642
+ this.toggleCollapsedItalic(e, t);
2643
+ return;
2644
+ }
2645
+ this.isRangeItalic(e, { ignoreWhitespace: !0 }) ? this.unwrapItalicTags(e) : this.wrapWithItalic(e);
2646
+ }
2647
+ /**
2648
+ * Handle toggle for collapsed selection (caret)
2649
+ * @param range - Current range
2650
+ * @param selection - Current selection
2651
+ */
2652
+ toggleCollapsedItalic(t, e) {
2653
+ if (this.isRangeItalic(t, { ignoreWhitespace: !0 })) {
2654
+ const r = document.createTextNode("​");
2655
+ t.insertNode(r), t.selectNode(r), this.unwrapItalicTags(t);
2656
+ const s = document.createRange();
2657
+ s.setStart(r, 1), s.setEnd(r, 1), e.removeAllRanges(), e.addRange(s);
2658
+ } else {
2659
+ const r = document.createElement("i"), s = document.createTextNode("​");
2660
+ r.appendChild(s), t.insertNode(r);
2661
+ const o = document.createRange();
2662
+ o.setStart(s, 1), o.setEnd(s, 1), e.removeAllRanges(), e.addRange(o);
2663
+ }
2664
+ }
2665
+ /**
2666
+ * Check if current selection is within an italic tag
2667
+ * @param selection - The Selection object to check
2668
+ */
2669
+ isSelectionVisuallyItalic(t) {
2670
+ if (!t || t.rangeCount === 0)
2671
+ return !1;
2672
+ const e = t.getRangeAt(0);
2673
+ return this.isRangeItalic(e, { ignoreWhitespace: !0 });
2674
+ }
2675
+ /**
2676
+ * Check if a range contains italic text
2677
+ * @param range - The range to check
2678
+ * @param options - Options for checking italic status
2679
+ */
2680
+ isRangeItalic(t, e) {
2681
+ var s;
2682
+ if (t.collapsed)
2683
+ return !!this.findItalicElement(t.startContainer);
2684
+ const n = document.createTreeWalker(
2685
+ t.commonAncestorContainer,
2686
+ NodeFilter.SHOW_TEXT,
2687
+ {
2688
+ acceptNode: (o) => {
2689
+ try {
2690
+ return t.intersectsNode(o) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2691
+ } catch (a) {
2692
+ const l = document.createRange();
2693
+ l.selectNodeContents(o);
2694
+ const d = t.compareBoundaryPoints(Range.END_TO_START, l) > 0, c = t.compareBoundaryPoints(Range.START_TO_END, l) < 0;
2695
+ return d && c ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2696
+ }
2697
+ }
2698
+ }
2699
+ ), r = [];
2700
+ for (; n.nextNode(); ) {
2701
+ const o = n.currentNode, a = (s = o.textContent) != null ? s : "";
2702
+ e.ignoreWhitespace && a.trim().length === 0 || a.length !== 0 && r.push(o);
2703
+ }
2704
+ return r.length === 0 ? !!this.findItalicElement(t.startContainer) : r.every((o) => this.hasItalicParent(o));
2705
+ }
2706
+ /**
2707
+ * Wrap selection with <i> tag
2708
+ * @param range - The Range object containing the selection to wrap
2709
+ */
2710
+ wrapWithItalic(t) {
2711
+ const e = this.getRangeHtmlWithoutItalic(t), n = this.replaceRangeWithHtml(t, `<i>${e}</i>`), r = window.getSelection();
2712
+ r && n && (r.removeAllRanges(), r.addRange(n));
2713
+ }
2714
+ /**
2715
+ * Remove italic tags (<i>/<em>) while preserving content
2716
+ * @param range - The Range object containing the selection to unwrap
2717
+ */
2718
+ unwrapItalicTags(t) {
2719
+ const e = this.collectItalicAncestors(t), n = window.getSelection();
2720
+ if (!n)
2721
+ return;
2722
+ const r = document.createElement("span"), s = t.extractContents();
2723
+ r.appendChild(s), this.removeNestedItalic(r), t.insertNode(r);
2724
+ const o = document.createRange();
2725
+ for (o.selectNodeContents(r), n.removeAllRanges(), n.addRange(o); ; ) {
2726
+ const c = this.findItalicElement(r);
2727
+ if (!c)
2728
+ break;
2729
+ this.moveMarkerOutOfItalic(r, c);
2730
+ }
2731
+ const a = r.firstChild, l = r.lastChild;
2732
+ this.unwrapElement(r), (a && l ? (() => {
2733
+ const c = document.createRange();
2734
+ return c.setStartBefore(a), c.setEndAfter(l), n.removeAllRanges(), n.addRange(c), c;
2735
+ })() : void 0) || n.removeAllRanges(), e.forEach((c) => {
2736
+ var h;
2737
+ ((h = c.textContent) != null ? h : "").length === 0 && c.remove();
2738
+ });
2739
+ }
2740
+ /**
2741
+ * Check if a node or any of its parents is an italic tag
2742
+ * @param node - The node to check
2743
+ */
2744
+ hasItalicParent(t) {
2745
+ return t ? t.nodeType === Node.ELEMENT_NODE && this.isItalicTag(t) ? !0 : this.hasItalicParent(t.parentNode) : !1;
2746
+ }
2747
+ /**
2748
+ * Find an italic element in the parent chain
2749
+ * @param node - The node to start searching from
2750
+ */
2751
+ findItalicElement(t) {
2752
+ return t ? t.nodeType === Node.ELEMENT_NODE && this.isItalicTag(t) ? t : this.findItalicElement(t.parentNode) : null;
2753
+ }
2754
+ /**
2755
+ * Check if an element is an italic tag (<i> or <em>)
2756
+ * @param node - The element to check
2757
+ */
2758
+ isItalicTag(t) {
2759
+ const e = t.tagName;
2760
+ return e === "I" || e === "EM";
2761
+ }
2762
+ /**
2763
+ * Collect all italic ancestor elements within a range
2764
+ * @param range - The range to search for italic ancestors
2765
+ */
2766
+ collectItalicAncestors(t) {
2767
+ const e = /* @__PURE__ */ new Set(), n = document.createTreeWalker(
2768
+ t.commonAncestorContainer,
2769
+ NodeFilter.SHOW_TEXT,
2770
+ {
2771
+ acceptNode: (r) => {
2772
+ try {
2773
+ return t.intersectsNode(r) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2774
+ } catch (s) {
2775
+ const o = document.createRange();
2776
+ o.selectNodeContents(r);
2777
+ const a = t.compareBoundaryPoints(Range.END_TO_START, o) > 0, l = t.compareBoundaryPoints(Range.START_TO_END, o) < 0;
2778
+ return a && l ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
2779
+ }
2780
+ }
2781
+ }
2782
+ );
2783
+ for (; n.nextNode(); ) {
2784
+ const r = this.findItalicElement(n.currentNode);
2785
+ r && e.add(r);
2786
+ }
2787
+ return Array.from(e);
2788
+ }
2789
+ /**
2790
+ * Get HTML content of a range with italic tags removed
2791
+ * @param range - The range to extract HTML from
2792
+ */
2793
+ getRangeHtmlWithoutItalic(t) {
2794
+ const e = t.cloneContents();
2795
+ this.removeNestedItalic(e);
2796
+ const n = document.createElement("div");
2797
+ return n.appendChild(e), n.innerHTML;
2798
+ }
2799
+ /**
2800
+ * Remove nested italic tags from a root node
2801
+ * @param root - The root node to process
2802
+ */
2803
+ removeNestedItalic(t) {
2804
+ var n;
2805
+ const e = (n = t.querySelectorAll) == null ? void 0 : n.call(t, "i,em");
2806
+ e && e.forEach((r) => {
2807
+ this.unwrapElement(r);
2808
+ });
2809
+ }
2810
+ /**
2811
+ * Unwrap an element by moving its children to the parent
2812
+ * @param element - The element to unwrap
2813
+ */
2814
+ unwrapElement(t) {
2815
+ const e = t.parentNode;
2816
+ if (!e) {
2817
+ t.remove();
2818
+ return;
2819
+ }
2820
+ for (; t.firstChild; )
2821
+ e.insertBefore(t.firstChild, t);
2822
+ e.removeChild(t);
2823
+ }
2824
+ /**
2825
+ * Replace the current range contents with provided HTML snippet
2826
+ * @param range - Range to replace
2827
+ * @param html - HTML string to insert
2828
+ */
2829
+ replaceRangeWithHtml(t, e) {
2830
+ var a, l;
2831
+ const n = this.createFragmentFromHtml(e), r = (a = n.firstChild) != null ? a : null, s = (l = n.lastChild) != null ? l : null;
2832
+ if (t.deleteContents(), !r || !s)
2833
+ return;
2834
+ t.insertNode(n);
2835
+ const o = document.createRange();
2836
+ return o.setStartBefore(r), o.setEndAfter(s), o;
2837
+ }
2838
+ /**
2839
+ * Convert an HTML snippet to a document fragment
2840
+ * @param html - HTML string to convert
2841
+ */
2842
+ createFragmentFromHtml(t) {
2843
+ const e = document.createElement("template");
2844
+ return e.innerHTML = t, e.content;
2845
+ }
2846
+ /**
2847
+ * Move a temporary marker element outside of an italic ancestor while preserving content order
2848
+ * @param marker - Marker element wrapping the selection contents
2849
+ * @param italicElement - Italic ancestor containing the marker
2850
+ */
2851
+ moveMarkerOutOfItalic(t, e) {
2852
+ const n = e.parentNode;
2853
+ if (!n)
2854
+ return;
2855
+ if (Array.from(e.childNodes).forEach((l) => {
2856
+ var d;
2857
+ l.nodeType === Node.TEXT_NODE && ((d = l.textContent) != null ? d : "").length === 0 && l.remove();
2858
+ }), e.childNodes.length === 1 && e.firstChild === t) {
2859
+ e.replaceWith(t);
2860
+ return;
2861
+ }
2862
+ if (e.firstChild === t) {
2863
+ n.insertBefore(t, e);
2864
+ return;
2865
+ }
2866
+ if (e.lastChild === t) {
2867
+ n.insertBefore(t, e.nextSibling);
2868
+ return;
2869
+ }
2870
+ const a = e.cloneNode(!1);
2871
+ for (; t.nextSibling; )
2872
+ a.appendChild(t.nextSibling);
2873
+ n.insertBefore(a, e.nextSibling), n.insertBefore(t, a);
2874
+ }
2875
+ };
2876
+ O.isInline = !0, O.title = "Italic", O.titleKey = "italic", O.shortcut = "CMD+I";
2877
+ let Q = O;
2878
+ const D = class D {
2879
+ /**
2880
+ * @param api - Blok API
2881
+ */
2882
+ constructor({ api: t }) {
2883
+ this.INPUT_BASE_CLASSES = "hidden w-full m-0 px-2 py-1 text-sm leading-[22px] font-medium bg-item-hover-bg border border-[rgba(226,226,229,0.2)] rounded-md outline-none box-border appearance-none font-[inherit] placeholder:text-gray-text mobile:text-[15px] mobile:font-medium", this.DATA_ATTRIBUTES = {
2884
+ buttonActive: "data-blok-link-tool-active",
2885
+ buttonUnlink: "data-blok-link-tool-unlink",
2886
+ inputOpened: "data-blok-link-tool-input-opened"
2887
+ }, this.nodes = {
2888
+ input: null,
2889
+ button: null
2890
+ }, this.inputOpened = !1, this.unlinkAvailable = !1, this.handleButtonClick = (e) => {
2891
+ !this.inputOpened || !this.unlinkAvailable || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), this.restoreSelection(), this.unlink(), this.inlineToolbar.close());
2892
+ }, this.toolbar = t.toolbar, this.inlineToolbar = t.inlineToolbar, this.notifier = t.notifier, this.i18n = t.i18n, this.selection = new H(), this.nodes.input = this.createInput();
2893
+ }
2894
+ /**
2895
+ * Sanitizer Rule
2896
+ * Leave <a> tags
2897
+ * @returns {object}
2898
+ */
2899
+ static get sanitize() {
2900
+ return {
2901
+ a: {
2902
+ href: !0,
2903
+ target: "_blank",
2904
+ rel: "nofollow"
2905
+ }
2906
+ };
2907
+ }
2908
+ /**
2909
+ * Create button for Inline Toolbar
2910
+ */
2911
+ render() {
2912
+ return {
2913
+ icon: Ct,
2914
+ name: "link",
2915
+ isActive: () => !!this.selection.findParentTag("A"),
2916
+ children: {
2917
+ hideChevron: !0,
2918
+ width: "200px",
2919
+ items: [
2920
+ {
2921
+ type: Tt.Html,
2922
+ element: this.nodes.input
2923
+ }
2924
+ ],
2925
+ onOpen: () => {
2926
+ this.openActions(!0);
2927
+ },
2928
+ onClose: () => {
2929
+ this.closeActions();
2930
+ }
2931
+ }
2932
+ };
2933
+ }
2934
+ /**
2935
+ * Input for the link
2936
+ */
2937
+ createInput() {
2938
+ const t = document.createElement("input");
2939
+ return t.placeholder = this.i18n.t("tools.link.addLink"), t.enterKeyHint = "done", t.className = this.INPUT_BASE_CLASSES, t.setAttribute("data-blok-testid", "inline-tool-input"), this.setBooleanStateAttribute(t, this.DATA_ATTRIBUTES.inputOpened, !1), t.addEventListener("keydown", (e) => {
2940
+ e.key === "Enter" && this.enterPressed(e);
2941
+ }), t;
2942
+ }
2943
+ /**
2944
+ * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope.
2945
+ */
2946
+ openActions(t = !1) {
2947
+ if (!this.nodes.input)
2948
+ return;
2949
+ const e = this.selection.findParentTag("A"), n = !!e;
2950
+ if (this.updateButtonStateAttributes(n), this.unlinkAvailable = n, e) {
2951
+ const r = e.getAttribute("href");
2952
+ this.nodes.input.value = r !== null ? r : "";
2953
+ } else
2954
+ this.nodes.input.value = "";
2955
+ this.nodes.input.className = x(this.INPUT_BASE_CLASSES, "block"), this.setBooleanStateAttribute(this.nodes.input, this.DATA_ATTRIBUTES.inputOpened, !0), this.selection.setFakeBackground(), this.selection.save(), t && this.focusInputWithRetry(), this.inputOpened = !0;
2956
+ }
2957
+ /**
2958
+ * Ensures the link input receives focus even if other listeners steal it
2959
+ */
2960
+ focusInputWithRetry() {
2961
+ this.nodes.input && (this.nodes.input.focus(), !(typeof window == "undefined" || typeof document == "undefined") && window.setTimeout(() => {
2962
+ var t;
2963
+ document.activeElement !== this.nodes.input && ((t = this.nodes.input) == null || t.focus());
2964
+ }, 0));
2965
+ }
2966
+ /**
2967
+ * Resolve the current inline toolbar button element
2968
+ */
2969
+ getButtonElement() {
2970
+ const t = document.querySelector(
2971
+ `${A(m.interface, yt)} [data-blok-item-name="link"]`
2972
+ );
2973
+ return t && t !== this.nodes.button && (t.addEventListener("click", this.handleButtonClick, !0), this.nodes.button = t), t;
2974
+ }
2975
+ /**
2976
+ * Update button state attributes for e2e hooks
2977
+ * @param hasAnchor - Optional override for anchor presence
2978
+ */
2979
+ updateButtonStateAttributes(t) {
2980
+ const e = this.getButtonElement();
2981
+ if (!e)
2982
+ return;
2983
+ const n = typeof t == "boolean" ? t : !!this.selection.findParentTag("A");
2984
+ this.setBooleanStateAttribute(e, this.DATA_ATTRIBUTES.buttonActive, n), this.setBooleanStateAttribute(e, this.DATA_ATTRIBUTES.buttonUnlink, n);
2985
+ }
2986
+ /**
2987
+ * Close input
2988
+ * @param {boolean} clearSavedSelection — we don't need to clear saved selection
2989
+ * on toggle-clicks on the icon of opened Toolbar
2990
+ */
2991
+ closeActions(t = !0) {
2992
+ (this.selection.isFakeBackgroundEnabled || t && this.selection.savedSelectionRange) && this.restoreSelection(), this.nodes.input && (this.nodes.input.className = this.INPUT_BASE_CLASSES, this.setBooleanStateAttribute(this.nodes.input, this.DATA_ATTRIBUTES.inputOpened, !1), this.nodes.input.value = "", this.updateButtonStateAttributes(!1), this.unlinkAvailable = !1, t && this.selection.clearSaved(), this.inputOpened = !1);
2993
+ }
2994
+ /**
2995
+ * Restore selection after closing actions
2996
+ */
2997
+ restoreSelection() {
2998
+ const t = new H(), e = H.isAtBlok;
2999
+ if (e && t.save(), this.selection.removeFakeBackground(), this.selection.restore(), !e && this.selection.savedSelectionRange) {
3000
+ const s = this.selection.savedSelectionRange.commonAncestorContainer, o = s.nodeType === Node.ELEMENT_NODE ? s : s.parentElement;
3001
+ o == null || o.focus();
3002
+ }
3003
+ if (!e)
3004
+ return;
3005
+ t.restore();
3006
+ const n = t.savedSelectionRange;
3007
+ if (n) {
3008
+ const r = n.commonAncestorContainer, s = r.nodeType === Node.ELEMENT_NODE ? r : r.parentElement;
3009
+ s == null || s.focus();
3010
+ }
3011
+ }
3012
+ /**
3013
+ * Enter pressed on input
3014
+ * @param {KeyboardEvent} event - enter keydown event
3015
+ */
3016
+ enterPressed(t) {
3017
+ if (!this.nodes.input)
3018
+ return;
3019
+ const e = this.nodes.input.value || "";
3020
+ if (!e.trim()) {
3021
+ this.selection.restore(), this.unlink(), t.preventDefault(), this.closeActions(), this.inlineToolbar.close();
3022
+ return;
3023
+ }
3024
+ if (!this.validateURL(e)) {
3025
+ this.notifier.show({
3026
+ message: this.i18n.t("tools.link.invalidLink"),
3027
+ style: "error"
3028
+ }), At("Incorrect Link pasted", "warn", e);
3029
+ return;
3030
+ }
3031
+ const n = this.prepareLink(e);
3032
+ this.selection.removeFakeBackground(), this.selection.restore(), this.insertLink(n), t.preventDefault(), t.stopPropagation(), t.stopImmediatePropagation(), this.selection.collapseToEnd(), this.inlineToolbar.close();
3033
+ }
3034
+ /**
3035
+ * Detects if passed string is URL
3036
+ * @param {string} str - string to validate
3037
+ * @returns {boolean}
3038
+ */
3039
+ validateURL(t) {
3040
+ return !/\s/.test(t);
3041
+ }
3042
+ /**
3043
+ * Process link before injection
3044
+ * - sanitize
3045
+ * - add protocol for links like 'google.com'
3046
+ * @param {string} link - raw user input
3047
+ */
3048
+ prepareLink(t) {
3049
+ return this.addProtocol(t.trim());
3050
+ }
3051
+ /**
3052
+ * Add 'http' protocol to the links like 'vc.ru', 'google.com'
3053
+ * @param {string} link - string to process
3054
+ */
3055
+ addProtocol(t) {
3056
+ if (/^(\w+):(\/\/)?/.test(t))
3057
+ return t;
3058
+ const e = /^\/[^/\s]/.test(t), n = t.substring(0, 1) === "#", r = /^\/\/[^/\s]/.test(t);
3059
+ return !e && !n && !r ? "http://" + t : t;
3060
+ }
3061
+ /**
3062
+ * Inserts <a> tag with "href"
3063
+ * @param {string} link - "href" value
3064
+ */
3065
+ insertLink(t) {
3066
+ const e = this.selection.findParentTag("A");
3067
+ if (e) {
3068
+ this.selection.expandToTag(e), e.href = t, e.target = "_blank", e.rel = "nofollow";
3069
+ return;
3070
+ }
3071
+ const n = H.range;
3072
+ if (!n)
3073
+ return;
3074
+ const r = document.createElement("a");
3075
+ r.href = t, r.target = "_blank", r.rel = "nofollow", r.appendChild(n.extractContents()), n.insertNode(r), this.selection.expandToTag(r);
3076
+ }
3077
+ /**
3078
+ * Removes <a> tag
3079
+ */
3080
+ unlink() {
3081
+ const t = this.selection.findParentTag("A");
3082
+ t && (this.unwrap(t), this.updateButtonStateAttributes(!1), this.unlinkAvailable = !1);
3083
+ }
3084
+ /**
3085
+ * Unwrap passed node
3086
+ * @param term - node to unwrap
3087
+ */
3088
+ unwrap(t) {
3089
+ var n;
3090
+ const e = document.createDocumentFragment();
3091
+ for (; t.firstChild; )
3092
+ e.appendChild(t.firstChild);
3093
+ (n = t.parentNode) == null || n.replaceChild(e, t);
3094
+ }
3095
+ /**
3096
+ * Persist state as data attributes for testing hooks
3097
+ * @param element - The HTML element to set the attribute on, or null
3098
+ * @param attributeName - The name of the attribute to set
3099
+ * @param state - The boolean state value to persist
3100
+ */
3101
+ setBooleanStateAttribute(t, e, n) {
3102
+ t && t.setAttribute(e, n ? "true" : "false");
3103
+ }
3104
+ };
3105
+ D.isInline = !0, D.title = "Link", D.titleKey = "link", D.shortcut = "CMD+K";
3106
+ let tt = D;
3107
+ const vt = {
3108
+ paragraph: { preserveBlank: !0 },
3109
+ header: {},
3110
+ list: {}
3111
+ }, Rt = {
3112
+ bold: {},
3113
+ italic: {},
3114
+ link: {}
3115
+ };
3116
+ export {
3117
+ Z as Bold,
3118
+ Dt as Convert,
3119
+ Y as Header,
3120
+ Q as Italic,
3121
+ tt as Link,
3122
+ J as List,
3123
+ X as Paragraph,
3124
+ vt as defaultBlockTools,
3125
+ Rt as defaultInlineTools
3126
+ };