suneditor 3.0.0-alpha.2 → 3.0.0-alpha.20

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 (306) hide show
  1. package/.eslintrc.json +4 -3
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +19 -11
  4. package/README_V3_TEMP.md +705 -0
  5. package/dist/suneditor.min.css +1 -0
  6. package/dist/suneditor.min.js +1 -0
  7. package/example.md +587 -0
  8. package/package.json +15 -9
  9. package/src/assets/icons/_default.js +166 -131
  10. package/src/assets/{suneditor-content.css → suneditor-contents.css} +182 -45
  11. package/src/assets/suneditor.css +1195 -556
  12. package/src/assets/variables.css +138 -0
  13. package/src/core/base/eventHandlers/handler_toolbar.js +35 -14
  14. package/src/core/base/eventHandlers/handler_ww_clipboard.js +29 -4
  15. package/src/core/base/eventHandlers/handler_ww_dragDrop.js +59 -15
  16. package/src/core/base/eventHandlers/handler_ww_key_input.js +426 -212
  17. package/src/core/base/eventHandlers/handler_ww_mouse.js +108 -32
  18. package/src/core/base/eventManager.js +540 -209
  19. package/src/core/base/events.js +616 -320
  20. package/src/core/base/history.js +93 -39
  21. package/src/core/class/char.js +29 -13
  22. package/src/core/class/component.js +332 -145
  23. package/src/core/class/format.js +671 -509
  24. package/src/core/class/html.js +504 -290
  25. package/src/core/class/menu.js +114 -47
  26. package/src/core/class/nodeTransform.js +111 -66
  27. package/src/core/class/offset.js +409 -105
  28. package/src/core/class/selection.js +220 -108
  29. package/src/core/class/shortcuts.js +68 -8
  30. package/src/core/class/toolbar.js +106 -116
  31. package/src/core/class/ui.js +330 -0
  32. package/src/core/class/viewer.js +178 -74
  33. package/src/core/editor.js +489 -384
  34. package/src/core/section/actives.js +118 -22
  35. package/src/core/section/constructor.js +504 -170
  36. package/src/core/section/context.js +28 -23
  37. package/src/core/section/documentType.js +561 -0
  38. package/src/editorInjector/_classes.js +19 -5
  39. package/src/editorInjector/_core.js +71 -7
  40. package/src/editorInjector/index.js +63 -1
  41. package/src/helper/converter.js +137 -19
  42. package/src/helper/dom/domCheck.js +294 -0
  43. package/src/helper/dom/domQuery.js +609 -0
  44. package/src/helper/dom/domUtils.js +533 -0
  45. package/src/helper/dom/index.js +12 -0
  46. package/src/helper/env.js +42 -19
  47. package/src/helper/index.js +7 -4
  48. package/src/helper/keyCodeMap.js +183 -0
  49. package/src/helper/numbers.js +8 -8
  50. package/src/helper/unicode.js +5 -5
  51. package/src/langs/ckb.js +69 -3
  52. package/src/langs/cs.js +67 -1
  53. package/src/langs/da.js +68 -2
  54. package/src/langs/de.js +68 -3
  55. package/src/langs/en.js +29 -1
  56. package/src/langs/es.js +68 -3
  57. package/src/langs/fa.js +70 -2
  58. package/src/langs/fr.js +68 -2
  59. package/src/langs/he.js +68 -3
  60. package/src/langs/hu.js +226 -0
  61. package/src/langs/index.js +3 -2
  62. package/src/langs/it.js +65 -0
  63. package/src/langs/ja.js +68 -3
  64. package/src/langs/ko.js +66 -1
  65. package/src/langs/lv.js +68 -3
  66. package/src/langs/nl.js +68 -3
  67. package/src/langs/pl.js +68 -3
  68. package/src/langs/pt_br.js +65 -0
  69. package/src/langs/ro.js +69 -4
  70. package/src/langs/ru.js +68 -3
  71. package/src/langs/se.js +68 -3
  72. package/src/langs/tr.js +68 -0
  73. package/src/langs/ua.js +68 -3
  74. package/src/langs/ur.js +71 -6
  75. package/src/langs/zh_cn.js +69 -4
  76. package/src/modules/ApiManager.js +77 -54
  77. package/src/modules/Browser.js +667 -0
  78. package/src/modules/ColorPicker.js +162 -102
  79. package/src/modules/Controller.js +233 -136
  80. package/src/modules/Figure.js +913 -489
  81. package/src/modules/FileManager.js +141 -72
  82. package/src/modules/HueSlider.js +113 -61
  83. package/src/modules/Modal.js +292 -113
  84. package/src/modules/ModalAnchorEditor.js +380 -230
  85. package/src/modules/SelectMenu.js +270 -168
  86. package/src/modules/_DragHandle.js +2 -1
  87. package/src/modules/index.js +3 -3
  88. package/src/plugins/browser/audioGallery.js +83 -0
  89. package/src/plugins/browser/fileBrowser.js +103 -0
  90. package/src/plugins/browser/fileGallery.js +83 -0
  91. package/src/plugins/browser/imageGallery.js +81 -0
  92. package/src/plugins/browser/videoGallery.js +103 -0
  93. package/src/plugins/command/blockquote.js +40 -27
  94. package/src/plugins/command/exportPDF.js +134 -0
  95. package/src/plugins/command/fileUpload.js +226 -158
  96. package/src/plugins/command/list_bulleted.js +93 -47
  97. package/src/plugins/command/list_numbered.js +93 -47
  98. package/src/plugins/dropdown/align.js +66 -54
  99. package/src/plugins/dropdown/backgroundColor.js +76 -45
  100. package/src/plugins/dropdown/font.js +71 -47
  101. package/src/plugins/dropdown/fontColor.js +78 -46
  102. package/src/plugins/dropdown/formatBlock.js +74 -33
  103. package/src/plugins/dropdown/hr.js +102 -51
  104. package/src/plugins/dropdown/layout.js +37 -26
  105. package/src/plugins/dropdown/lineHeight.js +54 -38
  106. package/src/plugins/dropdown/list.js +60 -45
  107. package/src/plugins/dropdown/paragraphStyle.js +51 -30
  108. package/src/plugins/dropdown/table.js +1269 -777
  109. package/src/plugins/dropdown/template.js +38 -26
  110. package/src/plugins/dropdown/textStyle.js +43 -31
  111. package/src/plugins/field/mention.js +144 -82
  112. package/src/plugins/index.js +32 -6
  113. package/src/plugins/input/fontSize.js +161 -108
  114. package/src/plugins/input/pageNavigator.js +70 -0
  115. package/src/plugins/modal/audio.js +341 -169
  116. package/src/plugins/modal/drawing.js +530 -0
  117. package/src/plugins/modal/embed.js +886 -0
  118. package/src/plugins/modal/image.js +673 -358
  119. package/src/plugins/modal/link.js +100 -71
  120. package/src/plugins/modal/math.js +384 -168
  121. package/src/plugins/modal/video.js +693 -336
  122. package/src/plugins/popup/anchor.js +222 -0
  123. package/src/suneditor.js +54 -12
  124. package/src/themes/dark.css +85 -0
  125. package/src/typedef.js +86 -0
  126. package/types/assets/icons/_default.d.ts +152 -0
  127. package/types/core/base/eventHandlers/handler_toolbar.d.ts +41 -0
  128. package/types/core/base/eventHandlers/handler_ww_clipboard.d.ts +40 -0
  129. package/types/core/base/eventHandlers/handler_ww_dragDrop.d.ts +35 -0
  130. package/types/core/base/eventHandlers/handler_ww_key_input.d.ts +45 -0
  131. package/types/core/base/eventHandlers/handler_ww_mouse.d.ts +39 -0
  132. package/types/core/base/eventManager.d.ts +377 -0
  133. package/types/core/base/events.d.ts +297 -0
  134. package/types/core/base/history.d.ts +81 -0
  135. package/types/core/class/char.d.ts +60 -0
  136. package/types/core/class/component.d.ts +259 -0
  137. package/types/core/class/format.d.ts +615 -0
  138. package/types/core/class/html.d.ts +377 -0
  139. package/types/core/class/menu.d.ts +118 -0
  140. package/types/core/class/nodeTransform.d.ts +93 -0
  141. package/types/core/class/offset.d.ts +512 -0
  142. package/types/core/class/selection.d.ts +188 -0
  143. package/types/core/class/shortcuts.d.ts +142 -0
  144. package/types/core/class/toolbar.d.ts +189 -0
  145. package/types/core/class/ui.d.ts +144 -0
  146. package/types/core/class/viewer.d.ts +140 -0
  147. package/types/core/editor.d.ts +606 -0
  148. package/types/core/section/actives.d.ts +46 -0
  149. package/types/core/section/constructor.d.ts +748 -0
  150. package/types/core/section/context.d.ts +45 -0
  151. package/types/core/section/documentType.d.ts +178 -0
  152. package/types/editorInjector/_classes.d.ts +41 -0
  153. package/types/editorInjector/_core.d.ts +92 -0
  154. package/types/editorInjector/index.d.ts +71 -0
  155. package/types/helper/converter.d.ts +150 -0
  156. package/types/helper/dom/domCheck.d.ts +182 -0
  157. package/types/helper/dom/domQuery.d.ts +214 -0
  158. package/types/helper/dom/domUtils.d.ts +211 -0
  159. package/types/helper/dom/index.d.ts +9 -0
  160. package/types/helper/env.d.ts +149 -0
  161. package/types/helper/index.d.ts +163 -0
  162. package/types/helper/keyCodeMap.d.ts +110 -0
  163. package/types/helper/numbers.d.ts +43 -0
  164. package/types/helper/unicode.d.ts +28 -0
  165. package/types/index.d.ts +0 -0
  166. package/{typings/Lang.d.ts → types/langs/_Lang.d.ts} +170 -103
  167. package/types/langs/ckb.d.ts +384 -0
  168. package/types/langs/cs.d.ts +384 -0
  169. package/types/langs/da.d.ts +384 -0
  170. package/types/langs/de.d.ts +384 -0
  171. package/types/langs/en.d.ts +384 -0
  172. package/types/langs/es.d.ts +384 -0
  173. package/types/langs/fa.d.ts +384 -0
  174. package/types/langs/fr.d.ts +384 -0
  175. package/types/langs/he.d.ts +384 -0
  176. package/types/langs/hu.d.ts +384 -0
  177. package/types/langs/index.d.ts +48 -0
  178. package/types/langs/it.d.ts +384 -0
  179. package/types/langs/ja.d.ts +384 -0
  180. package/types/langs/ko.d.ts +384 -0
  181. package/types/langs/lv.d.ts +384 -0
  182. package/types/langs/nl.d.ts +384 -0
  183. package/types/langs/pl.d.ts +384 -0
  184. package/types/langs/pt_br.d.ts +384 -0
  185. package/types/langs/ro.d.ts +384 -0
  186. package/types/langs/ru.d.ts +384 -0
  187. package/types/langs/se.d.ts +384 -0
  188. package/types/langs/tr.d.ts +384 -0
  189. package/types/langs/ua.d.ts +384 -0
  190. package/types/langs/ur.d.ts +384 -0
  191. package/types/langs/zh_cn.d.ts +384 -0
  192. package/types/modules/ApiManager.d.ts +125 -0
  193. package/types/modules/Browser.d.ts +326 -0
  194. package/types/modules/ColorPicker.d.ts +131 -0
  195. package/types/modules/Controller.d.ts +231 -0
  196. package/types/modules/Figure.d.ts +504 -0
  197. package/types/modules/FileManager.d.ts +202 -0
  198. package/types/modules/HueSlider.d.ts +136 -0
  199. package/types/modules/Modal.d.ts +117 -0
  200. package/types/modules/ModalAnchorEditor.d.ts +236 -0
  201. package/types/modules/SelectMenu.d.ts +194 -0
  202. package/types/modules/_DragHandle.d.ts +7 -0
  203. package/types/modules/index.d.ts +26 -0
  204. package/types/plugins/browser/audioGallery.d.ts +55 -0
  205. package/types/plugins/browser/fileBrowser.d.ts +64 -0
  206. package/types/plugins/browser/fileGallery.d.ts +55 -0
  207. package/types/plugins/browser/imageGallery.d.ts +51 -0
  208. package/types/plugins/browser/videoGallery.d.ts +57 -0
  209. package/types/plugins/command/blockquote.d.ts +28 -0
  210. package/types/plugins/command/exportPDF.d.ts +46 -0
  211. package/types/plugins/command/fileUpload.d.ts +156 -0
  212. package/types/plugins/command/list_bulleted.d.ts +56 -0
  213. package/types/plugins/command/list_numbered.d.ts +56 -0
  214. package/types/plugins/dropdown/align.d.ts +60 -0
  215. package/types/plugins/dropdown/backgroundColor.d.ts +63 -0
  216. package/types/plugins/dropdown/font.d.ts +54 -0
  217. package/types/plugins/dropdown/fontColor.d.ts +63 -0
  218. package/types/plugins/dropdown/formatBlock.d.ts +58 -0
  219. package/types/plugins/dropdown/hr.d.ts +81 -0
  220. package/types/plugins/dropdown/layout.d.ts +40 -0
  221. package/types/plugins/dropdown/lineHeight.d.ts +50 -0
  222. package/types/plugins/dropdown/list.d.ts +39 -0
  223. package/types/plugins/dropdown/paragraphStyle.d.ts +54 -0
  224. package/types/plugins/dropdown/table.d.ts +579 -0
  225. package/types/plugins/dropdown/template.d.ts +40 -0
  226. package/types/plugins/dropdown/textStyle.d.ts +41 -0
  227. package/types/plugins/field/mention.d.ts +102 -0
  228. package/types/plugins/index.d.ts +107 -0
  229. package/types/plugins/input/fontSize.d.ts +170 -0
  230. package/types/plugins/input/pageNavigator.d.ts +28 -0
  231. package/types/plugins/modal/audio.d.ts +269 -0
  232. package/types/plugins/modal/drawing.d.ts +246 -0
  233. package/types/plugins/modal/embed.d.ts +387 -0
  234. package/types/plugins/modal/image.d.ts +451 -0
  235. package/types/plugins/modal/link.d.ts +128 -0
  236. package/types/plugins/modal/math.d.ts +193 -0
  237. package/types/plugins/modal/video.d.ts +485 -0
  238. package/types/plugins/popup/anchor.d.ts +56 -0
  239. package/types/suneditor.d.ts +51 -0
  240. package/types/typedef-global.d.ts +144 -0
  241. package/src/core/class/notice.js +0 -42
  242. package/src/helper/domUtils.js +0 -1177
  243. package/src/modules/FileBrowser.js +0 -271
  244. package/src/plugins/command/exportPdf.js +0 -168
  245. package/src/plugins/fileBrowser/imageGallery.js +0 -81
  246. package/src/themes/test.css +0 -61
  247. package/typings/CommandPlugin.d.ts +0 -8
  248. package/typings/DialogPlugin.d.ts +0 -20
  249. package/typings/FileBrowserPlugin.d.ts +0 -30
  250. package/typings/Module.d.ts +0 -15
  251. package/typings/Plugin.d.ts +0 -42
  252. package/typings/SubmenuPlugin.d.ts +0 -8
  253. package/typings/_classes.d.ts +0 -17
  254. package/typings/_colorPicker.d.ts +0 -60
  255. package/typings/_core.d.ts +0 -55
  256. package/typings/align.d.ts +0 -5
  257. package/typings/audio.d.ts +0 -5
  258. package/typings/backgroundColor.d.ts +0 -5
  259. package/typings/blockquote.d.ts +0 -5
  260. package/typings/char.d.ts +0 -39
  261. package/typings/component.d.ts +0 -38
  262. package/typings/context.d.ts +0 -39
  263. package/typings/converter.d.ts +0 -33
  264. package/typings/dialog.d.ts +0 -28
  265. package/typings/domUtils.d.ts +0 -361
  266. package/typings/editor.d.ts +0 -7
  267. package/typings/editor.ts +0 -542
  268. package/typings/env.d.ts +0 -70
  269. package/typings/eventManager.d.ts +0 -37
  270. package/typings/events.d.ts +0 -262
  271. package/typings/fileBrowser.d.ts +0 -42
  272. package/typings/fileManager.d.ts +0 -67
  273. package/typings/font.d.ts +0 -5
  274. package/typings/fontColor.d.ts +0 -5
  275. package/typings/fontSize.d.ts +0 -5
  276. package/typings/format.d.ts +0 -191
  277. package/typings/formatBlock.d.ts +0 -5
  278. package/typings/history.d.ts +0 -48
  279. package/typings/horizontalRule.d.ts +0 -5
  280. package/typings/image.d.ts +0 -5
  281. package/typings/imageGallery.d.ts +0 -5
  282. package/typings/index.d.ts +0 -21
  283. package/typings/index.modules.d.ts +0 -11
  284. package/typings/index.plugins.d.ts +0 -58
  285. package/typings/lineHeight.d.ts +0 -5
  286. package/typings/link.d.ts +0 -5
  287. package/typings/list.d.ts +0 -5
  288. package/typings/math.d.ts +0 -5
  289. package/typings/mediaContainer.d.ts +0 -25
  290. package/typings/mention.d.ts +0 -5
  291. package/typings/node.d.ts +0 -57
  292. package/typings/notice.d.ts +0 -16
  293. package/typings/numbers.d.ts +0 -29
  294. package/typings/offset.d.ts +0 -24
  295. package/typings/options.d.ts +0 -589
  296. package/typings/paragraphStyle.d.ts +0 -5
  297. package/typings/resizing.d.ts +0 -141
  298. package/typings/selection.d.ts +0 -94
  299. package/typings/shortcuts.d.ts +0 -13
  300. package/typings/suneditor.d.ts +0 -9
  301. package/typings/table.d.ts +0 -5
  302. package/typings/template.d.ts +0 -5
  303. package/typings/textStyle.d.ts +0 -5
  304. package/typings/toolbar.d.ts +0 -32
  305. package/typings/unicode.d.ts +0 -25
  306. package/typings/video.d.ts +0 -5
