@jackuait/blok 0.4.1-beta.0 → 0.4.1-beta.11

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