@ckeditor/ckeditor5-bookmark 0.0.1 → 44.0.0-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (408) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/LICENSE.md +4 -5
  3. package/README.md +31 -3
  4. package/build/bookmark.js +5 -0
  5. package/build/translations/af.js +1 -0
  6. package/build/translations/ar.js +1 -0
  7. package/build/translations/ast.js +1 -0
  8. package/build/translations/az.js +1 -0
  9. package/build/translations/bg.js +1 -0
  10. package/build/translations/bn.js +1 -0
  11. package/build/translations/bs.js +1 -0
  12. package/build/translations/ca.js +1 -0
  13. package/build/translations/cs.js +1 -0
  14. package/build/translations/da.js +1 -0
  15. package/build/translations/de-ch.js +1 -0
  16. package/build/translations/de.js +1 -0
  17. package/build/translations/el.js +1 -0
  18. package/build/translations/en-au.js +1 -0
  19. package/build/translations/en-gb.js +1 -0
  20. package/build/translations/eo.js +1 -0
  21. package/build/translations/es-co.js +1 -0
  22. package/build/translations/es.js +1 -0
  23. package/build/translations/et.js +1 -0
  24. package/build/translations/eu.js +1 -0
  25. package/build/translations/fa.js +1 -0
  26. package/build/translations/fi.js +1 -0
  27. package/build/translations/fr.js +1 -0
  28. package/build/translations/gl.js +1 -0
  29. package/build/translations/gu.js +1 -0
  30. package/build/translations/he.js +1 -0
  31. package/build/translations/hi.js +1 -0
  32. package/build/translations/hr.js +1 -0
  33. package/build/translations/hu.js +1 -0
  34. package/build/translations/hy.js +1 -0
  35. package/build/translations/id.js +1 -0
  36. package/build/translations/it.js +1 -0
  37. package/build/translations/ja.js +1 -0
  38. package/build/translations/jv.js +1 -0
  39. package/build/translations/kk.js +1 -0
  40. package/build/translations/km.js +1 -0
  41. package/build/translations/kn.js +1 -0
  42. package/build/translations/ko.js +1 -0
  43. package/build/translations/ku.js +1 -0
  44. package/build/translations/lt.js +1 -0
  45. package/build/translations/lv.js +1 -0
  46. package/build/translations/ms.js +1 -0
  47. package/build/translations/nb.js +1 -0
  48. package/build/translations/ne.js +1 -0
  49. package/build/translations/nl.js +1 -0
  50. package/build/translations/no.js +1 -0
  51. package/build/translations/oc.js +1 -0
  52. package/build/translations/pl.js +1 -0
  53. package/build/translations/pt-br.js +1 -0
  54. package/build/translations/pt.js +1 -0
  55. package/build/translations/ro.js +1 -0
  56. package/build/translations/ru.js +1 -0
  57. package/build/translations/si.js +1 -0
  58. package/build/translations/sk.js +1 -0
  59. package/build/translations/sl.js +1 -0
  60. package/build/translations/sq.js +1 -0
  61. package/build/translations/sr-latn.js +1 -0
  62. package/build/translations/sr.js +1 -0
  63. package/build/translations/sv.js +1 -0
  64. package/build/translations/th.js +1 -0
  65. package/build/translations/ti.js +1 -0
  66. package/build/translations/tk.js +1 -0
  67. package/build/translations/tr.js +1 -0
  68. package/build/translations/tt.js +1 -0
  69. package/build/translations/ug.js +1 -0
  70. package/build/translations/uk.js +1 -0
  71. package/build/translations/ur.js +1 -0
  72. package/build/translations/uz.js +1 -0
  73. package/build/translations/vi.js +1 -0
  74. package/build/translations/zh-cn.js +1 -0
  75. package/build/translations/zh.js +1 -0
  76. package/ckeditor5-metadata.json +24 -0
  77. package/dist/augmentation.d.ts +28 -0
  78. package/dist/bookmark.d.ts +34 -0
  79. package/dist/bookmarkconfig.d.ts +52 -0
  80. package/dist/bookmarkediting.d.ts +55 -0
  81. package/dist/bookmarkui.d.ts +170 -0
  82. package/dist/index-content.css +4 -0
  83. package/dist/index-editor.css +150 -0
  84. package/dist/index.css +195 -0
  85. package/dist/index.css.map +1 -0
  86. package/dist/index.d.ts +18 -0
  87. package/dist/index.js +1320 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/insertbookmarkcommand.d.ts +42 -0
  90. package/dist/translations/af.d.ts +8 -0
  91. package/dist/translations/af.js +5 -0
  92. package/dist/translations/af.umd.js +11 -0
  93. package/dist/translations/ar.d.ts +8 -0
  94. package/dist/translations/ar.js +5 -0
  95. package/dist/translations/ar.umd.js +11 -0
  96. package/dist/translations/ast.d.ts +8 -0
  97. package/dist/translations/ast.js +5 -0
  98. package/dist/translations/ast.umd.js +11 -0
  99. package/dist/translations/az.d.ts +8 -0
  100. package/dist/translations/az.js +5 -0
  101. package/dist/translations/az.umd.js +11 -0
  102. package/dist/translations/bg.d.ts +8 -0
  103. package/dist/translations/bg.js +5 -0
  104. package/dist/translations/bg.umd.js +11 -0
  105. package/dist/translations/bn.d.ts +8 -0
  106. package/dist/translations/bn.js +5 -0
  107. package/dist/translations/bn.umd.js +11 -0
  108. package/dist/translations/bs.d.ts +8 -0
  109. package/dist/translations/bs.js +5 -0
  110. package/dist/translations/bs.umd.js +11 -0
  111. package/dist/translations/ca.d.ts +8 -0
  112. package/dist/translations/ca.js +5 -0
  113. package/dist/translations/ca.umd.js +11 -0
  114. package/dist/translations/cs.d.ts +8 -0
  115. package/dist/translations/cs.js +5 -0
  116. package/dist/translations/cs.umd.js +11 -0
  117. package/dist/translations/da.d.ts +8 -0
  118. package/dist/translations/da.js +5 -0
  119. package/dist/translations/da.umd.js +11 -0
  120. package/dist/translations/de-ch.d.ts +8 -0
  121. package/dist/translations/de-ch.js +5 -0
  122. package/dist/translations/de-ch.umd.js +11 -0
  123. package/dist/translations/de.d.ts +8 -0
  124. package/dist/translations/de.js +5 -0
  125. package/dist/translations/de.umd.js +11 -0
  126. package/dist/translations/el.d.ts +8 -0
  127. package/dist/translations/el.js +5 -0
  128. package/dist/translations/el.umd.js +11 -0
  129. package/dist/translations/en-au.d.ts +8 -0
  130. package/dist/translations/en-au.js +5 -0
  131. package/dist/translations/en-au.umd.js +11 -0
  132. package/dist/translations/en-gb.d.ts +8 -0
  133. package/dist/translations/en-gb.js +5 -0
  134. package/dist/translations/en-gb.umd.js +11 -0
  135. package/dist/translations/en.d.ts +8 -0
  136. package/dist/translations/en.js +5 -0
  137. package/dist/translations/en.umd.js +11 -0
  138. package/dist/translations/eo.d.ts +8 -0
  139. package/dist/translations/eo.js +5 -0
  140. package/dist/translations/eo.umd.js +11 -0
  141. package/dist/translations/es-co.d.ts +8 -0
  142. package/dist/translations/es-co.js +5 -0
  143. package/dist/translations/es-co.umd.js +11 -0
  144. package/dist/translations/es.d.ts +8 -0
  145. package/dist/translations/es.js +5 -0
  146. package/dist/translations/es.umd.js +11 -0
  147. package/dist/translations/et.d.ts +8 -0
  148. package/dist/translations/et.js +5 -0
  149. package/dist/translations/et.umd.js +11 -0
  150. package/dist/translations/eu.d.ts +8 -0
  151. package/dist/translations/eu.js +5 -0
  152. package/dist/translations/eu.umd.js +11 -0
  153. package/dist/translations/fa.d.ts +8 -0
  154. package/dist/translations/fa.js +5 -0
  155. package/dist/translations/fa.umd.js +11 -0
  156. package/dist/translations/fi.d.ts +8 -0
  157. package/dist/translations/fi.js +5 -0
  158. package/dist/translations/fi.umd.js +11 -0
  159. package/dist/translations/fr.d.ts +8 -0
  160. package/dist/translations/fr.js +5 -0
  161. package/dist/translations/fr.umd.js +11 -0
  162. package/dist/translations/gl.d.ts +8 -0
  163. package/dist/translations/gl.js +5 -0
  164. package/dist/translations/gl.umd.js +11 -0
  165. package/dist/translations/gu.d.ts +8 -0
  166. package/dist/translations/gu.js +5 -0
  167. package/dist/translations/gu.umd.js +11 -0
  168. package/dist/translations/he.d.ts +8 -0
  169. package/dist/translations/he.js +5 -0
  170. package/dist/translations/he.umd.js +11 -0
  171. package/dist/translations/hi.d.ts +8 -0
  172. package/dist/translations/hi.js +5 -0
  173. package/dist/translations/hi.umd.js +11 -0
  174. package/dist/translations/hr.d.ts +8 -0
  175. package/dist/translations/hr.js +5 -0
  176. package/dist/translations/hr.umd.js +11 -0
  177. package/dist/translations/hu.d.ts +8 -0
  178. package/dist/translations/hu.js +5 -0
  179. package/dist/translations/hu.umd.js +11 -0
  180. package/dist/translations/hy.d.ts +8 -0
  181. package/dist/translations/hy.js +5 -0
  182. package/dist/translations/hy.umd.js +11 -0
  183. package/dist/translations/id.d.ts +8 -0
  184. package/dist/translations/id.js +5 -0
  185. package/dist/translations/id.umd.js +11 -0
  186. package/dist/translations/it.d.ts +8 -0
  187. package/dist/translations/it.js +5 -0
  188. package/dist/translations/it.umd.js +11 -0
  189. package/dist/translations/ja.d.ts +8 -0
  190. package/dist/translations/ja.js +5 -0
  191. package/dist/translations/ja.umd.js +11 -0
  192. package/dist/translations/jv.d.ts +8 -0
  193. package/dist/translations/jv.js +5 -0
  194. package/dist/translations/jv.umd.js +11 -0
  195. package/dist/translations/kk.d.ts +8 -0
  196. package/dist/translations/kk.js +5 -0
  197. package/dist/translations/kk.umd.js +11 -0
  198. package/dist/translations/km.d.ts +8 -0
  199. package/dist/translations/km.js +5 -0
  200. package/dist/translations/km.umd.js +11 -0
  201. package/dist/translations/kn.d.ts +8 -0
  202. package/dist/translations/kn.js +5 -0
  203. package/dist/translations/kn.umd.js +11 -0
  204. package/dist/translations/ko.d.ts +8 -0
  205. package/dist/translations/ko.js +5 -0
  206. package/dist/translations/ko.umd.js +11 -0
  207. package/dist/translations/ku.d.ts +8 -0
  208. package/dist/translations/ku.js +5 -0
  209. package/dist/translations/ku.umd.js +11 -0
  210. package/dist/translations/lt.d.ts +8 -0
  211. package/dist/translations/lt.js +5 -0
  212. package/dist/translations/lt.umd.js +11 -0
  213. package/dist/translations/lv.d.ts +8 -0
  214. package/dist/translations/lv.js +5 -0
  215. package/dist/translations/lv.umd.js +11 -0
  216. package/dist/translations/ms.d.ts +8 -0
  217. package/dist/translations/ms.js +5 -0
  218. package/dist/translations/ms.umd.js +11 -0
  219. package/dist/translations/nb.d.ts +8 -0
  220. package/dist/translations/nb.js +5 -0
  221. package/dist/translations/nb.umd.js +11 -0
  222. package/dist/translations/ne.d.ts +8 -0
  223. package/dist/translations/ne.js +5 -0
  224. package/dist/translations/ne.umd.js +11 -0
  225. package/dist/translations/nl.d.ts +8 -0
  226. package/dist/translations/nl.js +5 -0
  227. package/dist/translations/nl.umd.js +11 -0
  228. package/dist/translations/no.d.ts +8 -0
  229. package/dist/translations/no.js +5 -0
  230. package/dist/translations/no.umd.js +11 -0
  231. package/dist/translations/oc.d.ts +8 -0
  232. package/dist/translations/oc.js +5 -0
  233. package/dist/translations/oc.umd.js +11 -0
  234. package/dist/translations/pl.d.ts +8 -0
  235. package/dist/translations/pl.js +5 -0
  236. package/dist/translations/pl.umd.js +11 -0
  237. package/dist/translations/pt-br.d.ts +8 -0
  238. package/dist/translations/pt-br.js +5 -0
  239. package/dist/translations/pt-br.umd.js +11 -0
  240. package/dist/translations/pt.d.ts +8 -0
  241. package/dist/translations/pt.js +5 -0
  242. package/dist/translations/pt.umd.js +11 -0
  243. package/dist/translations/ro.d.ts +8 -0
  244. package/dist/translations/ro.js +5 -0
  245. package/dist/translations/ro.umd.js +11 -0
  246. package/dist/translations/ru.d.ts +8 -0
  247. package/dist/translations/ru.js +5 -0
  248. package/dist/translations/ru.umd.js +11 -0
  249. package/dist/translations/si.d.ts +8 -0
  250. package/dist/translations/si.js +5 -0
  251. package/dist/translations/si.umd.js +11 -0
  252. package/dist/translations/sk.d.ts +8 -0
  253. package/dist/translations/sk.js +5 -0
  254. package/dist/translations/sk.umd.js +11 -0
  255. package/dist/translations/sl.d.ts +8 -0
  256. package/dist/translations/sl.js +5 -0
  257. package/dist/translations/sl.umd.js +11 -0
  258. package/dist/translations/sq.d.ts +8 -0
  259. package/dist/translations/sq.js +5 -0
  260. package/dist/translations/sq.umd.js +11 -0
  261. package/dist/translations/sr-latn.d.ts +8 -0
  262. package/dist/translations/sr-latn.js +5 -0
  263. package/dist/translations/sr-latn.umd.js +11 -0
  264. package/dist/translations/sr.d.ts +8 -0
  265. package/dist/translations/sr.js +5 -0
  266. package/dist/translations/sr.umd.js +11 -0
  267. package/dist/translations/sv.d.ts +8 -0
  268. package/dist/translations/sv.js +5 -0
  269. package/dist/translations/sv.umd.js +11 -0
  270. package/dist/translations/th.d.ts +8 -0
  271. package/dist/translations/th.js +5 -0
  272. package/dist/translations/th.umd.js +11 -0
  273. package/dist/translations/ti.d.ts +8 -0
  274. package/dist/translations/ti.js +5 -0
  275. package/dist/translations/ti.umd.js +11 -0
  276. package/dist/translations/tk.d.ts +8 -0
  277. package/dist/translations/tk.js +5 -0
  278. package/dist/translations/tk.umd.js +11 -0
  279. package/dist/translations/tr.d.ts +8 -0
  280. package/dist/translations/tr.js +5 -0
  281. package/dist/translations/tr.umd.js +11 -0
  282. package/dist/translations/tt.d.ts +8 -0
  283. package/dist/translations/tt.js +5 -0
  284. package/dist/translations/tt.umd.js +11 -0
  285. package/dist/translations/ug.d.ts +8 -0
  286. package/dist/translations/ug.js +5 -0
  287. package/dist/translations/ug.umd.js +11 -0
  288. package/dist/translations/uk.d.ts +8 -0
  289. package/dist/translations/uk.js +5 -0
  290. package/dist/translations/uk.umd.js +11 -0
  291. package/dist/translations/ur.d.ts +8 -0
  292. package/dist/translations/ur.js +5 -0
  293. package/dist/translations/ur.umd.js +11 -0
  294. package/dist/translations/uz.d.ts +8 -0
  295. package/dist/translations/uz.js +5 -0
  296. package/dist/translations/uz.umd.js +11 -0
  297. package/dist/translations/vi.d.ts +8 -0
  298. package/dist/translations/vi.js +5 -0
  299. package/dist/translations/vi.umd.js +11 -0
  300. package/dist/translations/zh-cn.d.ts +8 -0
  301. package/dist/translations/zh-cn.js +5 -0
  302. package/dist/translations/zh-cn.umd.js +11 -0
  303. package/dist/translations/zh.d.ts +8 -0
  304. package/dist/translations/zh.js +5 -0
  305. package/dist/translations/zh.umd.js +11 -0
  306. package/dist/ui/bookmarkactionsview.d.ts +106 -0
  307. package/dist/ui/bookmarkformview.d.ts +122 -0
  308. package/dist/updatebookmarkcommand.d.ts +46 -0
  309. package/dist/utils.d.ts +15 -0
  310. package/lang/contexts.json +13 -0
  311. package/lang/translations/af.po +56 -0
  312. package/lang/translations/ar.po +56 -0
  313. package/lang/translations/ast.po +56 -0
  314. package/lang/translations/az.po +56 -0
  315. package/lang/translations/bg.po +56 -0
  316. package/lang/translations/bn.po +56 -0
  317. package/lang/translations/bs.po +56 -0
  318. package/lang/translations/ca.po +56 -0
  319. package/lang/translations/cs.po +56 -0
  320. package/lang/translations/da.po +56 -0
  321. package/lang/translations/de-ch.po +56 -0
  322. package/lang/translations/de.po +56 -0
  323. package/lang/translations/el.po +56 -0
  324. package/lang/translations/en-au.po +56 -0
  325. package/lang/translations/en-gb.po +56 -0
  326. package/lang/translations/en.po +56 -0
  327. package/lang/translations/eo.po +56 -0
  328. package/lang/translations/es-co.po +56 -0
  329. package/lang/translations/es.po +56 -0
  330. package/lang/translations/et.po +56 -0
  331. package/lang/translations/eu.po +56 -0
  332. package/lang/translations/fa.po +56 -0
  333. package/lang/translations/fi.po +56 -0
  334. package/lang/translations/fr.po +56 -0
  335. package/lang/translations/gl.po +56 -0
  336. package/lang/translations/gu.po +56 -0
  337. package/lang/translations/he.po +56 -0
  338. package/lang/translations/hi.po +56 -0
  339. package/lang/translations/hr.po +56 -0
  340. package/lang/translations/hu.po +56 -0
  341. package/lang/translations/hy.po +56 -0
  342. package/lang/translations/id.po +56 -0
  343. package/lang/translations/it.po +56 -0
  344. package/lang/translations/ja.po +56 -0
  345. package/lang/translations/jv.po +56 -0
  346. package/lang/translations/kk.po +56 -0
  347. package/lang/translations/km.po +56 -0
  348. package/lang/translations/kn.po +56 -0
  349. package/lang/translations/ko.po +56 -0
  350. package/lang/translations/ku.po +56 -0
  351. package/lang/translations/lt.po +56 -0
  352. package/lang/translations/lv.po +56 -0
  353. package/lang/translations/ms.po +56 -0
  354. package/lang/translations/nb.po +56 -0
  355. package/lang/translations/ne.po +56 -0
  356. package/lang/translations/nl.po +56 -0
  357. package/lang/translations/no.po +56 -0
  358. package/lang/translations/oc.po +56 -0
  359. package/lang/translations/pl.po +56 -0
  360. package/lang/translations/pt-br.po +56 -0
  361. package/lang/translations/pt.po +56 -0
  362. package/lang/translations/ro.po +56 -0
  363. package/lang/translations/ru.po +56 -0
  364. package/lang/translations/si.po +56 -0
  365. package/lang/translations/sk.po +56 -0
  366. package/lang/translations/sl.po +56 -0
  367. package/lang/translations/sq.po +56 -0
  368. package/lang/translations/sr-latn.po +56 -0
  369. package/lang/translations/sr.po +56 -0
  370. package/lang/translations/sv.po +56 -0
  371. package/lang/translations/th.po +56 -0
  372. package/lang/translations/ti.po +56 -0
  373. package/lang/translations/tk.po +56 -0
  374. package/lang/translations/tr.po +56 -0
  375. package/lang/translations/tt.po +56 -0
  376. package/lang/translations/ug.po +56 -0
  377. package/lang/translations/uk.po +56 -0
  378. package/lang/translations/ur.po +56 -0
  379. package/lang/translations/uz.po +56 -0
  380. package/lang/translations/vi.po +56 -0
  381. package/lang/translations/zh-cn.po +56 -0
  382. package/lang/translations/zh.po +56 -0
  383. package/package.json +32 -3
  384. package/src/augmentation.d.ts +24 -0
  385. package/src/augmentation.js +5 -0
  386. package/src/bookmark.d.ts +30 -0
  387. package/src/bookmark.js +36 -0
  388. package/src/bookmarkconfig.d.ts +48 -0
  389. package/src/bookmarkconfig.js +5 -0
  390. package/src/bookmarkediting.d.ts +51 -0
  391. package/src/bookmarkediting.js +211 -0
  392. package/src/bookmarkui.d.ts +166 -0
  393. package/src/bookmarkui.js +582 -0
  394. package/src/index.d.ts +14 -0
  395. package/src/index.js +13 -0
  396. package/src/insertbookmarkcommand.d.ts +38 -0
  397. package/src/insertbookmarkcommand.js +113 -0
  398. package/src/ui/bookmarkactionsview.d.ts +102 -0
  399. package/src/ui/bookmarkactionsview.js +154 -0
  400. package/src/ui/bookmarkformview.d.ts +118 -0
  401. package/src/ui/bookmarkformview.js +203 -0
  402. package/src/updatebookmarkcommand.d.ts +42 -0
  403. package/src/updatebookmarkcommand.js +75 -0
  404. package/src/utils.d.ts +11 -0
  405. package/src/utils.js +19 -0
  406. package/theme/bookmark.css +50 -0
  407. package/theme/bookmarkactions.css +44 -0
  408. package/theme/bookmarkform.css +42 -0