@@ -0,0 +1,667 @@
1
+ import CoreInjector from '../editorInjector/_core';
2
+ import { dom, keyCodeMap } from '../helper';
3
+ import ApiManager from './ApiManager';
4
+
5
+ /**
6
+ * @typedef {Object} BrowserFile
7
+ * @property {string} [src=""] - Source url
8
+ * @property {string} [name=""] - File name | Folder name
9
+ * @property {string=} thumbnail - Thumbnail url
10
+ * @property {string=} alt - Image alt
11
+ * @property {Array<string>|string=} tag - Tag name list
12
+ * @property {string=} type - Type (image, video, audio, etc.)
13
+ * @property {string=} frame - Frame name (iframe, video, etc.)
14
+ * @property {BrowserFile | string=} _data - The folder's contents or an API URL.
15
+ * @property {boolean=} default - Whether this folder is the default selection.
16
+ * @property {Object<string, *>=} meta - Metadata
17
+ */
18
+
19
+ /**
20
+ * @typedef BrowserParams
21
+ * @property {string} title - File browser window title. Required. Can be overridden in browser.
22
+ * @property {string=} className - Class name of the file browser. Optional. Default: ''.
23
+ * @property {Object<string, *>|Array<*>=} data - direct data without server calls
24
+ * @property {string=} url - File server url. Required. Can be overridden in browser.
25
+ * @property {Object<string, string>=} headers - File server http header. Required. Can be overridden in browser.
26
+ * @property {(target: Node) => void} selectorHandler - Function that actions when an item is clicked. Required. Can be overridden in browser.
27
+ * @property {boolean=} useSearch - Whether to use the search function. Optional. Default: true.
28
+ * @property {string=} searchUrl - File server search url. Optional. Can be overridden in browser.
29
+ * @property {Object<string, string>=} searchUrlHeader - File server search http header. Optional. Can be overridden in browser.
30
+ * @property {string=} listClass - Class name of list div. Required. Can be overridden in browser.
31
+ * @property {(item: BrowserFile) => string=} drawItemHandler - Function that defines the HTML of a file item. Required. Can be overridden in browser.
32
+ * @property {Array<*>=} props - "props" argument to "drawItemHandler" function. Optional. Can be overridden in browser.
33
+ * @property {number=} columnSize - Number of "div.se-file-item-column" to be created. Optional. Can be overridden in browser. Default: 4.
34
+ * @property {((item: BrowserFile) => string)=} thumbnail - Default thumbnail
35
+ */
36
+
37
+ /**
38
+ * @class
39
+ * @description File browser plugin
40
+ */
41
+ class Browser extends CoreInjector {
42
+ /**
43
+ * @constructor
44
+ * @param {*} inst The instance object that called the constructor.
45
+ * @param {BrowserParams} params Browser options
46
+ */
47
+ constructor(inst, params) {
48
+ super(inst.editor);
49
+
50
+ // create HTML
51
+ this.useSearch = params.useSearch ?? true;
52
+ const browserFrame = dom.utils.createElement('DIV', { class: 'se-browser sun-editor-common' + (params.className ? ` ${params.className}` : '') });
53
+ const contentHTML = CreateHTMLInfos(inst.editor, this.useSearch);
54
+ const content = contentHTML.html;
55
+
56
+ // members
57
+ this.kind = inst.constructor.key || inst.constructor.name;
58
+ this.inst = inst;
59
+ this.area = browserFrame;
60
+ this.header = contentHTML.header;
61
+ this.titleArea = contentHTML.titleArea;
62
+ this.tagArea = contentHTML.tagArea;
63
+ this.body = contentHTML.body;
64
+ this.list = contentHTML.list;
65
+ this.side = contentHTML.side;
66
+ this.wrapper = contentHTML.wrapper;
67
+ this._loading = contentHTML._loading;
68
+
69
+ this.title = params.title;
70
+ this.listClass = params.listClass || 'se-preview-list';
71
+ this.directData = params.data;
72
+ this.url = params.url;
73
+ this.urlHeader = params.headers;
74
+ this.searchUrl = params.searchUrl;
75
+ this.searchUrlHeader = params.searchUrlHeader;
76
+ this.drawItemHandler = (params.drawItemHandler || DrawItems).bind({ thumbnail: params.thumbnail, props: params.props || [] });
77
+ this.selectorHandler = params.selectorHandler;
78
+ this.columnSize = params.columnSize || 4;
79
+ this.folderDefaultPath = '';
80
+ this.closeArrow = this.icons.menu_arrow_right;
81
+ this.openArrow = this.icons.menu_arrow_down;
82
+ this.icon_folder = this.icons.side_menu_folder_item;
83
+ this.icon_folder_item = this.icons.side_menu_folder;
84
+ this.icon_item = this.icons.side_menu_item;
85
+
86
+ /**
87
+ * @type {Array<BrowserFile>}
88
+ */
89
+ this.items = [];
90
+ /**
91
+ * @type {Object<string, {name: string, meta: Object<string, *>}>}
92
+ */
93
+ this.folders = {};
94
+ /**
95
+ * @type {Object<string, {key?: string, name?: string, children?: *}>}
96
+ */
97
+ this.tree = {};
98
+ /**
99
+ * @type {BrowserFile}
100
+ */
101
+ this.data = {};
102
+ this.selectedTags = [];
103
+ this.keyword = '';
104
+ this.sideInner = null;
105
+ this._closeSignal = false;
106
+ this._bindClose = null;
107
+ this.__globalEventHandler = (e) => {
108
+ if (!keyCodeMap.isEsc(e.code)) return;
109
+ this.close();
110
+ };
111
+ // api manager
112
+ this.apiManager = new ApiManager(this, { method: 'GET' });
113
+
114
+ // init
115
+ browserFrame.appendChild(dom.utils.createElement('DIV', { class: 'se-browser-back' }));
116
+ browserFrame.appendChild(content);
117
+ this.carrierWrapper.appendChild(browserFrame);
118
+
119
+ this.eventManager.addEvent(this.tagArea, 'click', this.#OnClickTag.bind(this));
120
+ this.eventManager.addEvent(this.list, 'click', this.#OnClickFile.bind(this));
121
+ this.eventManager.addEvent(this.side, 'click', this.#OnClickSide.bind(this));
122
+ this.eventManager.addEvent(content, 'mousedown', this.#OnMouseDown_browser.bind(this));
123
+ this.eventManager.addEvent(content, 'click', this.#OnClick_browser.bind(this));
124
+ this.eventManager.addEvent(browserFrame.querySelector('form.se-browser-search-form'), 'submit', this.#Search.bind(this));
125
+ this.eventManager.addEvent((this.sideOpenBtn = /** @type {HTMLButtonElement} */ (browserFrame.querySelector('.se-side-open-btn'))), 'click', this.#SideOpen.bind(this));
126
+ this.eventManager.addEvent([this.header, browserFrame.querySelector('.se-browser-main')], 'mousedown', this.#SideClose.bind(this));
127
+ }
128
+
129
+ /**
130
+ * @description Open a file browser plugin
131
+ * @param {Object} [params={}]
132
+ * @param {string=} params.listClass - Class name of list div. If not, use "this.listClass".
133
+ * @param {string=} params.title - File browser window title. If not, use "this.title".
134
+ * @param {string=} params.url - File server url. If not, use "this.url".
135
+ * @param {Object<string, string>=} params.urlHeader - File server http header. If not, use "this.urlHeader".
136
+ */
137
+ open(params) {
138
+ if (!params) params = {};
139
+ this.__addGlobalEvent();
140
+
141
+ const listClassName = params.listClass || this.listClass;
142
+ if (!dom.utils.hasClass(this.list, listClassName)) {
143
+ this.list.className = 'se-browser-list ' + listClassName;
144
+ }
145
+
146
+ this.titleArea.textContent = params.title || this.title;
147
+ this.area.style.display = 'block';
148
+ this.editor.opendBrowser = this;
149
+ this.closeArrow = this.options.get('_rtl') ? this.icons.menu_arrow_left : this.icons.menu_arrow_right;
150
+
151
+ if (this.directData) {
152
+ this.__drowItems(this.directData);
153
+ } else {
154
+ this._drawFileList(params.url || this.url, params.urlHeader || this.urlHeader, false);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * @description Close a browser plugin
160
+ * - The plugin's "init" method is called.
161
+ */
162
+ close() {
163
+ this.__removeGlobalEvent();
164
+ this.apiManager.cancel();
165
+
166
+ this.area.style.display = 'none';
167
+ this.selectedTags = [];
168
+ this.items = [];
169
+ this.folders = {};
170
+ this.tree = {};
171
+ this.data = {};
172
+ this.keyword = '';
173
+ this.list.innerHTML = this.tagArea.innerHTML = this.titleArea.textContent = '';
174
+ this.editor.opendBrowser = null;
175
+ this.sideInner = null;
176
+
177
+ if (typeof this.inst.init === 'function') this.inst.init();
178
+ }
179
+
180
+ /**
181
+ * @description Search files
182
+ * @param {string} keyword - Search keyword
183
+ */
184
+ search(keyword) {
185
+ if (this.searchUrl) {
186
+ this.keyword = keyword;
187
+ this._drawFileList(this.searchUrl + '?keyword=' + keyword, this.searchUrlHeader, false);
188
+ } else {
189
+ this.keyword = keyword.toLowerCase();
190
+ this._drawListItem(this.items, false);
191
+ }
192
+ }
193
+
194
+ /**
195
+ * @description Filter items by tag
196
+ * @param {Array<BrowserFile>} items - Items to filter
197
+ * @returns {Array<BrowserFile>}
198
+ */
199
+ tagfilter(items) {
200
+ const selectedTags = this.selectedTags;
201
+ return selectedTags.length === 0 ? items : items.filter((item) => !Array.isArray(item.tag) || item.tag.some((tag) => selectedTags.includes(tag)));
202
+ }
203
+
204
+ /**
205
+ * @description Show file browser loading box
206
+ */
207
+ showBrowserLoading() {
208
+ this._loading.style.display = 'block';
209
+ }
210
+
211
+ /**
212
+ * @description Close file browser loading box
213
+ */
214
+ closeBrowserLoading() {
215
+ this._loading.style.display = 'none';
216
+ }
217
+
218
+ /**
219
+ * @private
220
+ * @description Fetches the file list from the server.
221
+ * @param {string} url - The file server URL.
222
+ * @param {Object<string, string>} urlHeader - The HTTP headers for the request.
223
+ * @param {boolean} pageLoading - Indicates if this is a paginated request.
224
+ */
225
+ _drawFileList(url, urlHeader, pageLoading) {
226
+ this.apiManager.call({ method: 'GET', url, headers: urlHeader, callBack: this.#CallBackGet.bind(this), errorCallBack: this.#CallBackError.bind(this) });
227
+ if (!pageLoading) {
228
+ this.sideOpenBtn.style.display = 'none';
229
+ this.showBrowserLoading();
230
+ }
231
+ }
232
+
233
+ /**
234
+ * @private
235
+ * @description Updates the displayed list of file items.
236
+ * @param {Array<BrowserFile>} items - The file items to display.
237
+ * @param {boolean} update - Whether to update the tags.
238
+ */
239
+ _drawListItem(items, update) {
240
+ const keyword = this.keyword;
241
+ items = this.tagfilter(items).filter((item) => item.name.toLowerCase().indexOf(keyword) > -1);
242
+
243
+ const _tags = [];
244
+ const len = items.length;
245
+ const columnSize = this.columnSize;
246
+ const splitSize = columnSize <= 1 ? 1 : Math.round(len / columnSize) || 1;
247
+ const drawItemHandler = this.drawItemHandler;
248
+
249
+ let tagsHTML = '';
250
+ let listHTML = '<div class="se-file-item-column">';
251
+ let columns = 1;
252
+ for (let i = 0, item, tags; i < len; i++) {
253
+ item = items[i];
254
+ tags = !item.tag ? [] : typeof item.tag === 'string' ? item.tag.split(',') : item.tag;
255
+ tags = item.tag = tags.map((v) => v.trim());
256
+ listHTML += drawItemHandler(item);
257
+
258
+ if ((i + 1) % splitSize === 0 && columns < columnSize && i + 1 < len) {
259
+ columns++;
260
+ listHTML += '</div><div class="se-file-item-column">';
261
+ }
262
+
263
+ if (update && tags.length > 0) {
264
+ for (let t = 0, tLen = tags.length, tag; t < tLen; t++) {
265
+ tag = tags[t];
266
+ if (tag && !_tags.includes(tag)) {
267
+ _tags.push(tag);
268
+ tagsHTML += `<a title="${tag}" aria-label="${tag}">${tag}</a>`;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ listHTML += '</div>';
274
+
275
+ this.list.innerHTML = listHTML;
276
+
277
+ if (update) {
278
+ this.items = items;
279
+ this.tagArea.innerHTML = tagsHTML;
280
+ }
281
+ }
282
+
283
+ /**
284
+ * @private
285
+ * @description Adds a global event listener for closing the browser.
286
+ */
287
+ __addGlobalEvent() {
288
+ this.__removeGlobalEvent();
289
+ this._bindClose = this.eventManager.addGlobalEvent('keydown', this.__globalEventHandler, true);
290
+ }
291
+
292
+ /**
293
+ * @private
294
+ * @description Removes the global event listener for closing the browser.
295
+ */
296
+ __removeGlobalEvent() {
297
+ if (this._bindClose) this._bindClose = this.eventManager.removeGlobalEvent(this._bindClose);
298
+ }
299
+
300
+ /**
301
+ * @private
302
+ * @description Renders the file items or folder structure from data.
303
+ * @param {BrowserFile[]|BrowserFile} data - The data representing the file structure.
304
+ * @returns {boolean} True if rendering was successful, false otherwise.
305
+ */
306
+ __drowItems(data) {
307
+ if (Array.isArray(data)) {
308
+ if (data.length > 0) {
309
+ this._drawListItem(data, true);
310
+ }
311
+ return true;
312
+ } else if (typeof data === 'object') {
313
+ this.sideOpenBtn.style.display = '';
314
+ this.__parseFolderData(data);
315
+
316
+ this.side.innerHTML = '';
317
+ const sideInner = (this.sideInner = dom.utils.createElement('div', null));
318
+ this.__createFolderList(this.tree, sideInner);
319
+ this.side.appendChild(sideInner);
320
+
321
+ if (this.folderDefaultPath) {
322
+ const openFolder = /** @type {HTMLButtonElement} */ (sideInner.querySelector(`[data-command="${this.folderDefaultPath}"]`));
323
+ openFolder.click();
324
+ if (this.folderDefaultPath.includes('/')) {
325
+ dom.utils.removeClass(openFolder.parentElement, 'se-menu-hidden');
326
+ openFolder.parentElement.previousElementSibling.querySelector('button').innerHTML = this.openArrow;
327
+ }
328
+ }
329
+
330
+ return true;
331
+ }
332
+ return false;
333
+ }
334
+
335
+ /**
336
+ * @private
337
+ * @description Parses folder data into a structured format.
338
+ * @param {BrowserFile} data - The folder data.
339
+ * @param {string} [path] - The current path in the folder hierarchy.
340
+ */
341
+ __parseFolderData(data, path) {
342
+ let current = this.tree;
343
+
344
+ // _data
345
+ if (data._data) {
346
+ this.data[path] = data._data;
347
+ if (!this.folderDefaultPath || data.default) {
348
+ this.folderDefaultPath = path;
349
+ }
350
+
351
+ const parts = path.split('/');
352
+ const len = parts.length - 1;
353
+ parts.forEach((part, index) => {
354
+ if (!current[part]) {
355
+ current[part] = { children: {} };
356
+ }
357
+
358
+ if (index === len) {
359
+ current[part].key = path;
360
+ current[part].name = this.folders[path].name;
361
+ } else {
362
+ current = current[part].children;
363
+ }
364
+ });
365
+ } else if (path) {
366
+ current[path] = { name: this.folders[path].name, children: {} };
367
+ }
368
+
369
+ // create folders, file path
370
+ Object.entries(data).forEach(([key, value]) => {
371
+ if (key === '_data' || !value || typeof value !== 'object') return;
372
+
373
+ const v = /** @type {BrowserFile} */ (value);
374
+ const currentPath = path ? `${path}/${key}` : key;
375
+
376
+ this.folders[currentPath] = {
377
+ name: v.name || key,
378
+ meta: v.meta || {}
379
+ };
380
+
381
+ this.__parseFolderData(v, currentPath);
382
+ });
383
+ }
384
+
385
+ /**
386
+ * @private
387
+ * @description Creates a nested folder list from parsed data.
388
+ * @param {BrowserFile[]|BrowserFile} folderData - The structured folder data.
389
+ * @param {HTMLElement} parentElement - The parent element to append folder structure to.
390
+ */
391
+ __createFolderList(folderData, parentElement) {
392
+ for (const key in folderData) {
393
+ const item = folderData[key];
394
+ if (!item) continue;
395
+
396
+ if (Object.keys(item.children).length > 0) {
397
+ const folderLabel = dom.utils.createElement(
398
+ 'div',
399
+ item.key ? { 'data-command': item.key, 'aria-label': item.name } : null,
400
+ `<span class="se-menu-icon">${item.key ? this.icon_folder : this.icon_folder_item}</span><span>${item.name}</span>`
401
+ );
402
+ const folderDiv = dom.utils.createElement('div', { class: 'se-menu-folder' }, folderLabel);
403
+
404
+ folderLabel.insertBefore(dom.utils.createElement('button', null, this.closeArrow), folderLabel.firstElementChild);
405
+ const childContainer = document.createElement('div');
406
+ dom.utils.addClass(childContainer, 'se-menu-child|se-menu-hidden');
407
+ this.__createFolderList(item.children, childContainer);
408
+ folderDiv.appendChild(childContainer);
409
+
410
+ parentElement.appendChild(folderDiv);
411
+ } else {
412
+ const folderLabel = dom.utils.createElement('div', { 'data-command': item.key, 'aria-label': item.name, class: 'se-menu-folder-item' }, `<span class="se-menu-icon">${this.icon_item}</span><span>${item.name}</span>`);
413
+ if (parentElement === this.sideInner) {
414
+ const folderDiv = dom.utils.createElement('div', { class: 'se-menu-folder' }, folderLabel);
415
+ parentElement.appendChild(folderDiv);
416
+ } else {
417
+ parentElement.appendChild(folderLabel);
418
+ }
419
+ }
420
+ }
421
+ }
422
+
423
+ /**
424
+ * @param {XMLHttpRequest} xmlHttp - XMLHttpRequest object.
425
+ */
426
+ #CallBackGet(xmlHttp) {
427
+ try {
428
+ const res = JSON.parse(xmlHttp.responseText);
429
+ const data = res.result;
430
+ if (this.__drowItems(data)) return;
431
+
432
+ if (res.nullMessage) {
433
+ this.list.innerHTML = res.nullMessage;
434
+ }
435
+ } catch (e) {
436
+ throw Error(`[SUNEDITOR.browser.drawList.fail] cause: "${e.message}"`);
437
+ } finally {
438
+ this.closeBrowserLoading();
439
+ this.body.style.maxHeight = dom.utils.getClientSize().h - this.header.offsetHeight - 50 + 'px';
440
+ }
441
+ }
442
+
443
+ /**
444
+ * @param {*} res - response data.
445
+ * @param {XMLHttpRequest} xmlHttp - XMLHttpRequest object.
446
+ */
447
+ #CallBackError(res, xmlHttp) {
448
+ this.closeBrowserLoading();
449
+ throw Error(`[SUNEDITOR.browser.get.serverException] status: ${xmlHttp.status}, response: ${res.errorMessage || xmlHttp.responseText}`);
450
+ }
451
+
452
+ /**
453
+ * @param {MouseEvent} e - Event object
454
+ */
455
+ #OnClickTag(e) {
456
+ const eventTarget = dom.query.getEventTarget(e);
457
+ if (!dom.check.isAnchor(eventTarget)) return;
458
+
459
+ const tagName = eventTarget.textContent;
460
+ const selectTag = this.tagArea.querySelector('a[title="' + tagName + '"]');
461
+ const sTagIndex = this.selectedTags.indexOf(tagName);
462
+
463
+ if (sTagIndex > -1) {
464
+ this.selectedTags.splice(sTagIndex, 1);
465
+ dom.utils.removeClass(selectTag, 'on');
466
+ } else {
467
+ this.selectedTags.push(tagName);
468
+ dom.utils.addClass(selectTag, 'on');
469
+ }
470
+
471
+ this._drawListItem(this.items, false);
472
+ }
473
+
474
+ /**
475
+ * @param {MouseEvent} e - Event object
476
+ */
477
+ #OnClickFile(e) {
478
+ const eventTarget = dom.query.getEventTarget(e);
479
+
480
+ e.preventDefault();
481
+ e.stopPropagation();
482
+
483
+ if (eventTarget === this.list) return;
484
+
485
+ const target = dom.query.getCommandTarget(eventTarget);
486
+ if (!target) return;
487
+
488
+ this.close();
489
+ this.selectorHandler(target);
490
+ }
491
+
492
+ /**
493
+ * @param {MouseEvent} e - Event object
494
+ */
495
+ #OnClickSide(e) {
496
+ const eventTarget = dom.query.getEventTarget(e);
497
+ e.stopPropagation();
498
+
499
+ if (/^button$/i.test(eventTarget.nodeName)) {
500
+ const childContainer = eventTarget.parentElement.parentElement.querySelector('.se-menu-child');
501
+ if (dom.utils.hasClass(childContainer, 'se-menu-hidden')) {
502
+ dom.utils.removeClass(childContainer, 'se-menu-hidden');
503
+ eventTarget.innerHTML = this.openArrow;
504
+ } else {
505
+ dom.utils.addClass(childContainer, 'se-menu-hidden');
506
+ eventTarget.innerHTML = this.closeArrow;
507
+ }
508
+ return;
509
+ }
510
+
511
+ const cmdTarget = dom.query.getCommandTarget(eventTarget);
512
+ if (!cmdTarget || dom.utils.hasClass(cmdTarget, 'active')) return;
513
+
514
+ const data = this.data[cmdTarget.getAttribute('data-command')];
515
+
516
+ dom.utils.removeClass(this.side.querySelectorAll('.active'), 'active');
517
+ dom.utils.addClass([cmdTarget, dom.query.getParentElement(cmdTarget, '.se-menu-folder')], 'active');
518
+ this.tagArea.innerHTML = '';
519
+
520
+ if (typeof data === 'string') {
521
+ this._drawFileList(data, this.urlHeader, true);
522
+ } else {
523
+ this._drawListItem(data, false);
524
+ }
525
+ }
526
+
527
+ /**
528
+ * @param {MouseEvent} e - Event object
529
+ */
530
+ #OnMouseDown_browser(e) {
531
+ const eventTarget = dom.query.getEventTarget(e);
532
+ if (/se-browser-inner/.test(eventTarget.className)) {
533
+ this._closeSignal = true;
534
+ } else {
535
+ this._closeSignal = false;
536
+ }
537
+ }
538
+
539
+ /**
540
+ * @param {MouseEvent} e - Event object
541
+ */
542
+ #OnClick_browser(e) {
543
+ const eventTarget = dom.query.getEventTarget(e);
544
+ e.stopPropagation();
545
+
546
+ if (/close/.test(eventTarget.getAttribute('data-command')) || this._closeSignal) {
547
+ this.close();
548
+ }
549
+ }
550
+
551
+ /**
552
+ * @param {SubmitEvent} e - Event object
553
+ */
554
+ #Search(e) {
555
+ const eventTarget = /** @type {HTMLElement} */ (e.currentTarget);
556
+ e.preventDefault();
557
+ this.search(/** @type {HTMLInputElement} */ (eventTarget.querySelector('input[type="text"]')).value);
558
+ }
559
+
560
+ /**
561
+ * @param {MouseEvent} e - Event object
562
+ */
563
+ #SideOpen(e) {
564
+ const eventTarget = dom.query.getEventTarget(e);
565
+ if (dom.utils.hasClass(eventTarget, 'active')) {
566
+ dom.utils.removeClass(this.side, 'se-side-show');
567
+ dom.utils.removeClass(eventTarget, 'active');
568
+ } else {
569
+ dom.utils.addClass(this.side, 'se-side-show');
570
+ dom.utils.addClass(eventTarget, 'active');
571
+ }
572
+ }
573
+
574
+ /**
575
+ * @param {MouseEvent} e - Event object
576
+ */
577
+ #SideClose({ target }) {
578
+ if (target === this.sideOpenBtn) return;
579
+ if (dom.utils.hasClass(this.sideOpenBtn, 'active')) {
580
+ dom.utils.removeClass(this.side, 'se-side-show');
581
+ dom.utils.removeClass(this.sideOpenBtn, 'active');
582
+ }
583
+ }
584
+ }
585
+
586
+ /**
587
+ * @private
588
+ * @param {__se__EditorCore} editor - editor instance
589
+ * @param {boolean} useSearch - Whether to use the search function
590
+ * @returns {{ html: HTMLElement, header: HTMLElement, titleArea: HTMLElement, tagArea: HTMLElement, body: HTMLElement, list: HTMLElement, side: HTMLElement, wrapper: HTMLElement, _loading: HTMLElement }} HTML
591
+ */
592
+ function CreateHTMLInfos(editor, useSearch) {
593
+ const lang = editor.lang;
594
+ const icons = editor.icons;
595
+ const htmlString = /*html*/ `
596
+ <div class="se-browser-content">
597
+ <div class="se-browser-header">
598
+ <button type="button" data-command="close" class="se-btn se-browser-close" class="close" title="${lang.close}" aria-label="${lang.close}">
599
+ ${icons.cancel}
600
+ </button>
601
+ <span class="se-browser-title"></span>
602
+ </div>
603
+ <div class="se-browser-wrapper">
604
+ <div class="se-browser-side"></div>
605
+ <div class="se-browser-main">
606
+ <div class="se-browser-bar">
607
+ <div class="se-browser-search">
608
+ <button class="se-btn se-side-open-btn">${icons.side_menu_hamburger}</button>
609
+ ${
610
+ useSearch
611
+ ? /*html*/ `
612
+ <form class="se-browser-search-form">
613
+ <input type="text" class="se-input-form" placeholder="${lang.search}" aria-label="${lang.search}">
614
+ <button type="submit" class="se-btn" title="${lang.search}" aria-label="${lang.search}">${icons.search}</button>
615
+ </form>`
616
+ : ''
617
+ }
618
+ </div>
619
+ </div>
620
+ <div class="se-browser-body">
621
+ <div class="se-browser-tags"></div>
622
+ <div class="se-loading-box sun-editor-common"><div class="se-loading-effect"></div></div>
623
+ <div class="se-browser-menus"></div>
624
+ <div class="se-browser-list"></div>
625
+ </div>
626
+ </div>
627
+ </div>
628
+ </div>`;
629
+
630
+ const content = dom.utils.createElement('DIV', { class: 'se-browser-inner' }, htmlString);
631
+
632
+ return {
633
+ html: content,
634
+ header: content.querySelector('.se-browser-header'),
635
+ titleArea: content.querySelector('.se-browser-title'),
636
+ tagArea: content.querySelector('.se-browser-tags'),
637
+ body: content.querySelector('.se-browser-body'),
638
+ list: content.querySelector('.se-browser-list'),
639
+ side: content.querySelector('.se-browser-side'),
640
+ wrapper: content.querySelector('.se-browser-wrapper'),
641
+ _loading: content.querySelector('.se-loading-box')
642
+ };
643
+ }
644
+
645
+ /**
646
+ * @private
647
+ * @this {{ thumbnail: ((...args: *) => *), props: Array<*> }}
648
+ * @description Define the HTML of the item to be put in "div.se-file-item-column".
649
+ * - Format: [ { src: "image src", name: "name(@option)", alt: "image alt(@option)", tag: "tag name(@option)" } ]
650
+ * @param {BrowserFile} item Item of the response data's array
651
+ */
652
+ function DrawItems(item) {
653
+ const srcName = item.src.split('/').pop();
654
+ const thumbnail = item.thumbnail || '';
655
+ const src = thumbnail || item.src;
656
+ const customProps = this.props?.map((v) => `data-${v}="${item[v]}"`).join(' ') || '';
657
+ const attrs = `data-type="${item.type}" data-command="${item.src}" data-name="${item.name || srcName}" data-thumbnail="${thumbnail}" data-extension="${item.src.split('.').pop()}" ${customProps}`;
658
+ const props = `class="${thumbnail || 'se-browser-empty-image'}" src="${src}" alt="${item.alt || srcName}" ${attrs}`;
659
+ return /*html*/ `
660
+ <div class="se-file-item-img">
661
+ ${this.thumbnail && !thumbnail && item.type !== 'image' ? `<div class="se-browser-empty-thumbnail" ${props}>${this.thumbnail(item)}</div>` : `<img class="${thumbnail || 'se-browser-empty-image'}" ${props}>`}
662
+ <div class="se-file-name-image se-file-name-back"></div>
663
+ <div class="se-file-name-image">${item.name || srcName}</div>
664
+ </div>`;
665
+ }
666
+
667
+ export default Browser;