package/dist/index.js ADDED
@@ -0,0 +1,1320 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { icons, Command, Plugin } from '@ckeditor/ckeditor5-core/dist/index.js';
6
+ import { toWidget, Widget } from '@ckeditor/ckeditor5-widget/dist/index.js';
7
+ import { View, ViewCollection, FocusCycler, submitHandler, FormHeaderView, LabeledFieldView, createLabeledInputText, ButtonView, LabelView, IconView, ContextualBalloon, CssTransitionDisablerMixin, MenuBarMenuListItemButtonView, clickOutsideHandler } from '@ckeditor/ckeditor5-ui/dist/index.js';
8
+ import { ClickObserver } from '@ckeditor/ckeditor5-engine/dist/index.js';
9
+ import { FocusTracker, KeystrokeHandler, logWarning } from '@ckeditor/ckeditor5-utils/dist/index.js';
10
+
11
+ /**
12
+ * The bookmark form view controller class.
13
+ *
14
+ * See {@link module:bookmark/ui/bookmarkformview~BookmarkFormView}.
15
+ */ class BookmarkFormView extends View {
16
+ /**
17
+ * Tracks information about DOM focus in the form.
18
+ */ focusTracker = new FocusTracker();
19
+ /**
20
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
21
+ */ keystrokes = new KeystrokeHandler();
22
+ /**
23
+ * The ID input view.
24
+ */ idInputView;
25
+ /**
26
+ * The Submit button view.
27
+ */ buttonView;
28
+ /**
29
+ * A collection of form child views in the form.
30
+ */ children;
31
+ /**
32
+ * An array of form validators used by {@link #isValid}.
33
+ */ _validators;
34
+ /**
35
+ * A collection of views that can be focused in the form.
36
+ */ _focusables = new ViewCollection();
37
+ /**
38
+ * Helps cycling over {@link #_focusables} in the form.
39
+ */ _focusCycler;
40
+ /**
41
+ * Creates an instance of the {@link module:bookmark/ui/bookmarkformview~BookmarkFormView} class.
42
+ *
43
+ * Also see {@link #render}.
44
+ *
45
+ * @param locale The localization services instance.
46
+ * @param validators Form validators used by {@link #isValid}.
47
+ */ constructor(locale, validators){
48
+ super(locale);
49
+ const t = locale.t;
50
+ this._validators = validators;
51
+ this.idInputView = this._createIdInput();
52
+ this.buttonView = this._createButton(t('Insert'), 'ck-button-action ck-button-bold');
53
+ this.buttonView.type = 'submit';
54
+ this.children = this._createViewChildren();
55
+ this._focusCycler = new FocusCycler({
56
+ focusables: this._focusables,
57
+ focusTracker: this.focusTracker,
58
+ keystrokeHandler: this.keystrokes,
59
+ actions: {
60
+ // Navigate form fields backwards using the Shift + Tab keystroke.
61
+ focusPrevious: 'shift + tab',
62
+ // Navigate form fields forwards using the Tab key.
63
+ focusNext: 'tab'
64
+ }
65
+ });
66
+ const classList = [
67
+ 'ck',
68
+ 'ck-bookmark-view'
69
+ ];
70
+ this.setTemplate({
71
+ tag: 'form',
72
+ attributes: {
73
+ class: classList,
74
+ // https://github.com/ckeditor/ckeditor5-link/issues/90
75
+ tabindex: '-1'
76
+ },
77
+ children: this.children
78
+ });
79
+ }
80
+ /**
81
+ * @inheritDoc
82
+ */ render() {
83
+ super.render();
84
+ submitHandler({
85
+ view: this
86
+ });
87
+ const childViews = [
88
+ this.idInputView,
89
+ this.buttonView
90
+ ];
91
+ childViews.forEach((v)=>{
92
+ // Register the view as focusable.
93
+ this._focusables.add(v);
94
+ // Register the view in the focus tracker.
95
+ this.focusTracker.add(v.element);
96
+ });
97
+ // Start listening for the keystrokes coming from #element.
98
+ this.keystrokes.listenTo(this.element);
99
+ }
100
+ /**
101
+ * @inheritDoc
102
+ */ destroy() {
103
+ super.destroy();
104
+ this.focusTracker.destroy();
105
+ this.keystrokes.destroy();
106
+ }
107
+ /**
108
+ * Focuses the fist {@link #_focusables} in the form.
109
+ */ focus() {
110
+ this._focusCycler.focusFirst();
111
+ }
112
+ /**
113
+ * Validates the form and returns `false` when some fields are invalid.
114
+ */ isValid() {
115
+ this.resetFormStatus();
116
+ for (const validator of this._validators){
117
+ const errorText = validator(this);
118
+ // One error per field is enough.
119
+ if (errorText) {
120
+ // Apply updated error.
121
+ this.idInputView.errorText = errorText;
122
+ return false;
123
+ }
124
+ }
125
+ return true;
126
+ }
127
+ /**
128
+ * Cleans up the supplementary error and information text of the {@link #idInputView}
129
+ * bringing them back to the state when the form has been displayed for the first time.
130
+ *
131
+ * See {@link #isValid}.
132
+ */ resetFormStatus() {
133
+ this.idInputView.errorText = null;
134
+ }
135
+ /**
136
+ * Creates header and form view.
137
+ */ _createViewChildren() {
138
+ const children = this.createCollection();
139
+ const t = this.t;
140
+ children.add(new FormHeaderView(this.locale, {
141
+ label: t('Bookmark')
142
+ }));
143
+ children.add(this._createFormContentView());
144
+ return children;
145
+ }
146
+ /**
147
+ * Creates form content view with input and button.
148
+ */ _createFormContentView() {
149
+ const view = new View(this.locale);
150
+ const children = this.createCollection();
151
+ const classList = [
152
+ 'ck',
153
+ 'ck-bookmark-form',
154
+ 'ck-responsive-form'
155
+ ];
156
+ children.add(this.idInputView);
157
+ children.add(this.buttonView);
158
+ view.setTemplate({
159
+ tag: 'div',
160
+ attributes: {
161
+ class: classList
162
+ },
163
+ children
164
+ });
165
+ return view;
166
+ }
167
+ /**
168
+ * Creates a labeled input view.
169
+ *
170
+ * @returns Labeled field view instance.
171
+ */ _createIdInput() {
172
+ const t = this.locale.t;
173
+ const labeledInput = new LabeledFieldView(this.locale, createLabeledInputText);
174
+ labeledInput.label = t('Bookmark name');
175
+ labeledInput.infoText = t('Enter the bookmark name without spaces.');
176
+ return labeledInput;
177
+ }
178
+ /**
179
+ * Creates a button view.
180
+ *
181
+ * @param label The button label.
182
+ * @param className The additional button CSS class name.
183
+ * @returns The button view instance.
184
+ */ _createButton(label, className) {
185
+ const button = new ButtonView(this.locale);
186
+ button.set({
187
+ label,
188
+ withText: true
189
+ });
190
+ button.extendTemplate({
191
+ attributes: {
192
+ class: className
193
+ }
194
+ });
195
+ return button;
196
+ }
197
+ /**
198
+ * The native DOM `value` of the {@link #idInputView} element.
199
+ *
200
+ * **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
201
+ * which works one way only and may not represent the actual state of the component in the DOM.
202
+ */ get id() {
203
+ const { element } = this.idInputView.fieldView;
204
+ if (!element) {
205
+ return null;
206
+ }
207
+ return element.value.trim();
208
+ }
209
+ }
210
+
211
+ /**
212
+ * The bookmark actions view class. This view displays the bookmark preview, allows
213
+ * removing or editing the bookmark.
214
+ */ class BookmarkActionsView extends View {
215
+ /**
216
+ * Tracks information about DOM focus in the actions.
217
+ */ focusTracker = new FocusTracker();
218
+ /**
219
+ * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
220
+ */ keystrokes = new KeystrokeHandler();
221
+ /**
222
+ * The bookmark preview view.
223
+ */ bookmarkPreviewView;
224
+ /**
225
+ * The remove button view.
226
+ */ removeButtonView;
227
+ /**
228
+ * The edit bookmark button view.
229
+ */ editButtonView;
230
+ /**
231
+ * A collection of views that can be focused in the view.
232
+ */ _focusables = new ViewCollection();
233
+ /**
234
+ * Helps cycling over {@link #_focusables} in the view.
235
+ */ _focusCycler;
236
+ /**
237
+ * @inheritDoc
238
+ */ constructor(locale){
239
+ super(locale);
240
+ const t = locale.t;
241
+ this.bookmarkPreviewView = this._createBookmarkPreviewView();
242
+ this.removeButtonView = this._createButton(t('Remove bookmark'), icons.remove, 'remove', this.bookmarkPreviewView);
243
+ this.editButtonView = this._createButton(t('Edit bookmark'), icons.pencil, 'edit', this.bookmarkPreviewView);
244
+ this.set('id', undefined);
245
+ this._focusCycler = new FocusCycler({
246
+ focusables: this._focusables,
247
+ focusTracker: this.focusTracker,
248
+ keystrokeHandler: this.keystrokes,
249
+ actions: {
250
+ // Navigate fields backwards using the Shift + Tab keystroke.
251
+ focusPrevious: 'shift + tab',
252
+ // Navigate fields forwards using the Tab key.
253
+ focusNext: 'tab'
254
+ }
255
+ });
256
+ this.setTemplate({
257
+ tag: 'div',
258
+ attributes: {
259
+ class: [
260
+ 'ck',
261
+ 'ck-bookmark-actions',
262
+ 'ck-responsive-form'
263
+ ],
264
+ // https://github.com/ckeditor/ckeditor5-link/issues/90
265
+ tabindex: '-1'
266
+ },
267
+ children: [
268
+ this.bookmarkPreviewView,
269
+ this.editButtonView,
270
+ this.removeButtonView
271
+ ]
272
+ });
273
+ }
274
+ /**
275
+ * @inheritDoc
276
+ */ render() {
277
+ super.render();
278
+ const childViews = [
279
+ this.editButtonView,
280
+ this.removeButtonView
281
+ ];
282
+ childViews.forEach((v)=>{
283
+ // Register the view as focusable.
284
+ this._focusables.add(v);
285
+ // Register the view in the focus tracker.
286
+ this.focusTracker.add(v.element);
287
+ });
288
+ // Start listening for the keystrokes coming from #element.
289
+ this.keystrokes.listenTo(this.element);
290
+ }
291
+ /**
292
+ * @inheritDoc
293
+ */ destroy() {
294
+ super.destroy();
295
+ this.focusTracker.destroy();
296
+ this.keystrokes.destroy();
297
+ }
298
+ /**
299
+ * Focuses the fist {@link #_focusables} in the actions.
300
+ */ focus() {
301
+ this._focusCycler.focusFirst();
302
+ }
303
+ /**
304
+ * Creates a button view.
305
+ *
306
+ * @param label The button label.
307
+ * @param icon The button icon.
308
+ * @param eventName An event name that the `ButtonView#execute` event will be delegated to.
309
+ * @param additionalLabel An additional label outside the button.
310
+ * @returns The button view instance.
311
+ */ _createButton(label, icon, eventName, additionalLabel) {
312
+ const button = new ButtonView(this.locale);
313
+ button.set({
314
+ label,
315
+ icon,
316
+ tooltip: true
317
+ });
318
+ button.delegate('execute').to(this, eventName);
319
+ // Since button label `id` is bound to the `ariaLabelledBy` property
320
+ // we need to modify this binding to include only the first ID token
321
+ // as this button will be labeled by multiple labels.
322
+ button.labelView.unbind('id');
323
+ button.labelView.bind('id').to(button, 'ariaLabelledBy', (ariaLabelledBy)=>{
324
+ return getFirstToken(ariaLabelledBy);
325
+ });
326
+ button.ariaLabelledBy = `${button.ariaLabelledBy} ${additionalLabel.id}`;
327
+ return button;
328
+ }
329
+ /**
330
+ * Creates a bookmark name preview label.
331
+ *
332
+ * @returns The label view instance.
333
+ */ _createBookmarkPreviewView() {
334
+ const label = new LabelView(this.locale);
335
+ label.extendTemplate({
336
+ attributes: {
337
+ class: [
338
+ 'ck',
339
+ 'ck-bookmark-actions__preview'
340
+ ]
341
+ }
342
+ });
343
+ // Bind label text with the bookmark ID.
344
+ label.bind('text').to(this, 'id');
345
+ return label;
346
+ }
347
+ }
348
+ /**
349
+ * Returns the first token from space separated token list.
350
+ */ function getFirstToken(tokenList) {
351
+ return tokenList.split(' ')[0];
352
+ }
353
+
354
+ /**
355
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
356
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
357
+ */ /**
358
+ * @module bookmark/utils
359
+ */ /**
360
+ * Returns `true` if the bookmark id is valid; otherwise, returns `false`.
361
+ */ function isBookmarkIdValid(id) {
362
+ if (!id || typeof id !== 'string') {
363
+ return false;
364
+ }
365
+ if (/\s/.test(id)) {
366
+ return false;
367
+ }
368
+ return true;
369
+ }
370
+
371
+ /**
372
+ * The insert bookmark command.
373
+ *
374
+ * The command is registered by {@link module:bookmark/bookmarkediting~BookmarkEditing} as `'insertBookmark'`.
375
+ *
376
+ * To insert a bookmark element at place where is the current collapsed selection or where is the beginning of document selection,
377
+ * execute the command passing the bookmark id as a parameter:
378
+ *
379
+ * ```ts
380
+ * editor.execute( 'insertBookmark', { bookmarkId: 'foo_bar' } );
381
+ * ```
382
+ */ class InsertBookmarkCommand extends Command {
383
+ /**
384
+ * @inheritDoc
385
+ */ refresh() {
386
+ const model = this.editor.model;
387
+ const selection = model.document.selection;
388
+ const position = this._getPositionToInsertBookmark(selection);
389
+ this.isEnabled = !!position;
390
+ }
391
+ /**
392
+ * Executes the command.
393
+ *
394
+ * @fires execute
395
+ * @param options Command options.
396
+ * @param options.bookmarkId The value of the `bookmarkId` attribute.
397
+ */ execute(options) {
398
+ if (!options) {
399
+ return;
400
+ }
401
+ const { bookmarkId } = options;
402
+ if (!isBookmarkIdValid(bookmarkId)) {
403
+ /**
404
+ * Insert bookmark command can be executed only with a valid name.
405
+ *
406
+ * A valid bookmark name must be a non-empty string and must not contain any spaces.
407
+ *
408
+ * @error insert-bookmark-command-executed-with-invalid-name
409
+ */ logWarning('insert-bookmark-command-executed-with-invalid-name');
410
+ return;
411
+ }
412
+ const editor = this.editor;
413
+ const model = editor.model;
414
+ const selection = model.document.selection;
415
+ model.change((writer)=>{
416
+ let position = this._getPositionToInsertBookmark(selection);
417
+ const isBookmarkAllowed = model.schema.checkChild(position, 'bookmark');
418
+ // If the position does not allow for `bookmark` but allows for a `paragraph`
419
+ // then insert a `paragraph` then we will insert a `bookmark` inside.
420
+ if (!isBookmarkAllowed) {
421
+ const newPosition = editor.execute('insertParagraph', {
422
+ position
423
+ });
424
+ if (!newPosition) {
425
+ return;
426
+ }
427
+ position = newPosition;
428
+ }
429
+ const bookmarkElement = writer.createElement('bookmark', {
430
+ ...Object.fromEntries(selection.getAttributes()),
431
+ bookmarkId
432
+ });
433
+ model.insertObject(bookmarkElement, position, null, {
434
+ setSelection: 'on'
435
+ });
436
+ });
437
+ }
438
+ /**
439
+ * Returns the position where the bookmark can be inserted. And if it is not possible to insert a bookmark,
440
+ * check if it is possible to insert a paragraph.
441
+ */ _getPositionToInsertBookmark(selection) {
442
+ const model = this.editor.model;
443
+ const schema = model.schema;
444
+ const firstRange = selection.getFirstRange();
445
+ const startPosition = firstRange.start;
446
+ // Return position if it is allowed to insert bookmark or if it is allowed to insert paragraph.
447
+ if (isBookmarkAllowed(startPosition, schema)) {
448
+ return startPosition;
449
+ }
450
+ for (const { previousPosition, item } of firstRange){
451
+ // When the table cell is selected (from the outside) we look for the first paragraph-like element inside.
452
+ if (item.is('element') && schema.checkChild(item, '$text') && isBookmarkAllowed(item, schema)) {
453
+ return model.createPositionAt(item, 0);
454
+ }
455
+ if (isBookmarkAllowed(previousPosition, schema)) {
456
+ return previousPosition;
457
+ }
458
+ }
459
+ return null;
460
+ }
461
+ }
462
+ /**
463
+ * Verify if the given position allows for bookmark insertion. Verify if auto-paragraphing could help.
464
+ */ function isBookmarkAllowed(position, schema) {
465
+ if (schema.checkChild(position, 'bookmark')) {
466
+ return true;
467
+ }
468
+ if (!schema.checkChild(position, 'paragraph')) {
469
+ return false;
470
+ }
471
+ return schema.checkChild('paragraph', 'bookmark');
472
+ }
473
+
474
+ /**
475
+ * The update bookmark command.
476
+ *
477
+ * The command is registered by {@link module:bookmark/bookmarkediting~BookmarkEditing} as `'updateBookmark'`.
478
+ *
479
+ * To update the `bookmarkId` of current selected bookmark element, execute the command passing the bookmark id as a parameter:
480
+ *
481
+ * ```ts
482
+ * editor.execute( 'updateBookmark', { bookmarkId: 'newId' } );
483
+ * ```
484
+ */ class UpdateBookmarkCommand extends Command {
485
+ /**
486
+ * @inheritDoc
487
+ */ refresh() {
488
+ const model = this.editor.model;
489
+ const selection = model.document.selection;
490
+ const selectedBookmark = getSelectedBookmark(selection);
491
+ this.isEnabled = !!selectedBookmark;
492
+ this.value = selectedBookmark ? selectedBookmark.getAttribute('bookmarkId') : undefined;
493
+ }
494
+ /**
495
+ * Executes the command.
496
+ *
497
+ * @fires execute
498
+ * @param options Command options.
499
+ * @param options.bookmarkId The new value of the `bookmarkId` attribute to set.
500
+ */ execute(options) {
501
+ if (!options) {
502
+ return;
503
+ }
504
+ const { bookmarkId } = options;
505
+ if (!isBookmarkIdValid(bookmarkId)) {
506
+ /**
507
+ * Update bookmark command can be executed only with a valid name.
508
+ *
509
+ * A valid bookmark name must be a non-empty string and must not contain any spaces.
510
+ *
511
+ * @error update-bookmark-command-executed-with-invalid-name
512
+ */ logWarning('update-bookmark-command-executed-with-invalid-name');
513
+ return;
514
+ }
515
+ const model = this.editor.model;
516
+ const selection = model.document.selection;
517
+ const selectedBookmark = getSelectedBookmark(selection);
518
+ if (selectedBookmark) {
519
+ model.change((writer)=>{
520
+ writer.setAttribute('bookmarkId', bookmarkId, selectedBookmark);
521
+ });
522
+ }
523
+ }
524
+ }
525
+ /**
526
+ * Returns the selected `bookmark` element in the model, if any.
527
+ */ function getSelectedBookmark(selection) {
528
+ const element = selection.getSelectedElement();
529
+ if (!!element && element.is('element', 'bookmark')) {
530
+ return element;
531
+ }
532
+ return null;
533
+ }
534
+
535
+ /**
536
+ * The bookmark editing plugin.
537
+ */ class BookmarkEditing extends Plugin {
538
+ /**
539
+ * A collection of bookmarks elements in the document.
540
+ */ _bookmarkElements = new Map();
541
+ /**
542
+ * @inheritDoc
543
+ */ static get pluginName() {
544
+ return 'BookmarkEditing';
545
+ }
546
+ /**
547
+ * @inheritDoc
548
+ */ static get isOfficialPlugin() {
549
+ return true;
550
+ }
551
+ /**
552
+ * @inheritDoc
553
+ */ init() {
554
+ const { editor } = this;
555
+ this._defineSchema();
556
+ this._defineConverters();
557
+ editor.commands.add('insertBookmark', new InsertBookmarkCommand(editor));
558
+ editor.commands.add('updateBookmark', new UpdateBookmarkCommand(editor));
559
+ this.listenTo(editor.model.document, 'change:data', ()=>{
560
+ this._trackBookmarkElements();
561
+ });
562
+ }
563
+ /**
564
+ * Returns the model element for the given bookmark ID if it exists.
565
+ */ getElementForBookmarkId(bookmarkId) {
566
+ for (const [element, id] of this._bookmarkElements){
567
+ if (id == bookmarkId) {
568
+ return element;
569
+ }
570
+ }
571
+ return null;
572
+ }
573
+ /**
574
+ * Defines the schema for the bookmark feature.
575
+ */ _defineSchema() {
576
+ const schema = this.editor.model.schema;
577
+ schema.register('bookmark', {
578
+ inheritAllFrom: '$inlineObject',
579
+ allowAttributes: 'bookmarkId',
580
+ disallowAttributes: [
581
+ 'linkHref',
582
+ 'htmlA'
583
+ ]
584
+ });
585
+ }
586
+ /**
587
+ * Defines the converters for the bookmark feature.
588
+ */ _defineConverters() {
589
+ const { editor } = this;
590
+ const { conversion, t } = editor;
591
+ editor.data.htmlProcessor.domConverter.registerInlineObjectMatcher((element)=>upcastMatcher(element));
592
+ // Register an inline object matcher so that bookmarks <a>s are correctly recognized as inline elements in editing pipeline.
593
+ // This prevents converting spaces around bookmarks to `&nbsp;`s.
594
+ editor.editing.view.domConverter.registerInlineObjectMatcher((element)=>upcastMatcher(element, false));
595
+ conversion.for('dataDowncast').elementToElement({
596
+ model: {
597
+ name: 'bookmark',
598
+ attributes: [
599
+ 'bookmarkId'
600
+ ]
601
+ },
602
+ view: (modelElement, { writer })=>{
603
+ const emptyElement = writer.createEmptyElement('a', {
604
+ 'id': modelElement.getAttribute('bookmarkId')
605
+ });
606
+ // `getFillerOffset` is not needed to set here, because `emptyElement` has already covered it.
607
+ return emptyElement;
608
+ }
609
+ });
610
+ conversion.for('editingDowncast').elementToElement({
611
+ model: {
612
+ name: 'bookmark',
613
+ attributes: [
614
+ 'bookmarkId'
615
+ ]
616
+ },
617
+ view: (modelElement, { writer })=>{
618
+ const id = modelElement.getAttribute('bookmarkId');
619
+ const containerElement = writer.createContainerElement('a', {
620
+ id,
621
+ class: 'ck-bookmark'
622
+ }, [
623
+ this._createBookmarkUIElement(writer)
624
+ ]);
625
+ this._bookmarkElements.set(modelElement, id);
626
+ // `getFillerOffset` is not needed to set here, because `toWidget` has already covered it.
627
+ const labelCreator = ()=>`${id} ${t('bookmark widget')}`;
628
+ return toWidget(containerElement, writer, {
629
+ label: labelCreator
630
+ });
631
+ }
632
+ });
633
+ conversion.for('upcast').add((dispatcher)=>dispatcher.on('element:a', dataViewModelAnchorInsertion(editor)));
634
+ }
635
+ /**
636
+ * Creates a UI element for the `bookmark` representation in editing view.
637
+ */ _createBookmarkUIElement(writer) {
638
+ return writer.createUIElement('span', {
639
+ class: 'ck-bookmark__icon'
640
+ }, function(domDocument) {
641
+ const domElement = this.toDomElement(domDocument);
642
+ const icon = new IconView();
643
+ icon.set({
644
+ content: icons.bookmarkInline,
645
+ isColorInherited: false
646
+ });
647
+ icon.render();
648
+ domElement.appendChild(icon.element);
649
+ return domElement;
650
+ });
651
+ }
652
+ /**
653
+ * Tracking the added or removed bookmark elements.
654
+ */ _trackBookmarkElements() {
655
+ this._bookmarkElements.forEach((id, element)=>{
656
+ if (element.root.rootName === '$graveyard') {
657
+ this._bookmarkElements.delete(element);
658
+ }
659
+ });
660
+ }
661
+ }
662
+ /**
663
+ * A helper function to match an `anchor` element which must contain `id` or `name` attribute but without `href` attribute,
664
+ * also when `expectEmpty` is set to `true` but the element is not empty matcher should not match any element.
665
+ *
666
+ * @param element The element to be checked.
667
+ * @param expectEmpty Default set to `true`, when set to `false` matcher expects that `anchor` is not empty;
668
+ * in editing pipeline it's not empty because it contains the `UIElement`.
669
+ */ function upcastMatcher(element, expectEmpty = true) {
670
+ const isAnchorElement = element.name === 'a';
671
+ if (!isAnchorElement) {
672
+ return null;
673
+ }
674
+ if (expectEmpty && !element.isEmpty) {
675
+ return null;
676
+ }
677
+ const hasIdAttribute = element.hasAttribute('id');
678
+ const hasNameAttribute = element.hasAttribute('name');
679
+ const hasHrefAttribute = element.hasAttribute('href');
680
+ if (hasIdAttribute && !hasHrefAttribute) {
681
+ return {
682
+ name: true,
683
+ attributes: [
684
+ 'id'
685
+ ]
686
+ };
687
+ }
688
+ if (hasNameAttribute && !hasHrefAttribute) {
689
+ return {
690
+ name: true,
691
+ attributes: [
692
+ 'name'
693
+ ]
694
+ };
695
+ }
696
+ return null;
697
+ }
698
+ /**
699
+ * A view-to-model converter that handles converting pointed or wrapped anchors with `id` and/or `name` attributes.
700
+ *
701
+ * @returns Returns a conversion callback.
702
+ */ function dataViewModelAnchorInsertion(editor) {
703
+ return (evt, data, conversionApi)=>{
704
+ const viewItem = data.viewItem;
705
+ const match = upcastMatcher(viewItem, false);
706
+ if (!match || !conversionApi.consumable.test(viewItem, match)) {
707
+ return;
708
+ }
709
+ const enableNonEmptyAnchorConversion = isEnabledNonEmptyAnchorConversion(editor);
710
+ if (!enableNonEmptyAnchorConversion && !viewItem.isEmpty) {
711
+ return;
712
+ }
713
+ const modelWriter = conversionApi.writer;
714
+ const anchorId = viewItem.getAttribute('id');
715
+ const anchorName = viewItem.getAttribute('name');
716
+ const bookmarkId = anchorId || anchorName;
717
+ const bookmark = modelWriter.createElement('bookmark', {
718
+ bookmarkId
719
+ });
720
+ if (!conversionApi.safeInsert(bookmark, data.modelCursor)) {
721
+ return;
722
+ }
723
+ conversionApi.consumable.consume(viewItem, match);
724
+ if (anchorId === anchorName) {
725
+ conversionApi.consumable.consume(viewItem, {
726
+ attributes: [
727
+ 'name'
728
+ ]
729
+ });
730
+ }
731
+ conversionApi.updateConversionResult(bookmark, data);
732
+ // Convert children uses the result of `bookmark` insertion to convert the `anchor` content
733
+ // after the bookmark element (not inside it).
734
+ const { modelCursor, modelRange } = conversionApi.convertChildren(viewItem, data.modelCursor);
735
+ data.modelCursor = modelCursor;
736
+ data.modelRange = modelWriter.createRange(data.modelRange.start, modelRange.end);
737
+ };
738
+ }
739
+ /**
740
+ * Normalize the bookmark configuration option `enableNonEmptyAnchorConversion`.
741
+ */ function isEnabledNonEmptyAnchorConversion(editor) {
742
+ const enableNonEmptyAnchorConversion = editor.config.get('bookmark.enableNonEmptyAnchorConversion');
743
+ // When not defined, option `enableNonEmptyAnchorConversion` by default is set to `true`.
744
+ return enableNonEmptyAnchorConversion !== undefined ? enableNonEmptyAnchorConversion : true;
745
+ }
746
+
747
+ const VISUAL_SELECTION_MARKER_NAME = 'bookmark-ui';
748
+ /**
749
+ * The UI plugin of the bookmark feature.
750
+ *
751
+ * It registers the `'bookmark'` UI button in the editor's {@link module:ui/componentfactory~ComponentFactory component factory}
752
+ * which inserts the `bookmark` element upon selection.
753
+ */ class BookmarkUI extends Plugin {
754
+ /**
755
+ * The actions view displayed inside of the balloon.
756
+ */ actionsView = null;
757
+ /**
758
+ * The form view displayed inside the balloon.
759
+ */ formView = null;
760
+ /**
761
+ * The contextual balloon plugin instance.
762
+ */ _balloon;
763
+ /**
764
+ * @inheritDoc
765
+ */ static get requires() {
766
+ return [
767
+ BookmarkEditing,
768
+ ContextualBalloon
769
+ ];
770
+ }
771
+ /**
772
+ * @inheritDoc
773
+ */ static get pluginName() {
774
+ return 'BookmarkUI';
775
+ }
776
+ /**
777
+ * @inheritDoc
778
+ */ static get isOfficialPlugin() {
779
+ return true;
780
+ }
781
+ /**
782
+ * @inheritDoc
783
+ */ init() {
784
+ const editor = this.editor;
785
+ editor.editing.view.addObserver(ClickObserver);
786
+ this._balloon = editor.plugins.get(ContextualBalloon);
787
+ // Create toolbar buttons.
788
+ this._createToolbarBookmarkButton();
789
+ this._enableBalloonActivators();
790
+ // Renders a fake visual selection marker on an expanded selection.
791
+ editor.conversion.for('editingDowncast').markerToHighlight({
792
+ model: VISUAL_SELECTION_MARKER_NAME,
793
+ view: {
794
+ classes: [
795
+ 'ck-fake-bookmark-selection'
796
+ ]
797
+ }
798
+ });
799
+ // Renders a fake visual selection marker on a collapsed selection.
800
+ editor.conversion.for('editingDowncast').markerToElement({
801
+ model: VISUAL_SELECTION_MARKER_NAME,
802
+ view: (data, { writer })=>{
803
+ if (!data.markerRange.isCollapsed) {
804
+ return null;
805
+ }
806
+ const markerElement = writer.createUIElement('span');
807
+ writer.addClass([
808
+ 'ck-fake-bookmark-selection',
809
+ 'ck-fake-bookmark-selection_collapsed'
810
+ ], markerElement);
811
+ return markerElement;
812
+ }
813
+ });
814
+ }
815
+ /**
816
+ * @inheritDoc
817
+ */ destroy() {
818
+ super.destroy();
819
+ // Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
820
+ if (this.formView) {
821
+ this.formView.destroy();
822
+ }
823
+ if (this.actionsView) {
824
+ this.actionsView.destroy();
825
+ }
826
+ }
827
+ /**
828
+ * Creates views.
829
+ */ _createViews() {
830
+ this.actionsView = this._createActionsView();
831
+ this.formView = this._createFormView();
832
+ // Attach lifecycle actions to the the balloon.
833
+ this._enableUserBalloonInteractions();
834
+ }
835
+ /**
836
+ * Creates the {@link module:bookmark/ui/bookmarkactionsview~BookmarkActionsView} instance.
837
+ */ _createActionsView() {
838
+ const editor = this.editor;
839
+ const actionsView = new BookmarkActionsView(editor.locale);
840
+ const updateBookmarkCommand = editor.commands.get('updateBookmark');
841
+ const deleteCommand = editor.commands.get('delete');
842
+ actionsView.bind('id').to(updateBookmarkCommand, 'value');
843
+ actionsView.editButtonView.bind('isEnabled').to(updateBookmarkCommand);
844
+ actionsView.removeButtonView.bind('isEnabled').to(deleteCommand);
845
+ // Display edit form view after clicking on the "Edit" button.
846
+ this.listenTo(actionsView, 'edit', ()=>{
847
+ this._addFormView();
848
+ });
849
+ // Execute remove command after clicking on the "Remove" button.
850
+ this.listenTo(actionsView, 'remove', ()=>{
851
+ this._hideUI();
852
+ editor.execute('delete');
853
+ });
854
+ // Close the panel on esc key press when the **actions have focus**.
855
+ actionsView.keystrokes.set('Esc', (data, cancel)=>{
856
+ this._hideUI();
857
+ cancel();
858
+ });
859
+ return actionsView;
860
+ }
861
+ /**
862
+ * Creates the {@link module:bookmark/ui/bookmarkformview~BookmarkFormView} instance.
863
+ */ _createFormView() {
864
+ const editor = this.editor;
865
+ const locale = editor.locale;
866
+ const insertBookmarkCommand = editor.commands.get('insertBookmark');
867
+ const updateBookmarkCommand = editor.commands.get('updateBookmark');
868
+ const commands = [
869
+ insertBookmarkCommand,
870
+ updateBookmarkCommand
871
+ ];
872
+ const formView = new (CssTransitionDisablerMixin(BookmarkFormView))(locale, getFormValidators(editor));
873
+ formView.idInputView.fieldView.bind('value').to(updateBookmarkCommand, 'value');
874
+ // Form elements should be read-only when corresponding commands are disabled.
875
+ formView.idInputView.bind('isEnabled').toMany(commands, 'isEnabled', (...areEnabled)=>areEnabled.some((isEnabled)=>isEnabled));
876
+ // Disable the "save" button if the command is disabled.
877
+ formView.buttonView.bind('isEnabled').toMany(commands, 'isEnabled', (...areEnabled)=>areEnabled.some((isEnabled)=>isEnabled));
878
+ // Execute link command after clicking the "Save" button.
879
+ this.listenTo(formView, 'submit', ()=>{
880
+ if (formView.isValid()) {
881
+ const value = formView.id;
882
+ if (this._getSelectedBookmarkElement()) {
883
+ editor.execute('updateBookmark', {
884
+ bookmarkId: value
885
+ });
886
+ } else {
887
+ editor.execute('insertBookmark', {
888
+ bookmarkId: value
889
+ });
890
+ }
891
+ this._closeFormView();
892
+ }
893
+ });
894
+ // Update balloon position when form error changes.
895
+ this.listenTo(formView.idInputView, 'change:errorText', ()=>{
896
+ editor.ui.update();
897
+ });
898
+ // Close the panel on esc key press when the **form has focus**.
899
+ formView.keystrokes.set('Esc', (data, cancel)=>{
900
+ this._closeFormView();
901
+ cancel();
902
+ });
903
+ return formView;
904
+ }
905
+ /**
906
+ * Creates a toolbar Bookmark button. Clicking this button will show
907
+ * a {@link #_balloon} attached to the selection.
908
+ */ _createToolbarBookmarkButton() {
909
+ const editor = this.editor;
910
+ editor.ui.componentFactory.add('bookmark', ()=>{
911
+ const buttonView = this._createButton(ButtonView);
912
+ buttonView.set({
913
+ tooltip: true
914
+ });
915
+ return buttonView;
916
+ });
917
+ editor.ui.componentFactory.add('menuBar:bookmark', ()=>{
918
+ return this._createButton(MenuBarMenuListItemButtonView);
919
+ });
920
+ }
921
+ /**
922
+ * Creates a button for `bookmark` command to use either in toolbar or in menu bar.
923
+ */ _createButton(ButtonClass) {
924
+ const editor = this.editor;
925
+ const locale = editor.locale;
926
+ const view = new ButtonClass(locale);
927
+ const insertCommand = editor.commands.get('insertBookmark');
928
+ const updateCommand = editor.commands.get('updateBookmark');
929
+ const t = locale.t;
930
+ view.set({
931
+ label: t('Bookmark'),
932
+ icon: icons.bookmark
933
+ });
934
+ // Execute the command.
935
+ this.listenTo(view, 'execute', ()=>this._showUI(true));
936
+ view.bind('isEnabled').toMany([
937
+ insertCommand,
938
+ updateCommand
939
+ ], 'isEnabled', (...areEnabled)=>areEnabled.some((isEnabled)=>isEnabled));
940
+ view.bind('isOn').to(updateCommand, 'value', (value)=>!!value);
941
+ return view;
942
+ }
943
+ /**
944
+ * Attaches actions that control whether the balloon panel containing the
945
+ * {@link #formView} should be displayed.
946
+ */ _enableBalloonActivators() {
947
+ const editor = this.editor;
948
+ const viewDocument = editor.editing.view.document;
949
+ // Handle click on view document and show panel when selection is placed inside the bookmark element.
950
+ // Keep panel open until selection will be inside the same bookmark element.
951
+ this.listenTo(viewDocument, 'click', ()=>{
952
+ const bookmark = this._getSelectedBookmarkElement();
953
+ if (bookmark) {
954
+ // Then show panel but keep focus inside editor editable.
955
+ this._showUI();
956
+ }
957
+ });
958
+ }
959
+ /**
960
+ * Attaches actions that control whether the balloon panel containing the
961
+ * {@link #formView} is visible or not.
962
+ */ _enableUserBalloonInteractions() {
963
+ // Focus the form if the balloon is visible and the Tab key has been pressed.
964
+ this.editor.keystrokes.set('Tab', (data, cancel)=>{
965
+ if (this._areActionsVisible && !this.actionsView.focusTracker.isFocused) {
966
+ this.actionsView.focus();
967
+ cancel();
968
+ }
969
+ }, {
970
+ // Use the high priority because the bookmark UI navigation is more important
971
+ // than other feature's actions, e.g. list indentation.
972
+ priority: 'high'
973
+ });
974
+ // Close the panel on the Esc key press when the editable has focus and the balloon is visible.
975
+ this.editor.keystrokes.set('Esc', (data, cancel)=>{
976
+ if (this._isUIVisible) {
977
+ this._hideUI();
978
+ cancel();
979
+ }
980
+ });
981
+ // Close on click outside of balloon panel element.
982
+ clickOutsideHandler({
983
+ emitter: this.formView,
984
+ activator: ()=>this._isUIInPanel,
985
+ contextElements: ()=>[
986
+ this._balloon.view.element
987
+ ],
988
+ callback: ()=>this._hideUI()
989
+ });
990
+ }
991
+ /**
992
+ * Updates the button label. If bookmark is selected label is set to 'Update' otherwise
993
+ * it is 'Insert'.
994
+ */ _updateFormButtonLabel(isBookmarkSelected) {
995
+ const t = this.editor.locale.t;
996
+ this.formView.buttonView.label = isBookmarkSelected ? t('Update') : t('Insert');
997
+ }
998
+ /**
999
+ * Adds the {@link #actionsView} to the {@link #_balloon}.
1000
+ *
1001
+ * @internal
1002
+ */ _addActionsView() {
1003
+ if (!this.actionsView) {
1004
+ this._createViews();
1005
+ }
1006
+ if (this._areActionsInPanel) {
1007
+ return;
1008
+ }
1009
+ this._balloon.add({
1010
+ view: this.actionsView,
1011
+ position: this._getBalloonPositionData()
1012
+ });
1013
+ }
1014
+ /**
1015
+ * Adds the {@link #formView} to the {@link #_balloon}.
1016
+ */ _addFormView() {
1017
+ if (!this.formView) {
1018
+ this._createViews();
1019
+ }
1020
+ if (this._isFormInPanel) {
1021
+ return;
1022
+ }
1023
+ const editor = this.editor;
1024
+ const updateBookmarkCommand = editor.commands.get('updateBookmark');
1025
+ this.formView.disableCssTransitions();
1026
+ this.formView.resetFormStatus();
1027
+ this._balloon.add({
1028
+ view: this.formView,
1029
+ position: this._getBalloonPositionData()
1030
+ });
1031
+ this.formView.idInputView.fieldView.value = updateBookmarkCommand.value || '';
1032
+ // Select input when form view is currently visible.
1033
+ if (this._balloon.visibleView === this.formView) {
1034
+ this.formView.idInputView.fieldView.select();
1035
+ }
1036
+ this.formView.enableCssTransitions();
1037
+ }
1038
+ /**
1039
+ * Closes the form view. Decides whether the balloon should be hidden completely.
1040
+ */ _closeFormView() {
1041
+ const updateBookmarkCommand = this.editor.commands.get('updateBookmark');
1042
+ if (updateBookmarkCommand.value !== undefined) {
1043
+ this._removeFormView();
1044
+ } else {
1045
+ this._hideUI();
1046
+ }
1047
+ }
1048
+ /**
1049
+ * Removes the {@link #formView} from the {@link #_balloon}.
1050
+ */ _removeFormView() {
1051
+ if (this._isFormInPanel) {
1052
+ // Blur the input element before removing it from DOM to prevent issues in some browsers.
1053
+ // See https://github.com/ckeditor/ckeditor5/issues/1501.
1054
+ this.formView.buttonView.focus();
1055
+ // Reset the ID field to update the state of the submit button.
1056
+ this.formView.idInputView.fieldView.reset();
1057
+ this._balloon.remove(this.formView);
1058
+ // Because the form has an input which has focus, the focus must be brought back
1059
+ // to the editor. Otherwise, it would be lost.
1060
+ this.editor.editing.view.focus();
1061
+ this._hideFakeVisualSelection();
1062
+ }
1063
+ }
1064
+ /**
1065
+ * Shows the correct UI type. It is either {@link #formView} or {@link #actionsView}.
1066
+ */ _showUI(forceVisible = false) {
1067
+ if (!this.formView) {
1068
+ this._createViews();
1069
+ }
1070
+ // When there's no bookmark under the selection, go straight to the editing UI.
1071
+ if (!this._getSelectedBookmarkElement()) {
1072
+ // Show visual selection on a text without a bookmark when the contextual balloon is displayed.
1073
+ this._showFakeVisualSelection();
1074
+ this._addActionsView();
1075
+ // Be sure panel with bookmark is visible.
1076
+ if (forceVisible) {
1077
+ this._balloon.showStack('main');
1078
+ }
1079
+ this._addFormView();
1080
+ } else {
1081
+ // Go to the editing UI if actions are already visible.
1082
+ if (this._areActionsVisible) {
1083
+ this._addFormView();
1084
+ } else {
1085
+ this._addActionsView();
1086
+ }
1087
+ // Be sure panel with bookmark is visible.
1088
+ if (forceVisible) {
1089
+ this._balloon.showStack('main');
1090
+ }
1091
+ }
1092
+ // Begin responding to ui#update once the UI is added.
1093
+ this._startUpdatingUI();
1094
+ }
1095
+ /**
1096
+ * Removes the {@link #formView} from the {@link #_balloon}.
1097
+ *
1098
+ * See {@link #_addFormView}, {@link #_addActionsView}.
1099
+ */ _hideUI() {
1100
+ if (!this._isUIInPanel) {
1101
+ return;
1102
+ }
1103
+ const editor = this.editor;
1104
+ this.stopListening(editor.ui, 'update');
1105
+ this.stopListening(this._balloon, 'change:visibleView');
1106
+ // Make sure the focus always gets back to the editable _before_ removing the focused form view.
1107
+ // Doing otherwise causes issues in some browsers. See https://github.com/ckeditor/ckeditor5-link/issues/193.
1108
+ editor.editing.view.focus();
1109
+ // Remove form first because it's on top of the stack.
1110
+ this._removeFormView();
1111
+ // Then remove the actions view because it's beneath the form.
1112
+ this._balloon.remove(this.actionsView);
1113
+ this._hideFakeVisualSelection();
1114
+ }
1115
+ /**
1116
+ * Makes the UI react to the {@link module:ui/editorui/editorui~EditorUI#event:update} event to
1117
+ * reposition itself when the editor UI should be refreshed.
1118
+ *
1119
+ * See: {@link #_hideUI} to learn when the UI stops reacting to the `update` event.
1120
+ */ _startUpdatingUI() {
1121
+ const editor = this.editor;
1122
+ const viewDocument = editor.editing.view.document;
1123
+ let prevSelectedBookmark = this._getSelectedBookmarkElement();
1124
+ let prevSelectionParent = getSelectionParent();
1125
+ this._updateFormButtonLabel(!!prevSelectedBookmark);
1126
+ const update = ()=>{
1127
+ const selectedBookmark = this._getSelectedBookmarkElement();
1128
+ const selectionParent = getSelectionParent();
1129
+ // Hide the panel if:
1130
+ //
1131
+ // * the selection went out of the EXISTING bookmark element. E.g. user moved the caret out
1132
+ // of the bookmark,
1133
+ // * the selection went to a different parent when creating a NEW bookmark. E.g. someone
1134
+ // else modified the document.
1135
+ // * the selection has expanded (e.g. displaying bookmark actions then pressing SHIFT+Right arrow).
1136
+ //
1137
+ if (prevSelectedBookmark && !selectedBookmark || !prevSelectedBookmark && selectionParent !== prevSelectionParent) {
1138
+ this._hideUI();
1139
+ } else if (this._isUIVisible) {
1140
+ // If still in a bookmark element, simply update the position of the balloon.
1141
+ // If there was no bookmark (e.g. inserting one), the balloon must be moved
1142
+ // to the new position in the editing view (a new native DOM range).
1143
+ this._balloon.updatePosition(this._getBalloonPositionData());
1144
+ }
1145
+ this._updateFormButtonLabel(!!prevSelectedBookmark);
1146
+ prevSelectedBookmark = selectedBookmark;
1147
+ prevSelectionParent = selectionParent;
1148
+ };
1149
+ function getSelectionParent() {
1150
+ return viewDocument.selection.focus.getAncestors().reverse().find((node)=>node.is('element'));
1151
+ }
1152
+ this.listenTo(editor.ui, 'update', update);
1153
+ this.listenTo(this._balloon, 'change:visibleView', update);
1154
+ }
1155
+ /**
1156
+ * Returns `true` when {@link #formView} is in the {@link #_balloon}.
1157
+ */ get _isFormInPanel() {
1158
+ return !!this.formView && this._balloon.hasView(this.formView);
1159
+ }
1160
+ /**
1161
+ * Returns `true` when {@link #actionsView} is in the {@link #_balloon}.
1162
+ */ get _areActionsInPanel() {
1163
+ return !!this.actionsView && this._balloon.hasView(this.actionsView);
1164
+ }
1165
+ /**
1166
+ * Returns `true` when {@link #actionsView} is in the {@link #_balloon} and it is
1167
+ * currently visible.
1168
+ */ get _areActionsVisible() {
1169
+ return !!this.actionsView && this._balloon.visibleView === this.actionsView;
1170
+ }
1171
+ /**
1172
+ * Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon}.
1173
+ */ get _isUIInPanel() {
1174
+ return this._isFormInPanel || this._areActionsInPanel;
1175
+ }
1176
+ /**
1177
+ * Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon} and it is
1178
+ * currently visible.
1179
+ */ get _isUIVisible() {
1180
+ const visibleView = this._balloon.visibleView;
1181
+ return !!this.formView && visibleView == this.formView || this._areActionsVisible;
1182
+ }
1183
+ /**
1184
+ * Returns positioning options for the {@link #_balloon}. They control the way the balloon is attached
1185
+ * to the target element or selection.
1186
+ */ _getBalloonPositionData() {
1187
+ const view = this.editor.editing.view;
1188
+ const model = this.editor.model;
1189
+ let target;
1190
+ const bookmarkElement = this._getSelectedBookmarkElement();
1191
+ if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
1192
+ // There are cases when we highlight selection using a marker (#7705, #4721).
1193
+ const markerViewElements = Array.from(this.editor.editing.mapper.markerNameToElements(VISUAL_SELECTION_MARKER_NAME));
1194
+ const newRange = view.createRange(view.createPositionBefore(markerViewElements[0]), view.createPositionAfter(markerViewElements[markerViewElements.length - 1]));
1195
+ target = view.domConverter.viewRangeToDom(newRange);
1196
+ } else if (bookmarkElement) {
1197
+ target = ()=>{
1198
+ const mapper = this.editor.editing.mapper;
1199
+ const domConverter = view.domConverter;
1200
+ const viewElement = mapper.toViewElement(bookmarkElement);
1201
+ return domConverter.mapViewToDom(viewElement);
1202
+ };
1203
+ }
1204
+ return target && {
1205
+ target
1206
+ };
1207
+ }
1208
+ /**
1209
+ * Returns the bookmark {@link module:engine/view/attributeelement~AttributeElement} under
1210
+ * the {@link module:engine/view/document~Document editing view's} selection or `null`
1211
+ * if there is none.
1212
+ */ _getSelectedBookmarkElement() {
1213
+ const selection = this.editor.model.document.selection;
1214
+ const element = selection.getSelectedElement();
1215
+ if (element && element.is('element', 'bookmark')) {
1216
+ return element;
1217
+ }
1218
+ return null;
1219
+ }
1220
+ /**
1221
+ * Displays a fake visual selection when the contextual balloon is displayed.
1222
+ *
1223
+ * This adds a 'bookmark-ui' marker into the document that is rendered as a highlight on selected text fragment.
1224
+ */ _showFakeVisualSelection() {
1225
+ const model = this.editor.model;
1226
+ model.change((writer)=>{
1227
+ const range = model.document.selection.getFirstRange();
1228
+ if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
1229
+ writer.updateMarker(VISUAL_SELECTION_MARKER_NAME, {
1230
+ range
1231
+ });
1232
+ } else {
1233
+ if (range.start.isAtEnd) {
1234
+ const startPosition = range.start.getLastMatchingPosition(({ item })=>!model.schema.isContent(item), {
1235
+ boundaries: range
1236
+ });
1237
+ writer.addMarker(VISUAL_SELECTION_MARKER_NAME, {
1238
+ usingOperation: false,
1239
+ affectsData: false,
1240
+ range: writer.createRange(startPosition, range.end)
1241
+ });
1242
+ } else {
1243
+ writer.addMarker(VISUAL_SELECTION_MARKER_NAME, {
1244
+ usingOperation: false,
1245
+ affectsData: false,
1246
+ range
1247
+ });
1248
+ }
1249
+ }
1250
+ });
1251
+ }
1252
+ /**
1253
+ * Hides the fake visual selection created in {@link #_showFakeVisualSelection}.
1254
+ */ _hideFakeVisualSelection() {
1255
+ const model = this.editor.model;
1256
+ if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
1257
+ model.change((writer)=>{
1258
+ writer.removeMarker(VISUAL_SELECTION_MARKER_NAME);
1259
+ });
1260
+ }
1261
+ }
1262
+ }
1263
+ /**
1264
+ * Returns bookmark form validation callbacks.
1265
+ */ function getFormValidators(editor) {
1266
+ const { t } = editor;
1267
+ const bookmarkEditing = editor.plugins.get(BookmarkEditing);
1268
+ return [
1269
+ (form)=>{
1270
+ if (!form.id) {
1271
+ return t('Bookmark must not be empty.');
1272
+ }
1273
+ },
1274
+ (form)=>{
1275
+ if (form.id && /\s/.test(form.id)) {
1276
+ return t('Bookmark name cannot contain space characters.');
1277
+ }
1278
+ },
1279
+ (form)=>{
1280
+ const selectedElement = editor.model.document.selection.getSelectedElement();
1281
+ const existingBookmarkForId = bookmarkEditing.getElementForBookmarkId(form.id);
1282
+ // Accept change of bookmark ID if no real change is happening (edit -> submit, without changes).
1283
+ if (selectedElement === existingBookmarkForId) {
1284
+ return;
1285
+ }
1286
+ if (existingBookmarkForId) {
1287
+ return t('Bookmark name already exists.');
1288
+ }
1289
+ }
1290
+ ];
1291
+ }
1292
+
1293
+ /**
1294
+ * The bookmark feature.
1295
+ *
1296
+ * For a detailed overview, check the {@glink features/bookmarks Bookmarks} feature guide.
1297
+ */ class Bookmark extends Plugin {
1298
+ /**
1299
+ * @inheritDoc
1300
+ */ static get pluginName() {
1301
+ return 'Bookmark';
1302
+ }
1303
+ /**
1304
+ * @inheritDoc
1305
+ */ static get requires() {
1306
+ return [
1307
+ BookmarkEditing,
1308
+ BookmarkUI,
1309
+ Widget
1310
+ ];
1311
+ }
1312
+ /**
1313
+ * @inheritDoc
1314
+ */ static get isOfficialPlugin() {
1315
+ return true;
1316
+ }
1317
+ }
1318
+
1319
+ export { Bookmark, BookmarkEditing, BookmarkUI, InsertBookmarkCommand, UpdateBookmarkCommand };
1320
+ //# sourceMappingURL=index.js.map