@oat-sa/tao-core-ui 1.54.2 → 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (822) hide show
  1. package/LICENSE +339 -339
  2. package/README.md +17 -17
  3. package/dist/actionbar.js +105 -105
  4. package/dist/adder.js +109 -109
  5. package/dist/animable/absorbable/absorbable.js +42 -42
  6. package/dist/animable/absorbable/css/absorb.css +7 -7
  7. package/dist/animable/pulsable/css/pulse.css +7 -7
  8. package/dist/animable/pulsable/pulsable.js +36 -36
  9. package/dist/areaBroker.js +51 -51
  10. package/dist/autocomplete/css/autocomplete.css +7 -7
  11. package/dist/autocomplete.js +400 -400
  12. package/dist/autoscroll.js +22 -22
  13. package/dist/badge/badge.js +48 -48
  14. package/dist/badge/css/badge.css +7 -7
  15. package/dist/breadcrumbs/css/breadcrumbs.css +7 -7
  16. package/dist/breadcrumbs.js +70 -70
  17. package/dist/btngrouper.js +64 -64
  18. package/dist/bulkActionPopup/css/bulkActionPopup.css +7 -7
  19. package/dist/bulkActionPopup.js +104 -104
  20. package/dist/button.js +102 -102
  21. package/dist/calculator/css/calculator.css +10 -10
  22. package/dist/calculator.js +51 -51
  23. package/dist/cascadingComboBox.js +47 -47
  24. package/dist/ckeditor/ckConfigurator.js +47 -47
  25. package/dist/ckeditor/dtdHandler.js +110 -110
  26. package/dist/class/css/selector.css +7 -7
  27. package/dist/class/selector.js +111 -111
  28. package/dist/component/alignable.js +81 -81
  29. package/dist/component/containable.js +37 -37
  30. package/dist/component/css/components.css +7 -7
  31. package/dist/component/css/windowComponent.css +7 -7
  32. package/dist/component/draggable.js +24 -24
  33. package/dist/component/placeable.js +70 -70
  34. package/dist/component/resizable.js +60 -60
  35. package/dist/component/stackable.js +20 -20
  36. package/dist/component/windowed.js +59 -59
  37. package/dist/component.js +153 -153
  38. package/dist/container.js +76 -76
  39. package/dist/contextualPopup/css/contextualPopup.css +7 -7
  40. package/dist/contextualPopup.js +113 -113
  41. package/dist/dashboard/css/dashboard.css +7 -7
  42. package/dist/dashboard.js +75 -75
  43. package/dist/datalist/css/datalist.css +7 -7
  44. package/dist/datalist.js +160 -160
  45. package/dist/datatable/css/datatable.css +7 -7
  46. package/dist/datatable/filterStrategy/filterStrategy.js +27 -27
  47. package/dist/datatable/filterStrategy/multiple.js +25 -25
  48. package/dist/datatable/filterStrategy/single.js +25 -25
  49. package/dist/datatable.js +374 -374
  50. package/dist/dateRange/css/dateRange.css +7 -7
  51. package/dist/dateRange/dateRange.js +110 -110
  52. package/dist/datetime/css/picker.css +7 -7
  53. package/dist/datetime/picker.js +174 -174
  54. package/dist/deleter.js +92 -92
  55. package/dist/destination/css/selector.css +7 -7
  56. package/dist/destination/selector.js +65 -65
  57. package/dist/dialog/alert.js +26 -26
  58. package/dist/dialog/confirm.js +27 -27
  59. package/dist/dialog/confirmDelete.js +44 -44
  60. package/dist/dialog.js +156 -156
  61. package/dist/disabler.js +90 -90
  62. package/dist/documentViewer/css/documentViewer.css +7 -7
  63. package/dist/documentViewer/providers/pdfViewer/fallback/viewer.js +43 -43
  64. package/dist/documentViewer/providers/pdfViewer/pdfjs/areaBroker.js +25 -25
  65. package/dist/documentViewer/providers/pdfViewer/pdfjs/findBar.js +133 -133
  66. package/dist/documentViewer/providers/pdfViewer/pdfjs/pageView.js +102 -102
  67. package/dist/documentViewer/providers/pdfViewer/pdfjs/pagesManager.js +52 -52
  68. package/dist/documentViewer/providers/pdfViewer/pdfjs/searchEngine.js +125 -125
  69. package/dist/documentViewer/providers/pdfViewer/pdfjs/textManager.js +67 -67
  70. package/dist/documentViewer/providers/pdfViewer/pdfjs/viewer.js +94 -94
  71. package/dist/documentViewer/providers/pdfViewer/pdfjs/wrapper.js +111 -111
  72. package/dist/documentViewer/providers/pdfViewer.js +42 -42
  73. package/dist/documentViewer/viewerFactory.js +71 -71
  74. package/dist/documentViewer.js +99 -99
  75. package/dist/dropdown/css/dropdown.css +7 -7
  76. package/dist/dropdown.js +97 -97
  77. package/dist/durationer.js +58 -58
  78. package/dist/dynamicComponent/css/dynamicComponent.css +7 -7
  79. package/dist/dynamicComponent.js +116 -116
  80. package/dist/feedback.js +97 -97
  81. package/dist/filesender.js +26 -26
  82. package/dist/filter.js +47 -47
  83. package/dist/form/css/dropdownForm.css +7 -7
  84. package/dist/form/css/form.css +7 -7
  85. package/dist/form/dropdownForm.js +112 -112
  86. package/dist/form/form.js +245 -245
  87. package/dist/form/simpleForm.js +71 -71
  88. package/dist/form/validator/css/validator.css +7 -7
  89. package/dist/form/validator/renderer.js +65 -65
  90. package/dist/form/validator/validator.js +87 -87
  91. package/dist/form/widget/css/widget.css +7 -7
  92. package/dist/form/widget/definitions.js +24 -24
  93. package/dist/form/widget/loader.js +16 -16
  94. package/dist/form/widget/providers/checkBox.js +75 -75
  95. package/dist/form/widget/providers/comboBox.js +59 -59
  96. package/dist/form/widget/providers/default.js +35 -35
  97. package/dist/form/widget/providers/hidden.js +50 -50
  98. package/dist/form/widget/providers/hiddenBox.js +71 -71
  99. package/dist/form/widget/providers/radioBox.js +70 -70
  100. package/dist/form/widget/providers/textArea.js +48 -48
  101. package/dist/form/widget/providers/textBox.js +34 -34
  102. package/dist/form/widget/widget.js +154 -154
  103. package/dist/form.js +10 -10
  104. package/dist/formValidator/formValidator.js +61 -61
  105. package/dist/formValidator/highlighters/highlighter.js +41 -41
  106. package/dist/formValidator/highlighters/message.js +29 -29
  107. package/dist/formValidator/highlighters/tooltip.js +32 -32
  108. package/dist/generis/form/css/form.css +7 -7
  109. package/dist/generis/form/form.js +86 -86
  110. package/dist/generis/validator/css/validator.css +7 -7
  111. package/dist/generis/validator/validator.js +51 -51
  112. package/dist/generis/widget/checkBox/checkBox.js +52 -52
  113. package/dist/generis/widget/comboBox/comboBox.js +45 -45
  114. package/dist/generis/widget/css/widget.css +7 -7
  115. package/dist/generis/widget/hiddenBox/hiddenBox.js +53 -53
  116. package/dist/generis/widget/loader.js +20 -20
  117. package/dist/generis/widget/textBox/textBox.js +40 -40
  118. package/dist/generis/widget/widget.js +60 -60
  119. package/dist/groupedComboBox.js +49 -49
  120. package/dist/groupvalidator.js +19 -19
  121. package/dist/hider.js +41 -41
  122. package/dist/highlighter.js +262 -262
  123. package/dist/image/ImgStateActive/extractLabel.js +20 -20
  124. package/dist/image/ImgStateActive/initHelper.js +69 -69
  125. package/dist/image/ImgStateActive/initMediaEditor.js +32 -32
  126. package/dist/image/ImgStateActive/mediaSizer.js +16 -16
  127. package/dist/image/ImgStateActive.js +88 -88
  128. package/dist/incrementer.js +58 -58
  129. package/dist/inplacer.js +87 -87
  130. package/dist/interactUtils.js +42 -42
  131. package/dist/itemButtonList/css/item-button-list.css +23 -23
  132. package/dist/itemButtonList.js +115 -115
  133. package/dist/keyNavigation/navigableDomElement.js +76 -76
  134. package/dist/keyNavigation/navigator.js +158 -158
  135. package/dist/listbox/css/listbox.css +7 -7
  136. package/dist/listbox.js +97 -97
  137. package/dist/liststyler.js +57 -57
  138. package/dist/loadingButton/css/button.css +7 -7
  139. package/dist/loadingButton/loadingButton.js +48 -48
  140. package/dist/lock.js +125 -125
  141. package/dist/login/login.js +100 -100
  142. package/dist/maths/calculator/basicCalculator.js +63 -63
  143. package/dist/maths/calculator/calculatorComponent.js +29 -29
  144. package/dist/maths/calculator/core/areaBroker.js +25 -25
  145. package/dist/maths/calculator/core/board.js +313 -313
  146. package/dist/maths/calculator/core/expression.js +111 -111
  147. package/dist/maths/calculator/core/labels.js +29 -29
  148. package/dist/maths/calculator/core/plugin.js +19 -19
  149. package/dist/maths/calculator/core/terms.js +26 -26
  150. package/dist/maths/calculator/core/tokenizer.js +98 -98
  151. package/dist/maths/calculator/core/tokens.js +59 -59
  152. package/dist/maths/calculator/css/calculator.css +7 -7
  153. package/dist/maths/calculator/defaultCalculator.js +23 -23
  154. package/dist/maths/calculator/plugins/core/degrad.js +22 -22
  155. package/dist/maths/calculator/plugins/core/history.js +45 -45
  156. package/dist/maths/calculator/plugins/core/remind.js +22 -22
  157. package/dist/maths/calculator/plugins/core/stepNavigation.js +31 -31
  158. package/dist/maths/calculator/plugins/keyboard/templateKeyboard/templateKeyboard.js +61 -61
  159. package/dist/maths/calculator/plugins/modifiers/pow10.js +35 -35
  160. package/dist/maths/calculator/plugins/modifiers/sign.js +90 -90
  161. package/dist/maths/calculator/plugins/screen/simpleScreen/simpleScreen.js +60 -60
  162. package/dist/maths/calculator/pluginsLoader.js +21 -21
  163. package/dist/maths/calculator/scientificCalculator.js +91 -91
  164. package/dist/mediaEditor/mediaEditorComponent.js +64 -64
  165. package/dist/mediaEditor/plugins/mediaAlignment/helper.js +16 -16
  166. package/dist/mediaEditor/plugins/mediaAlignment/mediaAlignmentComponent.js +60 -60
  167. package/dist/mediaEditor/plugins/mediaAlignment/style.css +7 -7
  168. package/dist/mediaEditor/plugins/mediaDimension/helper.js +70 -70
  169. package/dist/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +153 -153
  170. package/dist/mediaEditor/plugins/mediaDimension/style.css +141 -141
  171. package/dist/mediaplayer/css/player.css +7 -7
  172. package/dist/mediaplayer/players/html5.js +65 -65
  173. package/dist/mediaplayer/players/youtube.js +52 -52
  174. package/dist/mediaplayer/players.js +18 -18
  175. package/dist/mediaplayer/support.js +55 -55
  176. package/dist/mediaplayer/utils/reminder.js +100 -100
  177. package/dist/mediaplayer/utils/timeObserver.js +92 -92
  178. package/dist/mediaplayer/youtubeManager.js +35 -35
  179. package/dist/mediaplayer.js +460 -460
  180. package/dist/mediasizer.js +135 -135
  181. package/dist/modal.js +87 -87
  182. package/dist/movableComponent.js +35 -35
  183. package/dist/pageSizeSelector.js +44 -44
  184. package/dist/pageStatus.js +33 -33
  185. package/dist/pagination/css/pagination.css +7 -7
  186. package/dist/pagination/paginationStrategy.js +23 -23
  187. package/dist/pagination/providers/pages.js +37 -37
  188. package/dist/pagination/providers/simple.js +35 -35
  189. package/dist/pagination.js +45 -45
  190. package/dist/previewer.js +67 -67
  191. package/dist/progressbar.js +58 -58
  192. package/dist/report.js +86 -86
  193. package/dist/resource/css/selector.css +7 -7
  194. package/dist/resource/filters.js +73 -73
  195. package/dist/resource/list.js +66 -66
  196. package/dist/resource/selectable.js +92 -92
  197. package/dist/resource/selector.js +195 -195
  198. package/dist/resource/tree.js +104 -104
  199. package/dist/resourcemgr/css/resourcemgr.css +7 -7
  200. package/dist/resourcemgr/fileBrowser.js +98 -95
  201. package/dist/resourcemgr/fileSelector.js +58 -58
  202. package/dist/resourcemgr/util/updatePermissions.js +4 -4
  203. package/dist/resourcemgr.js +62 -62
  204. package/dist/scroller.js +26 -26
  205. package/dist/searchModal/advancedSearch.js +130 -130
  206. package/dist/searchModal/css/advancedSearch.css +7 -7
  207. package/dist/searchModal/css/searchModal.css +7 -7
  208. package/dist/searchModal.js +96 -96
  209. package/dist/selecter.js +6 -6
  210. package/dist/stacker.js +43 -43
  211. package/dist/switch/css/switch.css +7 -7
  212. package/dist/switch/switch.js +75 -75
  213. package/dist/tableModel.js +33 -33
  214. package/dist/tabs/css/tabs.css +12 -12
  215. package/dist/tabs.js +219 -220
  216. package/dist/taskQueue/css/taskQueue.css +7 -7
  217. package/dist/taskQueue/status.js +72 -72
  218. package/dist/taskQueue/table.js +67 -67
  219. package/dist/taskQueue/taskQueue.js +18 -18
  220. package/dist/taskQueue/taskQueueModel.js +86 -86
  221. package/dist/taskQueue.js +47 -47
  222. package/dist/taskQueueButton/css/taskable.css +7 -7
  223. package/dist/taskQueueButton/css/treeButton.css +7 -7
  224. package/dist/taskQueueButton/standardButton.js +39 -39
  225. package/dist/taskQueueButton/taskable.js +54 -54
  226. package/dist/taskQueueButton/treeButton.js +56 -56
  227. package/dist/themeLoader.js +75 -75
  228. package/dist/themes.js +84 -84
  229. package/dist/toggler.js +57 -57
  230. package/dist/tooltip.js +54 -54
  231. package/dist/tooltipster.js +17 -17
  232. package/dist/transformer.js +117 -117
  233. package/dist/tristateCheckboxGroup/css/tristateCheckboxGroup.css +7 -7
  234. package/dist/tristateCheckboxGroup.js +75 -75
  235. package/dist/uploader.js +172 -175
  236. package/dist/validator/validators.js +48 -48
  237. package/dist/validator.js +24 -24
  238. package/dist/waitForMedia.js +33 -33
  239. package/dist/waitingDialog/css/waitingDialog.css +7 -7
  240. package/dist/waitingDialog/waitingDialog.js +54 -54
  241. package/package.json +107 -107
  242. package/scss/basic.scss +16 -16
  243. package/scss/ckeditor/skins/tao/scss/inc/_ck-icons.scss +59 -59
  244. package/scss/ckeditor/skins/tao/scss/inc/_tao.scss +59 -59
  245. package/scss/font/tao/tao.svg +234 -234
  246. package/scss/inc/_base.scss +495 -496
  247. package/scss/inc/_bootstrap.scss +6 -6
  248. package/scss/inc/_buttons.scss +114 -114
  249. package/scss/inc/_colors.scss +88 -88
  250. package/scss/inc/_feedback.scss +150 -150
  251. package/scss/inc/_flex-grid.scss +15 -15
  252. package/scss/inc/_fonts.scss +4 -4
  253. package/scss/inc/_forms.scss +832 -832
  254. package/scss/inc/_functions.scss +283 -283
  255. package/scss/inc/_jquery.nouislider.scss +254 -254
  256. package/scss/inc/_normalize.scss +528 -528
  257. package/scss/inc/_report.scss +67 -67
  258. package/scss/inc/_secondary-properties.scss +89 -89
  259. package/scss/inc/_select2.scss +634 -634
  260. package/scss/inc/_toolbars.scss +155 -155
  261. package/scss/inc/_tooltip.scss +312 -312
  262. package/scss/inc/_variables.scss +21 -21
  263. package/scss/inc/base/_highlight.scss +5 -5
  264. package/scss/inc/base/_list-style.scss +58 -58
  265. package/scss/inc/base/_svg.scss +3 -3
  266. package/scss/inc/base/_table.scss +62 -62
  267. package/scss/inc/fonts/_source-sans-pro.scss +29 -29
  268. package/scss/inc/fonts/_tao-icon-classes.scss +226 -226
  269. package/scss/inc/fonts/_tao-icon-def.scss +12 -12
  270. package/scss/inc/fonts/_tao-icon-vars.scss +240 -240
  271. package/src/.DS_Store +0 -0
  272. package/src/actionbar/tpl/main.tpl +8 -8
  273. package/src/actionbar.js +251 -251
  274. package/src/adder.js +250 -250
  275. package/src/animable/absorbable/absorbable.js +134 -134
  276. package/src/animable/absorbable/css/absorb.css +7 -7
  277. package/src/animable/absorbable/scss/absorb.scss +37 -37
  278. package/src/animable/pulsable/css/pulse.css +7 -7
  279. package/src/animable/pulsable/pulsable.js +90 -90
  280. package/src/animable/pulsable/scss/pulse.scss +22 -22
  281. package/src/areaBroker.js +160 -160
  282. package/src/autocomplete/css/autocomplete.css +7 -7
  283. package/src/autocomplete/scss/autocomplete.scss +37 -37
  284. package/src/autocomplete.js +1029 -1029
  285. package/src/autoscroll.js +57 -57
  286. package/src/badge/badge.js +119 -119
  287. package/src/badge/css/badge.css +7 -7
  288. package/src/badge/scss/badge.scss +92 -92
  289. package/src/badge/tpl/badge.tpl +4 -4
  290. package/src/breadcrumbs/css/breadcrumbs.css +7 -7
  291. package/src/breadcrumbs/scss/breadcrumbs.scss +52 -52
  292. package/src/breadcrumbs/tpl/breadcrumbs.tpl +20 -20
  293. package/src/breadcrumbs.js +99 -99
  294. package/src/btngrouper.js +213 -213
  295. package/src/bulkActionPopup/css/bulkActionPopup.css +7 -7
  296. package/src/bulkActionPopup/scss/bulkActionPopup.scss +63 -63
  297. package/src/bulkActionPopup/tpl/layout.tpl +76 -76
  298. package/src/bulkActionPopup/tpl/select.tpl +8 -8
  299. package/src/bulkActionPopup.js +274 -274
  300. package/src/button/tpl/button.tpl +4 -4
  301. package/src/button.js +135 -135
  302. package/src/calculator/css/calculator.css +10 -10
  303. package/src/calculator/scss/calculator.scss +139 -139
  304. package/src/calculator.js +188 -188
  305. package/src/cascadingComboBox.js +126 -126
  306. package/src/ckeditor/ckConfigurator.js +707 -707
  307. package/src/ckeditor/dtdHandler.js +1030 -1030
  308. package/src/class/css/selector.css +7 -7
  309. package/src/class/scss/selector.scss +101 -101
  310. package/src/class/selector.js +329 -329
  311. package/src/class/tpl/listItem.tpl +9 -9
  312. package/src/class/tpl/selector.tpl +10 -10
  313. package/src/component/alignable.js +274 -274
  314. package/src/component/containable.js +122 -122
  315. package/src/component/css/components.css +7 -7
  316. package/src/component/css/windowComponent.css +7 -7
  317. package/src/component/draggable.js +99 -99
  318. package/src/component/placeable.js +233 -233
  319. package/src/component/resizable.js +190 -190
  320. package/src/component/scss/components.scss +507 -507
  321. package/src/component/scss/windowComponent.scss +62 -62
  322. package/src/component/stackable.js +67 -67
  323. package/src/component/tpl/window.tpl +7 -7
  324. package/src/component/windowed.js +206 -206
  325. package/src/component.js +401 -401
  326. package/src/container.js +200 -200
  327. package/src/contextualPopup/css/contextualPopup.css +7 -7
  328. package/src/contextualPopup/scss/contextualPopup.scss +78 -78
  329. package/src/contextualPopup/tpl/popup.tpl +10 -10
  330. package/src/contextualPopup.js +297 -297
  331. package/src/css/basic.css +7764 -0
  332. package/src/css/ckeditor/skins/tao/css/dialog.css +950 -0
  333. package/src/css/ckeditor/skins/tao/css/editor.css +1850 -0
  334. package/src/dashboard/css/dashboard.css +7 -7
  335. package/src/dashboard/scss/dashboard.scss +93 -93
  336. package/src/dashboard/tpl/dashboard.tpl +16 -16
  337. package/src/dashboard/tpl/dashboardMetricsList.tpl +15 -15
  338. package/src/dashboard.js +184 -184
  339. package/src/datalist/css/datalist.css +7 -7
  340. package/src/datalist/scss/datalist.scss +116 -116
  341. package/src/datalist/tpl/list.tpl +24 -24
  342. package/src/datalist/tpl/main.tpl +44 -44
  343. package/src/datalist.js +500 -500
  344. package/src/datatable/css/datatable.css +7 -7
  345. package/src/datatable/filterStrategy/filterStrategy.js +70 -70
  346. package/src/datatable/filterStrategy/multiple.js +126 -126
  347. package/src/datatable/filterStrategy/single.js +108 -108
  348. package/src/datatable/scss/datatable.scss +146 -146
  349. package/src/datatable/tpl/button.tpl +6 -6
  350. package/src/datatable/tpl/layout.tpl +158 -158
  351. package/src/datatable.js +1056 -1056
  352. package/src/dateRange/css/dateRange.css +7 -7
  353. package/src/dateRange/dateRange.js +341 -341
  354. package/src/dateRange/scss/dateRange.scss +7 -7
  355. package/src/dateRange/tpl/select.tpl +18 -18
  356. package/src/datetime/css/picker.css +7 -7
  357. package/src/datetime/picker.js +576 -576
  358. package/src/datetime/scss/picker.scss +192 -192
  359. package/src/datetime/tpl/picker.tpl +18 -18
  360. package/src/deleter/undo.tpl +6 -6
  361. package/src/deleter.js +296 -296
  362. package/src/destination/css/selector.css +7 -7
  363. package/src/destination/scss/selector.scss +36 -36
  364. package/src/destination/selector.js +195 -195
  365. package/src/destination/tpl/selector.tpl +13 -13
  366. package/src/dialog/alert.js +70 -70
  367. package/src/dialog/confirm.js +85 -85
  368. package/src/dialog/confirmDelete.js +95 -95
  369. package/src/dialog/tpl/body.tpl +24 -24
  370. package/src/dialog/tpl/buttons.tpl +6 -6
  371. package/src/dialog/tpl/checkbox.tpl +5 -5
  372. package/src/dialog.js +517 -517
  373. package/src/disabler.js +230 -230
  374. package/src/documentViewer/css/documentViewer.css +7 -7
  375. package/src/documentViewer/providers/pdfViewer/fallback/viewer.js +69 -69
  376. package/src/documentViewer/providers/pdfViewer/pdfjs/areaBroker.js +41 -41
  377. package/src/documentViewer/providers/pdfViewer/pdfjs/findBar.js +475 -475
  378. package/src/documentViewer/providers/pdfViewer/pdfjs/findBar.tpl +20 -20
  379. package/src/documentViewer/providers/pdfViewer/pdfjs/match.tpl +1 -1
  380. package/src/documentViewer/providers/pdfViewer/pdfjs/page.tpl +4 -4
  381. package/src/documentViewer/providers/pdfViewer/pdfjs/pageView.js +318 -318
  382. package/src/documentViewer/providers/pdfViewer/pdfjs/pagesManager.js +167 -167
  383. package/src/documentViewer/providers/pdfViewer/pdfjs/searchEngine.js +451 -451
  384. package/src/documentViewer/providers/pdfViewer/pdfjs/textManager.js +252 -252
  385. package/src/documentViewer/providers/pdfViewer/pdfjs/viewer.js +299 -299
  386. package/src/documentViewer/providers/pdfViewer/pdfjs/viewer.tpl +16 -16
  387. package/src/documentViewer/providers/pdfViewer/pdfjs/wrapper.js +351 -351
  388. package/src/documentViewer/providers/pdfViewer.js +93 -93
  389. package/src/documentViewer/scss/documentViewer.scss +184 -184
  390. package/src/documentViewer/viewerFactory.js +191 -191
  391. package/src/documentViewer.js +238 -238
  392. package/src/dropdown/css/dropdown.css +7 -7
  393. package/src/dropdown/scss/dropdown.scss +99 -99
  394. package/src/dropdown/tpl/dropdown.tpl +8 -8
  395. package/src/dropdown/tpl/list-item.tpl +4 -4
  396. package/src/dropdown.js +255 -255
  397. package/src/durationer.js +222 -222
  398. package/src/dynamicComponent/css/dynamicComponent.css +7 -7
  399. package/src/dynamicComponent/scss/dynamicComponent.scss +98 -98
  400. package/src/dynamicComponent/tpl/layout.tpl +17 -17
  401. package/src/dynamicComponent.js +554 -554
  402. package/src/feedback/feedback.tpl +7 -7
  403. package/src/feedback.js +295 -295
  404. package/src/filesender.js +114 -114
  405. package/src/filter/template.tpl +5 -5
  406. package/src/filter.js +135 -135
  407. package/src/form/css/dropdownForm.css +7 -7
  408. package/src/form/css/form.css +7 -7
  409. package/src/form/dropdownForm.js +281 -281
  410. package/src/form/form.js +688 -688
  411. package/src/form/scss/dropdownForm.scss +60 -60
  412. package/src/form/scss/form.scss +25 -25
  413. package/src/form/simpleForm.js +125 -125
  414. package/src/form/tpl/dropdownForm.tpl +4 -4
  415. package/src/form/tpl/form.tpl +7 -7
  416. package/src/form/validator/css/validator.css +7 -7
  417. package/src/form/validator/renderer.js +118 -118
  418. package/src/form/validator/scss/validator.scss +14 -14
  419. package/src/form/validator/tpl/message.tpl +1 -1
  420. package/src/form/validator/tpl/validator.tpl +1 -1
  421. package/src/form/validator/validator.js +220 -220
  422. package/src/form/widget/css/widget.css +7 -7
  423. package/src/form/widget/definitions.js +51 -51
  424. package/src/form/widget/loader.js +40 -40
  425. package/src/form/widget/providers/checkBox.js +138 -138
  426. package/src/form/widget/providers/comboBox.js +63 -63
  427. package/src/form/widget/providers/default.js +90 -90
  428. package/src/form/widget/providers/hidden.js +62 -62
  429. package/src/form/widget/providers/hiddenBox.js +152 -152
  430. package/src/form/widget/providers/radioBox.js +99 -99
  431. package/src/form/widget/providers/textArea.js +52 -52
  432. package/src/form/widget/providers/textBox.js +48 -48
  433. package/src/form/widget/scss/widget.scss +55 -55
  434. package/src/form/widget/tpl/checkBox.tpl +25 -25
  435. package/src/form/widget/tpl/comboBox.tpl +13 -13
  436. package/src/form/widget/tpl/hidden.tpl +1 -1
  437. package/src/form/widget/tpl/hiddenBox.tpl +17 -17
  438. package/src/form/widget/tpl/label.tpl +6 -6
  439. package/src/form/widget/tpl/radioBox.tpl +25 -25
  440. package/src/form/widget/tpl/textArea.tpl +8 -8
  441. package/src/form/widget/tpl/widget.tpl +8 -8
  442. package/src/form/widget/widget.js +372 -372
  443. package/src/form.js +53 -53
  444. package/src/formValidator/formValidator.js +253 -253
  445. package/src/formValidator/highlighters/highlighter.js +102 -102
  446. package/src/formValidator/highlighters/message.js +70 -70
  447. package/src/formValidator/highlighters/tooltip.js +78 -78
  448. package/src/generis/form/css/form.css +7 -7
  449. package/src/generis/form/form.js +239 -239
  450. package/src/generis/form/readme.md +70 -70
  451. package/src/generis/form/scss/form.scss +23 -23
  452. package/src/generis/form/tpl/form.tpl +16 -16
  453. package/src/generis/validator/css/validator.css +7 -7
  454. package/src/generis/validator/readme.md +46 -46
  455. package/src/generis/validator/scss/validator.scss +13 -13
  456. package/src/generis/validator/validator.js +128 -128
  457. package/src/generis/widget/checkBox/checkBox.js +112 -112
  458. package/src/generis/widget/checkBox/checkBox.tpl +18 -18
  459. package/src/generis/widget/comboBox/comboBox.js +67 -67
  460. package/src/generis/widget/comboBox/comboBox.tpl +12 -12
  461. package/src/generis/widget/css/widget.css +7 -7
  462. package/src/generis/widget/hiddenBox/hiddenBox.js +132 -132
  463. package/src/generis/widget/hiddenBox/hiddenBox.tpl +16 -16
  464. package/src/generis/widget/loader.js +49 -49
  465. package/src/generis/widget/readme.md +59 -59
  466. package/src/generis/widget/scss/widget.scss +61 -61
  467. package/src/generis/widget/textBox/textBox.js +65 -65
  468. package/src/generis/widget/textBox/textBox.tpl +7 -7
  469. package/src/generis/widget/widget.js +164 -164
  470. package/src/generis/widget/widget.tpl +5 -5
  471. package/src/groupedComboBox.js +99 -99
  472. package/src/groupvalidator.js +84 -84
  473. package/src/hider.js +88 -88
  474. package/src/highlighter.js +1166 -1166
  475. package/src/image/ImgStateActive/extractLabel.js +29 -29
  476. package/src/image/ImgStateActive/initHelper.js +135 -135
  477. package/src/image/ImgStateActive/initMediaEditor.js +91 -91
  478. package/src/image/ImgStateActive/mediaSizer.js +53 -53
  479. package/src/image/ImgStateActive.js +115 -115
  480. package/src/incrementer.js +319 -319
  481. package/src/inplacer.js +316 -316
  482. package/src/interactUtils.js +140 -140
  483. package/src/itemButtonList/css/item-button-list.css +23 -23
  484. package/src/itemButtonList/scss/item-button-list.scss +236 -236
  485. package/src/itemButtonList/tpl/itemButtonList.tpl +21 -21
  486. package/src/itemButtonList.js +274 -274
  487. package/src/keyNavigation/navigableDomElement.js +282 -282
  488. package/src/keyNavigation/navigator.js +543 -543
  489. package/src/listbox/css/listbox.css +7 -7
  490. package/src/listbox/scss/listbox.scss +116 -116
  491. package/src/listbox/tpl/list.tpl +14 -14
  492. package/src/listbox/tpl/main.tpl +9 -9
  493. package/src/listbox.js +251 -251
  494. package/src/liststyler.js +155 -155
  495. package/src/loadingButton/css/button.css +7 -7
  496. package/src/loadingButton/loadingButton.js +110 -110
  497. package/src/loadingButton/scss/button.scss +41 -41
  498. package/src/loadingButton/tpl/button.tpl +5 -5
  499. package/src/lock/lock.tpl +16 -16
  500. package/src/lock.js +395 -395
  501. package/src/login/login.js +322 -322
  502. package/src/login/tpl/login.tpl +29 -29
  503. package/src/login/tpl/passwordReveal.tpl +7 -7
  504. package/src/maths/calculator/basicCalculator.js +55 -55
  505. package/src/maths/calculator/calculatorComponent.js +128 -128
  506. package/src/maths/calculator/core/areaBroker.js +38 -38
  507. package/src/maths/calculator/core/board.js +841 -841
  508. package/src/maths/calculator/core/expression.js +430 -430
  509. package/src/maths/calculator/core/labels.js +116 -116
  510. package/src/maths/calculator/core/plugin.js +40 -40
  511. package/src/maths/calculator/core/terms.js +459 -459
  512. package/src/maths/calculator/core/tokenizer.js +245 -245
  513. package/src/maths/calculator/core/tokens.js +178 -178
  514. package/src/maths/calculator/core/tpl/board.tpl +4 -4
  515. package/src/maths/calculator/css/calculator.css +7 -7
  516. package/src/maths/calculator/defaultCalculator.js +66 -66
  517. package/src/maths/calculator/plugins/core/degrad.js +90 -90
  518. package/src/maths/calculator/plugins/core/history.js +166 -166
  519. package/src/maths/calculator/plugins/core/remind.js +96 -96
  520. package/src/maths/calculator/plugins/core/stepNavigation.js +175 -175
  521. package/src/maths/calculator/plugins/keyboard/templateKeyboard/defaultTemplate.tpl +36 -36
  522. package/src/maths/calculator/plugins/keyboard/templateKeyboard/templateKeyboard.js +91 -91
  523. package/src/maths/calculator/plugins/modifiers/pow10.js +143 -143
  524. package/src/maths/calculator/plugins/modifiers/sign.js +339 -339
  525. package/src/maths/calculator/plugins/screen/simpleScreen/defaultTemplate.tpl +3 -3
  526. package/src/maths/calculator/plugins/screen/simpleScreen/history.tpl +3 -3
  527. package/src/maths/calculator/plugins/screen/simpleScreen/simpleScreen.js +191 -191
  528. package/src/maths/calculator/pluginsLoader.js +46 -46
  529. package/src/maths/calculator/scientificCalculator.js +74 -74
  530. package/src/maths/calculator/scss/calculator.scss +396 -396
  531. package/src/maths/calculator/tpl/basicKeyboard.tpl +37 -37
  532. package/src/maths/calculator/tpl/basicScreen.tpl +2 -2
  533. package/src/maths/calculator/tpl/scientificKeyboard.tpl +61 -61
  534. package/src/maths/calculator/tpl/scientificScreen.tpl +3 -3
  535. package/src/mediaEditor/mediaEditorComponent.js +141 -141
  536. package/src/mediaEditor/plugins/mediaAlignment/helper.js +58 -58
  537. package/src/mediaEditor/plugins/mediaAlignment/mediaAlignmentComponent.js +96 -96
  538. package/src/mediaEditor/plugins/mediaAlignment/style.css +7 -7
  539. package/src/mediaEditor/plugins/mediaAlignment/tpl/mediaAlignment.tpl +25 -25
  540. package/src/mediaEditor/plugins/mediaDimension/helper.js +189 -189
  541. package/src/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +561 -561
  542. package/src/mediaEditor/plugins/mediaDimension/style.css +141 -141
  543. package/src/mediaEditor/plugins/mediaDimension/tpl/mediaDimension.tpl +55 -55
  544. package/src/mediaEditor/tpl/editor.tpl +4 -4
  545. package/src/mediaplayer/css/player.css +7 -7
  546. package/src/mediaplayer/players/html5.js +564 -564
  547. package/src/mediaplayer/players/youtube.js +323 -323
  548. package/src/mediaplayer/players.js +29 -29
  549. package/src/mediaplayer/readme.md +305 -305
  550. package/src/mediaplayer/scss/player.scss +530 -530
  551. package/src/mediaplayer/support.js +126 -126
  552. package/src/mediaplayer/tpl/audio.tpl +6 -6
  553. package/src/mediaplayer/tpl/player.tpl +37 -37
  554. package/src/mediaplayer/tpl/source.tpl +1 -1
  555. package/src/mediaplayer/tpl/video.tpl +6 -6
  556. package/src/mediaplayer/tpl/youtube.tpl +1 -1
  557. package/src/mediaplayer/utils/reminder.js +184 -184
  558. package/src/mediaplayer/utils/timeObserver.js +143 -143
  559. package/src/mediaplayer/youtubeManager.js +161 -161
  560. package/src/mediaplayer.js +1606 -1606
  561. package/src/mediasizer/mediasizer.tpl +55 -55
  562. package/src/mediasizer.js +635 -635
  563. package/src/modal.js +365 -365
  564. package/src/movableComponent.js +78 -78
  565. package/src/pageSizeSelector/tpl/pageSizeSelector.tpl +9 -9
  566. package/src/pageSizeSelector.js +107 -107
  567. package/src/pageStatus.js +147 -147
  568. package/src/pagination/css/pagination.css +7 -7
  569. package/src/pagination/paginationStrategy.js +53 -53
  570. package/src/pagination/providers/pages.js +161 -161
  571. package/src/pagination/providers/simple.js +74 -74
  572. package/src/pagination/providers/tpl/pages/page.tpl +1 -1
  573. package/src/pagination/providers/tpl/pages.tpl +8 -8
  574. package/src/pagination/providers/tpl/simple.tpl +7 -7
  575. package/src/pagination/scss/pagination.scss +111 -111
  576. package/src/pagination.js +237 -237
  577. package/src/previewer.js +300 -300
  578. package/src/progressbar.js +165 -165
  579. package/src/report/feedback.tpl +11 -11
  580. package/src/report/layout.tpl +10 -10
  581. package/src/report.js +184 -184
  582. package/src/resource/css/selector.css +7 -7
  583. package/src/resource/filters.js +208 -208
  584. package/src/resource/list.js +200 -200
  585. package/src/resource/scss/_filters.scss +26 -26
  586. package/src/resource/scss/_resource-list.scss +107 -107
  587. package/src/resource/scss/_resource-tree.scss +205 -205
  588. package/src/resource/scss/selector.scss +187 -187
  589. package/src/resource/selectable.js +322 -322
  590. package/src/resource/selector.js +871 -871
  591. package/src/resource/tpl/filters.tpl +2 -2
  592. package/src/resource/tpl/list.tpl +7 -7
  593. package/src/resource/tpl/listNode.tpl +4 -4
  594. package/src/resource/tpl/selector.tpl +46 -46
  595. package/src/resource/tpl/tree.tpl +4 -4
  596. package/src/resource/tpl/treeNode.tpl +30 -30
  597. package/src/resource/tree.js +400 -400
  598. package/src/resourcemgr/css/resourcemgr.css +7 -7
  599. package/src/resourcemgr/fileBrowser.js +381 -370
  600. package/src/resourcemgr/filePreview.js +73 -73
  601. package/src/resourcemgr/fileSelector.js +348 -348
  602. package/src/resourcemgr/scss/resourcemgr.scss +254 -254
  603. package/src/resourcemgr/tpl/fileSelect.tpl +39 -39
  604. package/src/resourcemgr/tpl/folder.tpl +11 -11
  605. package/src/resourcemgr/tpl/layout.tpl +84 -84
  606. package/src/resourcemgr/tpl/rootFolder.tpl +13 -13
  607. package/src/resourcemgr/util/updatePermissions.js +53 -53
  608. package/src/resourcemgr.js +214 -214
  609. package/src/scroller.js +94 -94
  610. package/src/scss/.DS_Store +0 -0
  611. package/src/scss/basic.scss +16 -0
  612. package/src/scss/ckeditor/skins/tao/scss/dialog.scss +763 -0
  613. package/src/scss/ckeditor/skins/tao/scss/editor.scss +111 -0
  614. package/src/scss/ckeditor/skins/tao/scss/inc/_ck-icons.scss +59 -0
  615. package/src/scss/ckeditor/skins/tao/scss/inc/_colorpanel.scss +118 -0
  616. package/src/scss/ckeditor/skins/tao/scss/inc/_elementspath.scss +69 -0
  617. package/src/scss/ckeditor/skins/tao/scss/inc/_mainui.scss +194 -0
  618. package/src/scss/ckeditor/skins/tao/scss/inc/_menu.scss +181 -0
  619. package/src/scss/ckeditor/skins/tao/scss/inc/_panel.scss +200 -0
  620. package/src/scss/ckeditor/skins/tao/scss/inc/_presets.scss +32 -0
  621. package/src/scss/ckeditor/skins/tao/scss/inc/_reset.scss +101 -0
  622. package/src/scss/ckeditor/skins/tao/scss/inc/_richcombo.scss +213 -0
  623. package/src/scss/ckeditor/skins/tao/scss/inc/_tao.scss +59 -0
  624. package/src/scss/ckeditor/skins/tao/scss/inc/_toolbar.scss +301 -0
  625. package/src/scss/font/source-sans-pro/source-sans-pro-italic.eot +0 -0
  626. package/src/scss/font/source-sans-pro/source-sans-pro-italic.eot.b64 +1 -0
  627. package/src/scss/font/source-sans-pro/source-sans-pro-italic.woff +0 -0
  628. package/src/scss/font/source-sans-pro/source-sans-pro-italic.woff.b64 +1 -0
  629. package/src/scss/font/source-sans-pro/source-sans-pro-regular.eot +0 -0
  630. package/src/scss/font/source-sans-pro/source-sans-pro-regular.eot.b64 +1 -0
  631. package/src/scss/font/source-sans-pro/source-sans-pro-regular.woff +0 -0
  632. package/src/scss/font/source-sans-pro/source-sans-pro-regular.woff.b64 +1 -0
  633. package/src/scss/font/source-sans-pro/source-sans-pro-semibold-italic.eot +0 -0
  634. package/src/scss/font/source-sans-pro/source-sans-pro-semibold-italic.eot.b64 +1 -0
  635. package/src/scss/font/source-sans-pro/source-sans-pro-semibold-italic.woff +0 -0
  636. package/src/scss/font/source-sans-pro/source-sans-pro-semibold-italic.woff.b64 +1 -0
  637. package/src/scss/font/source-sans-pro/source-sans-pro-semibold.eot +0 -0
  638. package/src/scss/font/source-sans-pro/source-sans-pro-semibold.eot.b64 +1 -0
  639. package/src/scss/font/source-sans-pro/source-sans-pro-semibold.woff +0 -0
  640. package/src/scss/font/source-sans-pro/source-sans-pro-semibold.woff.b64 +1 -0
  641. package/src/scss/font/tao/tao.eot +0 -0
  642. package/src/scss/font/tao/tao.svg +235 -0
  643. package/src/scss/font/tao/tao.ttf +0 -0
  644. package/src/scss/font/tao/tao.woff +0 -0
  645. package/src/scss/inc/_base.scss +496 -0
  646. package/src/scss/inc/_bootstrap.scss +6 -0
  647. package/src/scss/inc/_buttons.scss +114 -0
  648. package/src/scss/inc/_colors.scss +88 -0
  649. package/src/scss/inc/_feedback.scss +150 -0
  650. package/src/scss/inc/_flex-grid.scss +15 -0
  651. package/src/scss/inc/_fonts.scss +4 -0
  652. package/src/scss/inc/_forms.scss +827 -0
  653. package/src/scss/inc/_functions.scss +283 -0
  654. package/src/scss/inc/_grid.scss +66 -0
  655. package/src/scss/inc/_jquery.nouislider.scss +254 -0
  656. package/src/scss/inc/_normalize.scss +528 -0
  657. package/src/scss/inc/_report.scss +68 -0
  658. package/src/scss/inc/_secondary-properties.scss +89 -0
  659. package/src/scss/inc/_select2.scss +634 -0
  660. package/src/scss/inc/_toolbars.scss +155 -0
  661. package/src/scss/inc/_tooltip.scss +312 -0
  662. package/src/scss/inc/_variables.scss +21 -0
  663. package/src/scss/inc/base/_highlight.scss +5 -0
  664. package/src/scss/inc/base/_list-style.scss +59 -0
  665. package/src/scss/inc/base/_svg.scss +3 -0
  666. package/src/scss/inc/base/_table.scss +63 -0
  667. package/src/scss/inc/fonts/_source-sans-pro.scss +29 -0
  668. package/src/scss/inc/fonts/_tao-icon-classes.scss +226 -0
  669. package/src/scss/inc/fonts/_tao-icon-def.scss +12 -0
  670. package/src/scss/inc/fonts/_tao-icon-vars.scss +240 -0
  671. package/src/searchModal/advancedSearch.js +601 -601
  672. package/src/searchModal/css/advancedSearch.css +7 -7
  673. package/src/searchModal/css/searchModal.css +7 -7
  674. package/src/searchModal/scss/advancedSearch.scss +177 -177
  675. package/src/searchModal/scss/searchModal.scss +375 -375
  676. package/src/searchModal/tpl/advanced-search.tpl +9 -9
  677. package/src/searchModal/tpl/info-message.tpl +3 -3
  678. package/src/searchModal/tpl/invalid-criteria-warning.tpl +10 -10
  679. package/src/searchModal/tpl/layout.tpl +25 -25
  680. package/src/searchModal/tpl/list-checkbox-criterion.tpl +12 -12
  681. package/src/searchModal/tpl/list-select-criterion.tpl +6 -6
  682. package/src/searchModal/tpl/text-criterion.tpl +6 -6
  683. package/src/searchModal.js +496 -496
  684. package/src/selecter.js +43 -43
  685. package/src/stacker.js +133 -133
  686. package/src/switch/css/switch.css +7 -7
  687. package/src/switch/scss/switch.scss +83 -83
  688. package/src/switch/switch.js +195 -195
  689. package/src/switch/tpl/switch.tpl +7 -7
  690. package/src/tableModel.js +112 -112
  691. package/src/tabs/css/tabs.css +12 -12
  692. package/src/tabs/scss/tabs.scss +50 -50
  693. package/src/tabs/tpl/panel.tpl +3 -3
  694. package/src/tabs/tpl/tabs.tpl +10 -10
  695. package/src/tabs.js +528 -529
  696. package/src/taskQueue/css/taskQueue.css +7 -7
  697. package/src/taskQueue/scss/taskQueue.scss +47 -47
  698. package/src/taskQueue/status.js +228 -228
  699. package/src/taskQueue/table.js +350 -350
  700. package/src/taskQueue/taskQueue.js +33 -33
  701. package/src/taskQueue/taskQueueModel.js +548 -548
  702. package/src/taskQueue/tpl/statusMessage.tpl +7 -7
  703. package/src/taskQueue.js +218 -218
  704. package/src/taskQueueButton/css/taskable.css +7 -7
  705. package/src/taskQueueButton/css/treeButton.css +7 -7
  706. package/src/taskQueueButton/scss/taskable.scss +4 -4
  707. package/src/taskQueueButton/scss/treeButton.scss +34 -34
  708. package/src/taskQueueButton/standardButton.js +108 -108
  709. package/src/taskQueueButton/taskable.js +202 -202
  710. package/src/taskQueueButton/tpl/report.tpl +5 -5
  711. package/src/taskQueueButton/tpl/treeButton.tpl +6 -6
  712. package/src/taskQueueButton/treeButton.js +109 -109
  713. package/src/themeLoader.js +252 -252
  714. package/src/themes.js +162 -162
  715. package/src/toggler.js +200 -200
  716. package/src/tooltip/default.tpl +3 -3
  717. package/src/tooltip.js +160 -160
  718. package/src/tooltipster.js +25 -25
  719. package/src/transformer.js +327 -327
  720. package/src/tristateCheckboxGroup/css/tristateCheckboxGroup.css +7 -7
  721. package/src/tristateCheckboxGroup/scss/tristateCheckboxGroup.scss +15 -15
  722. package/src/tristateCheckboxGroup/tpl/li.tpl +6 -6
  723. package/src/tristateCheckboxGroup.js +207 -207
  724. package/src/uploader/fileEntry.tpl +6 -6
  725. package/src/uploader/uploader.tpl +32 -32
  726. package/src/uploader.js +594 -613
  727. package/src/validator/Report.js +10 -10
  728. package/src/validator/Validator.js +108 -108
  729. package/src/validator/validators.js +220 -220
  730. package/src/validator.js +264 -264
  731. package/src/waitForMedia.js +82 -82
  732. package/src/waitingDialog/css/waitingDialog.css +7 -7
  733. package/src/waitingDialog/scss/waitingDialog.scss +34 -34
  734. package/src/waitingDialog/waitingDialog.js +240 -240
  735. package/dist/animable/absorbable/css/absorb.css.map +0 -1
  736. package/dist/animable/pulsable/css/pulse.css.map +0 -1
  737. package/dist/autocomplete/css/autocomplete.css.map +0 -1
  738. package/dist/badge/css/badge.css.map +0 -1
  739. package/dist/breadcrumbs/css/breadcrumbs.css.map +0 -1
  740. package/dist/bulkActionPopup/css/bulkActionPopup.css.map +0 -1
  741. package/dist/calculator/css/calculator.css.map +0 -1
  742. package/dist/class/css/selector.css.map +0 -1
  743. package/dist/component/css/components.css.map +0 -1
  744. package/dist/component/css/windowComponent.css.map +0 -1
  745. package/dist/contextualPopup/css/contextualPopup.css.map +0 -1
  746. package/dist/dashboard/css/dashboard.css.map +0 -1
  747. package/dist/datalist/css/datalist.css.map +0 -1
  748. package/dist/datatable/css/datatable.css.map +0 -1
  749. package/dist/dateRange/css/dateRange.css.map +0 -1
  750. package/dist/datetime/css/picker.css.map +0 -1
  751. package/dist/destination/css/selector.css.map +0 -1
  752. package/dist/documentViewer/css/documentViewer.css.map +0 -1
  753. package/dist/dropdown/css/dropdown.css.map +0 -1
  754. package/dist/dynamicComponent/css/dynamicComponent.css.map +0 -1
  755. package/dist/form/css/dropdownForm.css.map +0 -1
  756. package/dist/form/css/form.css.map +0 -1
  757. package/dist/form/validator/css/validator.css.map +0 -1
  758. package/dist/form/widget/css/widget.css.map +0 -1
  759. package/dist/generis/form/css/form.css.map +0 -1
  760. package/dist/generis/validator/css/validator.css.map +0 -1
  761. package/dist/generis/widget/css/widget.css.map +0 -1
  762. package/dist/itemButtonList/css/item-button-list.css.map +0 -1
  763. package/dist/listbox/css/listbox.css.map +0 -1
  764. package/dist/loadingButton/css/button.css.map +0 -1
  765. package/dist/maths/calculator/css/calculator.css.map +0 -1
  766. package/dist/mediaplayer/css/player.css.map +0 -1
  767. package/dist/pagination/css/pagination.css.map +0 -1
  768. package/dist/resource/css/selector.css.map +0 -1
  769. package/dist/resourcemgr/css/resourcemgr.css.map +0 -1
  770. package/dist/searchModal/css/advancedSearch.css.map +0 -1
  771. package/dist/searchModal/css/searchModal.css.map +0 -1
  772. package/dist/switch/css/switch.css.map +0 -1
  773. package/dist/tabs/css/tabs.css.map +0 -1
  774. package/dist/taskQueue/css/taskQueue.css.map +0 -1
  775. package/dist/taskQueueButton/css/taskable.css.map +0 -1
  776. package/dist/taskQueueButton/css/treeButton.css.map +0 -1
  777. package/dist/tristateCheckboxGroup/css/tristateCheckboxGroup.css.map +0 -1
  778. package/dist/waitingDialog/css/waitingDialog.css.map +0 -1
  779. package/src/animable/absorbable/css/absorb.css.map +0 -1
  780. package/src/animable/pulsable/css/pulse.css.map +0 -1
  781. package/src/autocomplete/css/autocomplete.css.map +0 -1
  782. package/src/badge/css/badge.css.map +0 -1
  783. package/src/breadcrumbs/css/breadcrumbs.css.map +0 -1
  784. package/src/bulkActionPopup/css/bulkActionPopup.css.map +0 -1
  785. package/src/calculator/css/calculator.css.map +0 -1
  786. package/src/class/css/selector.css.map +0 -1
  787. package/src/component/css/components.css.map +0 -1
  788. package/src/component/css/windowComponent.css.map +0 -1
  789. package/src/contextualPopup/css/contextualPopup.css.map +0 -1
  790. package/src/dashboard/css/dashboard.css.map +0 -1
  791. package/src/datalist/css/datalist.css.map +0 -1
  792. package/src/datatable/css/datatable.css.map +0 -1
  793. package/src/dateRange/css/dateRange.css.map +0 -1
  794. package/src/datetime/css/picker.css.map +0 -1
  795. package/src/destination/css/selector.css.map +0 -1
  796. package/src/documentViewer/css/documentViewer.css.map +0 -1
  797. package/src/dropdown/css/dropdown.css.map +0 -1
  798. package/src/dynamicComponent/css/dynamicComponent.css.map +0 -1
  799. package/src/form/css/dropdownForm.css.map +0 -1
  800. package/src/form/css/form.css.map +0 -1
  801. package/src/form/validator/css/validator.css.map +0 -1
  802. package/src/form/widget/css/widget.css.map +0 -1
  803. package/src/generis/form/css/form.css.map +0 -1
  804. package/src/generis/validator/css/validator.css.map +0 -1
  805. package/src/generis/widget/css/widget.css.map +0 -1
  806. package/src/itemButtonList/css/item-button-list.css.map +0 -1
  807. package/src/listbox/css/listbox.css.map +0 -1
  808. package/src/loadingButton/css/button.css.map +0 -1
  809. package/src/maths/calculator/css/calculator.css.map +0 -1
  810. package/src/mediaplayer/css/player.css.map +0 -1
  811. package/src/pagination/css/pagination.css.map +0 -1
  812. package/src/resource/css/selector.css.map +0 -1
  813. package/src/resourcemgr/css/resourcemgr.css.map +0 -1
  814. package/src/searchModal/css/advancedSearch.css.map +0 -1
  815. package/src/searchModal/css/searchModal.css.map +0 -1
  816. package/src/switch/css/switch.css.map +0 -1
  817. package/src/tabs/css/tabs.css.map +0 -1
  818. package/src/taskQueue/css/taskQueue.css.map +0 -1
  819. package/src/taskQueueButton/css/taskable.css.map +0 -1
  820. package/src/taskQueueButton/css/treeButton.css.map +0 -1
  821. package/src/tristateCheckboxGroup/css/tristateCheckboxGroup.css.map +0 -1
  822. package/src/waitingDialog/css/waitingDialog.css.map +0 -1
@@ -1,1166 +1,1166 @@
1
- /**
2
- * This program is free software; you can redistribute it and/or
3
- * modify it under the terms of the GNU General Public License
4
- * as published by the Free Software Foundation; under version 2
5
- * of the License (non-upgradable).
6
- *
7
- * This program is distributed in the hope that it will be useful,
8
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- * GNU General Public License for more details.
11
- *
12
- * You should have received a copy of the GNU General Public License
13
- * along with this program; if not, write to the Free Software
14
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15
- *
16
- * Copyright (c) 2016-2021 (original work) Open Assessment Technologies SA;
17
- */
18
- /**
19
- * Highlighter helper: wraps every text node found within a Range object.
20
- *
21
- * @author Christophe Noël <christophe@taotesting.com>
22
- */
23
- import _ from 'lodash';
24
- import $ from 'jquery';
25
-
26
- /**
27
- * Data attribute used to logically group the wrapping nodes into a single selection
28
- * @type {string}
29
- */
30
- var GROUP_ATTR = 'data-hl-group';
31
-
32
- /**
33
- * Children of those nodes types cannot be highlighted
34
- * @type {string[]}
35
- */
36
- var defaultBlackList = ['textarea', 'math', 'script', '.select2-container'];
37
-
38
- /**
39
- * @param {Object} options
40
- * @param {String} options.className - name of the class that will be used by the wrappers tags to highlight text
41
- * @param {String} options.containerSelector - allows to select the root Node in which highlighting is allowed
42
- * @param {Array<String>} [options.containersBlackList] - additional blacklist selectors to be added to module instance's blacklist
43
- * @param {Array<String>} [options.containersWhiteList] - whitelist selectors; supported only in `keepEmptyNodes` mode.
44
- * Priority of blacklist or whitelist is decided by which selector is closest to the node. If no match found, node is considered whitelisted.
45
- * @param {Boolean} [options.clearOnClick] - clear single highlight node on click
46
- * @param {Object} [options.colors] - keys is keeping as the "c" value of storing/restore the highlighters for indexing, values are wrappers class names
47
- * @param {Boolean} [options.keepEmptyNodes] - retain original dom structure as far as possible and do not remove empty nodes if they were not created by highlighter
48
- * @returns {Object} - the highlighter instance
49
- */
50
- export default function (options) {
51
- var className = options.className;
52
- var containerSelector = options.containerSelector;
53
- var keepEmptyNodes = options.keepEmptyNodes;
54
-
55
- let highlightingClasses = [className];
56
-
57
- // Multi-color mode
58
- if (options.colors) {
59
- highlightingClasses = Object.values(options.colors);
60
- }
61
-
62
- /**
63
- * list of node selectors which should NOT receive any highlighting from this instance
64
- * an optional passed-in blacklist is merged with local defaults
65
- * @type {Array}
66
- */
67
- var containersBlackList = _.union(defaultBlackList, options.containersBlackList);
68
- var containersBlackListSelector = containersBlackList.join(', ');
69
- var containersWhiteListSelector = null;
70
- var containersBlackAndWhiteListSelector = containersBlackListSelector;
71
- if (options.keepEmptyNodes && options.containersWhiteList) {
72
- containersWhiteListSelector = options.containersWhiteList.join(', ');
73
- containersBlackAndWhiteListSelector = _.union(containersBlackList, options.containersWhiteList).join(', ');
74
- }
75
-
76
- /**
77
- * used in recursive loops to decide if we should wrap or not the current node
78
- * @type {boolean}
79
- */
80
- var isWrapping = false;
81
-
82
- /**
83
- * performance improvement to break out of a potentially big recursive loop once the wrapping has ended
84
- * @type {boolean}
85
- */
86
- var hasWrapped = false;
87
-
88
- /**
89
- * used in recursive loops to assign a group Id to the current wrapped node
90
- * @type {number}
91
- */
92
- var currentGroupId;
93
-
94
- /**
95
- * used in recursive loops to build the index of text nodes
96
- * @type {number}
97
- */
98
- var textNodesIndex;
99
-
100
- /**
101
- * Returns the node in which highlighting is allowed
102
- * @returns {Element}
103
- */
104
- function getContainer() {
105
- return $(containerSelector).get(0);
106
- }
107
-
108
- /**
109
- * Returns all highlighted nodes, excluding any inside blacklisted elements
110
- * @returns {JQuery<HTMLElement>}
111
- */
112
- function getHighlightedNodes() {
113
- return $(containerSelector)
114
- .find(`.${highlightingClasses.join(',.')}`)
115
- .filter((i, node) => !isBlacklisted(node));
116
- }
117
-
118
- /**
119
- * Attach data to wrapper node.
120
- * Use it when deleting this highlight to know if highlight content should be merged with neighbour text nodes or not.
121
- * Use it when building/restoring index to know if restored highlight content should be split off neighbour text node or not.
122
- * Needed to keep markup the same as it was before highlighting.
123
- * @param {HTMLElement} node
124
- * @param {Boolean} beforeWasSplit
125
- * @param {Boolean} afterWasSplit
126
- */
127
- function addSplitData(node, beforeWasSplit, afterWasSplit) {
128
- node.dataset.beforeWasSplit = beforeWasSplit;
129
- node.dataset.afterWasSplit = afterWasSplit;
130
- }
131
-
132
- /**
133
- * Highlight all text nodes within each given range
134
- * @param {Range[]} ranges - array of ranges to highlight, may be given by the helper selector.getAllRanges()
135
- */
136
- function highlightRanges(ranges) {
137
- ranges.forEach(function (range) {
138
- var rangeInfos;
139
-
140
- if (isRangeValid(range)) {
141
- currentGroupId = getAvailableGroupId();
142
-
143
- // easy peasy: highlighting a plain text without any DOM nodes
144
- // NOTE: The condition checks the whole node content and not a selected content in a given range, that allows to wrap whitespace
145
- if (
146
- isWrappable(range.commonAncestorContainer) &&
147
- !isWrappingNode(range.commonAncestorContainer.parentNode)
148
- ) {
149
- const wrapperNode = getWrapper(currentGroupId);
150
- if (!keepEmptyNodes) {
151
- range.surroundContents(wrapperNode);
152
- } else {
153
- addSplitData(wrapperNode, range.startOffset > 0, range.endOffset < range.commonAncestorContainer.length);
154
- rangeSurroundContentsNoEmptyNodes(range, wrapperNode);
155
- }
156
- } else if (
157
- isWrappable(range.commonAncestorContainer) &&
158
- isWrappingNode(range.commonAncestorContainer.parentNode) &&
159
- range.commonAncestorContainer.parentNode !== className
160
- ) {
161
- highlightContainerNodes(range.commonAncestorContainer, className, range, currentGroupId);
162
-
163
- // now the fun stuff: highlighting a mix of text and DOM nodes
164
- } else {
165
- rangeInfos = {
166
- startNode: isElement(range.startContainer)
167
- ? range.startContainer.childNodes[range.startOffset]
168
- : range.startContainer,
169
- startNodeContainer: range.startContainer,
170
- startOffset: range.startOffset,
171
-
172
- endNode: isElement(range.endContainer) && range.endOffset > 0
173
- ? range.endContainer.childNodes[range.endOffset - 1]
174
- : range.endContainer,
175
- endNodeContainer: range.endContainer,
176
- endOffset: range.endOffset,
177
- commonRange: range
178
- };
179
-
180
- isWrapping = false;
181
- hasWrapped = false;
182
- wrapTextNodesInRange(range.commonAncestorContainer, rangeInfos);
183
- }
184
- }
185
-
186
- if (!keepEmptyNodes) {
187
- // clean up the markup after wrapping...
188
- range.commonAncestorContainer.normalize();
189
- }
190
-
191
- currentGroupId = 0;
192
- isWrapping = false;
193
- reindexGroups(getContainer());
194
- mergeAdjacentWrappingNodes(getContainer());
195
- unWrapEmptyHighlights();
196
- });
197
-
198
- if (options.clearOnClick) {
199
- $(containerSelector + ' .' + className)
200
- .off('click')
201
- .on('click', clearSingleHighlight);
202
- }
203
- }
204
-
205
- /**
206
- * Check if a range is valid
207
- * @param {Range} range
208
- * @returns {boolean}
209
- */
210
- function isRangeValid(range) {
211
- var rangeInContainer;
212
- try {
213
- rangeInContainer =
214
- $.contains(getContainer(), range.commonAncestorContainer) ||
215
- getContainer().isSameNode(range.commonAncestorContainer);
216
-
217
- return rangeInContainer && !range.collapsed;
218
- } catch (e) {
219
- return false;
220
- }
221
- }
222
-
223
- /**
224
- * Core wrapping function. Traverse the DOM tree and highlight (= wraps) all text nodes within the given range.
225
- * Recursive.
226
- *
227
- * @param {Node} rootNode - top of the node hierarchy in which text nodes will be searched
228
- * @param {Object} rangeInfos
229
- * @param {Node} rangeInfos.startNode - node on which the selection starts
230
- * @param {Node} rangeInfos.startNodeContainer - container of the startNode, or the start node itself in case of text nodes
231
- * @param {number} rangeInfos.startOffset - same as range.startOffset, but not read-only to allow override
232
- * @param {Node} rangeInfos.endNode - node on which the selection ends
233
- * @param {Node} rangeInfos.endNodeContainer - container of the endNode, or the end node itself in case of text nodes
234
- * @param {number} rangeInfos.endOffset - same as range.endOffset, but not read-only to allow override
235
- */
236
- function wrapTextNodesInRange(rootNode, rangeInfos) {
237
- var childNodes = rootNode.childNodes;
238
- var currentNode, i;
239
- var splitDatas = [];
240
-
241
- for (i = 0; i < childNodes.length; i++) {
242
- if (hasWrapped) {
243
- break;
244
- }
245
- currentNode = childNodes[i];
246
-
247
- if (isBlacklisted(currentNode)) {
248
- if (isElement(currentNode)) {
249
- //go deeper in case a descendant of the current blacklisted is whitelisted
250
- wrapTextNodesInRange(currentNode, rangeInfos);
251
- }
252
- } else {
253
- const isCurrentNodeTextInsideOfAnotherHighlightingWrapper =
254
- isText(currentNode) &&
255
- isWrappingNode(currentNode.parentNode) &&
256
- currentNode.parentNode.className !== className;
257
-
258
- if (isCurrentNodeTextInsideOfAnotherHighlightingWrapper) {
259
- const internalRange = new Range();
260
- internalRange.selectNodeContents(currentNode);
261
-
262
- if (rangeInfos.startNode === currentNode) {
263
- internalRange.setStart(currentNode, rangeInfos.startOffset);
264
- }
265
-
266
- if (rangeInfos.endNode === currentNode) {
267
- internalRange.setEnd(currentNode, rangeInfos.endOffset);
268
- }
269
-
270
- const isNodeInRange = rangeInfos.commonRange.isPointInRange(currentNode, internalRange.endOffset);
271
-
272
- // Apply new highlighting color only for selected nodes
273
- if (isNodeInRange) {
274
- isWrapping = true;
275
- highlightContainerNodes(currentNode, className, internalRange, currentGroupId);
276
- }
277
- } else {
278
- // split current node in case the wrapping start/ends on a partially selected text node
279
- if (currentNode.isSameNode(rangeInfos.startNode)) {
280
- if (isText(rangeInfos.startNodeContainer) && rangeInfos.startOffset !== 0) {
281
- // we defer the wrapping to the next iteration of the loop
282
- //end of node should be highlighted
283
- rangeInfos.startNode = currentNode.splitText(rangeInfos.startOffset);
284
- rangeInfos.startOffset = 0;
285
- splitDatas.push({ node: rangeInfos.startNode, beforeWasSplit: true, afterWasSplit: false });
286
- } else {
287
- //whole node should be highlighted
288
- isWrapping = true;
289
- splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: false });
290
- }
291
- }
292
-
293
- if (currentNode.isSameNode(rangeInfos.endNode) && isText(rangeInfos.endNodeContainer)) {
294
- if (rangeInfos.endOffset !== 0) {
295
- if (rangeInfos.endOffset < currentNode.textContent.length) {
296
- //start of node should be highlighted
297
- currentNode.splitText(rangeInfos.endOffset);
298
- splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: true });
299
- } else {
300
- //whole node should be highlighted
301
- splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: false });
302
- }
303
- } else {
304
- isWrapping = false;
305
- }
306
- }
307
-
308
- // wrap the current node...
309
- if (isText(currentNode)) {
310
- if (!keepEmptyNodes) {
311
- wrapTextNode(currentNode, currentGroupId);
312
- } else if (willHighlightNotBeEmptyAfterMerge(currentNode)) {
313
- const wrapperNode = wrapTextNode(currentNode, currentGroupId);
314
- if (wrapperNode) {
315
- const splitData = splitDatas.find(d => d.node === currentNode);
316
- addSplitData(wrapperNode,
317
- splitData ? splitData.beforeWasSplit : false,
318
- splitData ? splitData.afterWasSplit : false);
319
- }
320
- }
321
-
322
- // ... or continue deeper in the node tree
323
- } else if (isElement(currentNode)) {
324
- //some selections end at the very start of the next node, we should end wrapping when we reach such node
325
- if (!currentNode.isSameNode(rangeInfos.endNode) || rangeInfos.endOffset > 0) {
326
- wrapTextNodesInRange(currentNode, rangeInfos);
327
- }
328
- }
329
- }
330
- }
331
-
332
- // end wrapping ?
333
- if (currentNode.isSameNode(rangeInfos.endNode)) {
334
- isWrapping = false;
335
- hasWrapped = true;
336
- break;
337
- }
338
- }
339
- }
340
-
341
- /**
342
- * Restructure content of the highlighted wrapper according to the selectedRange
343
- * @param {Node} textNode
344
- * @param {string} activeClass
345
- * @param {Range} selectedRange
346
- * @param {number} currentGroupId
347
- */
348
- function highlightContainerNodes(textNode, activeClass, selectedRange, currentGroupId) {
349
- const container = textNode.parentNode;
350
- const range = new Range();
351
- range.selectNodeContents(textNode);
352
-
353
- const isSelectionCoversNodeStart = range.compareBoundaryPoints(Range.START_TO_START, selectedRange) === 0;
354
- const isSelectionCoversNodeEnd = range.compareBoundaryPoints(Range.END_TO_END, selectedRange) === 0;
355
-
356
- /*
357
- There are 4 possible cases selected area is intersected with already highlighted element.
358
- In examples below the border is represents the selection, "yellow" is class name of already highlighted
359
- container, "red" is class name of currently active highlighter
360
- **********************************************************************************************************
361
- 1. The container content is completely selected, so that we only have to change the highlighter class name
362
-
363
- Input:
364
- __________________________________________________
365
- | |
366
- |<span class="yellow"> Lorem ipsum dolor sit</span>|
367
- |__________________________________________________|
368
-
369
- Output:
370
- <span class="red"> Lorem ipsum dolor sit</span>
371
-
372
- **********************************************************************************************************
373
- 2. The container content is partially selected from the begging.
374
-
375
- Input:
376
- ______________________________
377
- | |
378
- |<span class="yellow"> Lorem ip|sum dolor sit</span>
379
- |______________________________|
380
-
381
- Output:
382
- <span class="red"> Lorem ip</span><span class="yellow">sum dolor sit</span>
383
-
384
- **********************************************************************************************************
385
- 3. The container content is partially selected at the end.
386
-
387
- Input:
388
- ____________________
389
- | |
390
- <span class="yellow"> Lorem ip|sum dolor sit</span>|
391
- |____________________|
392
-
393
- Output:
394
- <span class="yellow"> Lorem ip</span><span class="red">sum dolor sit</span>
395
-
396
- **********************************************************************************************************
397
- 4. The container content is partially selected in the middle.
398
-
399
- Input:
400
- ___________
401
- | |
402
- <span class="yellow"> Lorem |ipsum dolor| sit</span>
403
- |___________|
404
-
405
- Output:
406
- <span class="yellow"> Lorem </span><span class="red">ipsum dolor</span><span class="yellow"> sit</span>
407
- */
408
- if (isSelectionCoversNodeStart && isSelectionCoversNodeEnd) {
409
- textNode.parentNode.className = activeClass;
410
- } else if (isSelectionCoversNodeStart) {
411
- textNode.splitText(selectedRange.endOffset);
412
- wrapContainerChildNodes(container, 0, activeClass, currentGroupId);
413
- } else if (isSelectionCoversNodeEnd) {
414
- textNode.splitText(selectedRange.startOffset);
415
- wrapContainerChildNodes(container, 1, activeClass, currentGroupId);
416
- } else {
417
- textNode.splitText(selectedRange.startOffset).splitText(selectedRange.endOffset);
418
- wrapContainerChildNodes(container, 1, activeClass, currentGroupId);
419
- }
420
- }
421
-
422
- /**
423
- * Wraps all containers text nodes with highlighter element.
424
- * The child node with index given by indexToWrapNode parameter will be wrap with class given by activeClass parameter
425
- * @param {Element} container
426
- * @param {number} indexToWrapNode
427
- * @param {string} activeClass
428
- * @param {number} currentGroupId
429
- */
430
- function wrapContainerChildNodes(container, indexToWrapNode, activeClass, currentGroupId) {
431
- const containerClass = container.className;
432
- const fragment = new DocumentFragment();
433
- const childNodesLength = container.childNodes.length;
434
-
435
- container.childNodes.forEach((node, index) => {
436
- var wrapperNode;
437
- if (index === indexToWrapNode) {
438
- wrapperNode = wrapNode(node.cloneNode(), activeClass, currentGroupId);
439
- } else {
440
- wrapperNode = wrapNode(node.cloneNode(), containerClass, currentGroupId);
441
- }
442
- fragment.appendChild(wrapperNode);
443
-
444
- if (keepEmptyNodes) {
445
- addSplitData(wrapperNode,
446
- index === 0 ? container.dataset.beforeWasSplit : true,
447
- index === childNodesLength - 1 ? container.dataset.afterWasSplit : true);
448
- }
449
- });
450
-
451
- container.replaceWith(fragment);
452
- }
453
-
454
- /**
455
- * wraps a text node into the highlight span
456
- * @param {Node} node - the node to wrap
457
- * @param {number} groupId - the highlight group
458
- * @returns {Node|null} wrapper node, if it was created
459
- */
460
- function wrapTextNode(node, groupId) {
461
- if (isWrapping && !isWrappingNode(node.parentNode) && isWrappable(node)) {
462
- $(node).wrap(getWrapper(groupId));
463
- return node.parentNode;
464
- }
465
- return null;
466
- }
467
-
468
- /**
469
- * We need to re-index the groups after a user highlight: either to merge groups or to resolve inconsistencies
470
- * Recursive.
471
- *
472
- * @param {Node} rootNode
473
- */
474
- function reindexGroups(rootNode) {
475
- if (!rootNode) {
476
- return;
477
- }
478
-
479
- var childNodes = rootNode.childNodes;
480
- var i, currentNode, parent;
481
-
482
- for (i = 0; i < childNodes.length; i++) {
483
- currentNode = childNodes[i];
484
-
485
- if (isWrappable(currentNode)) {
486
- parent = currentNode.parentNode;
487
- if (isWrappingNode(parent)) {
488
- if (isWrapping === false) {
489
- currentGroupId++;
490
- }
491
- isWrapping = true;
492
- parent.setAttribute(GROUP_ATTR, currentGroupId); // set the new group Id
493
- } else {
494
- isWrapping = false;
495
- }
496
- } else if (isElement(currentNode)) {
497
- reindexGroups(currentNode);
498
- }
499
- }
500
- }
501
-
502
- /**
503
- * Some highlights may result in having adjacent wrapping nodes. We remove them here to get a cleaner markup.
504
- *
505
- * @param {Node} rootNode
506
- */
507
- function mergeAdjacentWrappingNodes(rootNode) {
508
- if (!rootNode) {
509
- return;
510
- }
511
-
512
- var childNodes = rootNode.childNodes;
513
- var i, currentNode;
514
-
515
- for (i = 0; i < childNodes.length; i++) {
516
- currentNode = childNodes[i];
517
-
518
- if (isWrappingNode(currentNode)) {
519
- if (keepEmptyNodes) {
520
- currentNode.normalize();
521
- }
522
- while (
523
- isWrappingNode(currentNode.nextSibling) &&
524
- currentNode.className === currentNode.nextSibling.className
525
- ) {
526
- if (keepEmptyNodes) {
527
- currentNode.nextSibling.normalize();
528
- }
529
- currentNode.firstChild.textContent += currentNode.nextSibling.firstChild.textContent;
530
- if (keepEmptyNodes) {
531
- addSplitData(currentNode, currentNode.dataset.beforeWasSplit, currentNode.nextSibling.dataset.afterWasSplit);
532
- }
533
- currentNode.parentNode.removeChild(currentNode.nextSibling);
534
- }
535
- } else if (isElement(currentNode)) {
536
- mergeAdjacentWrappingNodes(currentNode);
537
- }
538
- }
539
- }
540
-
541
- /**
542
- * Unwraps highlighted nodes with a line break or with an empty content
543
- */
544
- function unWrapEmptyHighlights() {
545
- getHighlightedNodes().each((index, node) => {
546
- const nodeContent = node.textContent;
547
-
548
- if (nodeContent.trim().length === 0) {
549
- if (nodeContent.length === 0 || /\r|\n/.exec(nodeContent)) {
550
- clearSingleHighlight({ target: node });
551
- }
552
- }
553
- });
554
- }
555
-
556
- /**
557
- * Check condition to avoid the work of `unwrapEmptyHighlights` ahead of time, before `mergeAdjacentNodes` runs,
558
- * because in `keepEmptyNodes` case we do not want to add nodes to dom unless necessary.
559
- * Also be more strict and don't allow to select nodes with spaces only, because they may appear in unexpected places in markup
560
- * (here it's not exactly same as `unwrapEmptyHighlights`).
561
- * @param {Node} node - node which will be wrapped (highlighted)
562
- * @returns {Boolean}
563
- */
564
- function willHighlightNotBeEmptyAfterMerge(node) {
565
- if (!node.textContent.length) {
566
- return false;
567
- }
568
- if (node.textContent.trim().length) {
569
- return true;
570
- }
571
- const prevNode = node.previousSibling;
572
- const canWrapperBeMergedWithPreviousSibling = prevNode && isWrappingNode(prevNode) && prevNode.className === className;
573
- if (canWrapperBeMergedWithPreviousSibling) {
574
- return true;
575
- }
576
- const nextNode = node.nextSibling;
577
- const canWrapperBeMergedWithNextSibling = nextNode && isWrappingNode(nextNode) && nextNode.className === className;
578
- if (canWrapperBeMergedWithNextSibling) {
579
- return true;
580
- }
581
- return false;
582
- }
583
-
584
- /**
585
- * `range.surroundContents` can create empty text nodes,
586
- * which will cause trouble in `mergeAdjacentNodes` later (in `keepEmptyNodes` case).
587
- * This method surrounds range, then removes those nodes
588
- * @param {Range} range
589
- * @param {Node} wrapperNode
590
- */
591
- function rangeSurroundContentsNoEmptyNodes(range, wrapperNode) {
592
- const containerPreviousSibling = range.commonAncestorContainer.previousSibling;
593
- const containerNextSibling = range.commonAncestorContainer.nextSibling;
594
-
595
- range.surroundContents(wrapperNode);
596
-
597
- removeEmptyTextNodeIfDifferent(wrapperNode.previousSibling, containerPreviousSibling);
598
- removeEmptyTextNodeIfDifferent(wrapperNode.nextSibling, containerNextSibling);
599
- }
600
-
601
- /**
602
- * Remove `node`, if it's an empty text node and is *not* the same node as `nodeToCompare`
603
- * @param {Node} node
604
- * @param {Node} nodeToCompare
605
- */
606
- function removeEmptyTextNodeIfDifferent(node, nodeToCompare) {
607
- if (node && node !== nodeToCompare && isText(node) && node.textContent.length === 0) {
608
- node.remove();
609
- }
610
- }
611
-
612
- /**
613
- * Remove all wrapping nodes from markup
614
- */
615
- function clearHighlights() {
616
- getHighlightedNodes().each(function (i, elem) {
617
- if (!keepEmptyNodes) {
618
- var $wrapped = $(this);
619
- $wrapped.replaceWith($wrapped.text());
620
- } else {
621
- clearSingleHighlight({ target: elem });
622
- }
623
- });
624
- }
625
-
626
- /**
627
- * Remove unwrap dom node
628
- */
629
- function clearSingleHighlight(e) {
630
- if (!keepEmptyNodes) {
631
- const $wrapped = $(e.target);
632
- const text = $wrapped.text();
633
-
634
- // NOTE: JQuery replaceWith is not working with empty string https://bugs.jquery.com/ticket/13401
635
- if (text === '') {
636
- $wrapped.remove();
637
- } else {
638
- $wrapped.replaceWith(text);
639
- }
640
- } else {
641
- const nodeToRemove = e.target;
642
- const nodeToRemoveText = nodeToRemove.textContent;
643
- const beforeWasSplit = nodeToRemove.dataset.beforeWasSplit === 'true';
644
- const afterWasSplit = nodeToRemove.dataset.afterWasSplit === 'true';
645
- const prevNode = nodeToRemove.previousSibling;
646
- const nextNode = nodeToRemove.nextSibling;
647
-
648
- if (beforeWasSplit && prevNode && isText(prevNode) && prevNode.textContent) {
649
- //append text to previous sibling
650
- prevNode.textContent += nodeToRemoveText;
651
- nodeToRemove.remove();
652
-
653
- if (afterWasSplit && prevNode.nextSibling && isText(prevNode.nextSibling) && prevNode.nextSibling.textContent) {
654
- //merge it with next sibling
655
- prevNode.textContent += prevNode.nextSibling.textContent;
656
- prevNode.nextSibling.remove();
657
- }
658
- }
659
- else if (afterWasSplit && nextNode && isText(nextNode) && nextNode.textContent) {
660
- //append text to next sibling
661
- nextNode.textContent = nodeToRemoveText + nextNode.textContent;
662
- nodeToRemove.remove();
663
- } else if (nodeToRemoveText) {
664
- //keep text in a separate text node
665
- nodeToRemove.replaceWith(document.createTextNode(nodeToRemoveText));
666
- } else {
667
- //text is empty, just remove it
668
- nodeToRemove.remove();
669
- }
670
- }
671
- }
672
-
673
- /**
674
- * Index-related functions:
675
- * ========================
676
- * To allow saving and restoring highlights on an equivalent, but different, DOM tree (for example if the markup is deleted and re-created)
677
- * we build an index containing the status of each text node:
678
- * - not highlighted
679
- * - fully highlighted
680
- * - partially highlighted (= with inline ranges)
681
- *
682
- * This index will be used to restore a selection on the new DOM tree
683
- */
684
-
685
- /**
686
- * Bootstrap the process of building the highlight index
687
- * @returns {Object[]|BuildModelResultKeepEmpty|null}
688
- */
689
- function getHighlightIndex() {
690
- var rootNode = getContainer();
691
- if (!keepEmptyNodes) {
692
- var highlightIndex = [];
693
- if (rootNode) {
694
- rootNode.normalize();
695
- textNodesIndex = 0;
696
- buildHighlightIndex(rootNode, highlightIndex);
697
- }
698
- return highlightIndex;
699
- } else {
700
- if (rootNode) {
701
- return buildHighlightModelKeepEmpty(rootNode);
702
- } else {
703
- return null;
704
- }
705
- }
706
- }
707
-
708
- /**
709
- * Traverse the DOM tree to create the text Nodes index. Recursive.
710
- * @param {Node} rootNode
711
- * @param {Object[]} highlightIndex
712
- */
713
- function buildHighlightIndex(rootNode, highlightIndex) {
714
- var childNodes = rootNode.childNodes;
715
- var i, currentNode;
716
- var nodeInfos, inlineRange, inlineOffset, nodesToSkip;
717
-
718
- for (i = 0; i < childNodes.length; i++) {
719
- currentNode = childNodes[i];
720
-
721
- // Skip blacklisted nodes
722
- if (isBlacklisted(currentNode)) {
723
- continue;
724
- }
725
- // A simple node not highlighted and isolated (= not followed by an wrapped text)
726
- else if (isWrappable(currentNode) && !isWrappingNode(currentNode.nextSibling)) {
727
- highlightIndex[textNodesIndex] = { highlighted: false };
728
- textNodesIndex++;
729
-
730
- // an isolated node (= not followed by a highlight table text) with its whole content highlighted
731
- } else if (
732
- isWrappingNode(currentNode) &&
733
- !isText(currentNode.nextSibling) &&
734
- (!isWrappingNode(currentNode.nextSibling) ||
735
- currentNode.className === currentNode.nextSibling.className)
736
- ) {
737
- highlightIndex[textNodesIndex] = {
738
- highlighted: true,
739
- groupId: currentNode.getAttribute(GROUP_ATTR),
740
- c: getColorByClassName(currentNode.className)
741
- };
742
- textNodesIndex++;
743
-
744
- // less straightforward: a succession of (at least) 1 wrapping node with 1 wrappable text node, in either order, and possibly more
745
- // the trick is to create a unique text node on which we will be able to re-apply multiple partial highlights
746
- // for this, we use 'inlineRanges'
747
- } else if (isHotNode(currentNode)) {
748
- nodeInfos = {
749
- highlighted: true,
750
- inlineRanges: []
751
- };
752
-
753
- nodesToSkip = -1;
754
- inlineOffset = 0;
755
-
756
- while (currentNode) {
757
- if (isWrappingNode(currentNode)) {
758
- inlineRange = {
759
- groupId: currentNode.getAttribute(GROUP_ATTR),
760
- c: getColorByClassName(currentNode.className)
761
- };
762
-
763
- if (isText(currentNode.previousSibling) || isWrappingNode(currentNode.previousSibling)) {
764
- inlineRange.startOffset = inlineOffset;
765
- }
766
- if (isText(currentNode.nextSibling) || isWrappingNode(currentNode.nextSibling)) {
767
- inlineRange.endOffset = inlineOffset + currentNode.textContent.length;
768
- }
769
- nodeInfos.inlineRanges.push(inlineRange);
770
- }
771
-
772
- inlineOffset += currentNode.textContent.length;
773
- currentNode =
774
- isHotNode(currentNode.nextSibling) || isText(currentNode.nextSibling)
775
- ? currentNode.nextSibling
776
- : null;
777
- nodesToSkip++;
778
- }
779
- i += nodesToSkip; // we increase the loop counter to avoid looping over the nodes that we just analyzed
780
-
781
- highlightIndex[textNodesIndex] = nodeInfos;
782
- textNodesIndex++;
783
-
784
- // go deeper in the node tree...
785
- } else if (isElement(currentNode)) {
786
- buildHighlightIndex(currentNode, highlightIndex);
787
- }
788
- }
789
- }
790
-
791
- /**
792
- * @typedef HighlightEntryKeepEmpty
793
- * @property {String} groupId
794
- * @property {String} c - color
795
- * @property {Number} offsetBefore
796
- * @property {Number} textLength
797
- * @property {String} beforeWasSplit
798
- * @property {String} afterWasSplit
799
- * @property {Array<Number>} path - on each level from root container to highlight, index among siblings
800
- */
801
- /**
802
- * @typedef BuildModelResultKeepEmpty
803
- * @property {HighlightEntryKeepEmpty[]} highlightModel
804
- * @property {NodeList} wrapperNodes
805
- */
806
- /**
807
- * For `keepEmptyNodes` option, creates data model of highlights.
808
- * Additionally returns array of highlight nodes. Traverses DOM tree.
809
- * @param {Node} rootNode
810
- * @returns {BuildModelResultKeepEmpty|null} result
811
- */
812
- function buildHighlightModelKeepEmpty(rootNode) {
813
- const classNames = options.colors ? Object.values(options.colors) : [className];
814
- const wrapperNodesSelector = classNames.map(cls => containerSelector + ' .' + cls).join(', ');
815
- const wrapperNodes = Array.from(document.querySelectorAll(wrapperNodesSelector))
816
- .filter(node => !isBlacklisted(node));
817
-
818
- if (!wrapperNodes.length) {
819
- return null;
820
- }
821
-
822
- var highlightModel = [];
823
- const indexCache = new Map();
824
- for (var k = 0; k < wrapperNodes.length; k++) {
825
- var wrapperNode = wrapperNodes[k];
826
-
827
- //get info about highlight itself
828
- var offsetBefore = 0;
829
- var prevNode = wrapperNode.previousSibling;
830
- if (prevNode && isText(prevNode)) {
831
- const beforeWasSplit = wrapperNode.dataset.beforeWasSplit === 'true';
832
- if (beforeWasSplit) {
833
- offsetBefore = prevNode.textContent.length;
834
- }
835
- }
836
- var highlightData = {
837
- groupId: wrapperNode.getAttribute(GROUP_ATTR),
838
- c: getColorByClassName(wrapperNode.className),
839
- offsetBefore,
840
- textLength: wrapperNode.textContent.length,
841
- beforeWasSplit: wrapperNode.dataset.beforeWasSplit,
842
- afterWasSplit: wrapperNode.dataset.afterWasSplit,
843
- path: []
844
- };
845
-
846
- //get info about its position in the tree: path through all parents from rootNode to highlight
847
- let currentNode = wrapperNode;
848
- while (currentNode && currentNode !== rootNode) {
849
- let indexInModel = indexCache.get(currentNode);
850
- if (!indexInModel && indexInModel !== 0) {
851
- //should be more reliable to ignore empty nodes when indexing
852
- const childNodes = Array.from(currentNode.parentNode.childNodes).filter(node => !(isText(node) && !node.textContent.length));
853
- //index among its non-empty siblings
854
- indexInModel = childNodes.indexOf(currentNode);
855
- indexCache.set(currentNode, indexInModel);
856
- }
857
- highlightData.path.unshift(indexInModel);
858
- currentNode = currentNode.parentNode;
859
- }
860
-
861
- //add info about highlight and its position to model
862
- highlightModel.push(highlightData);
863
- }
864
- return {
865
- highlightModel,
866
- wrapperNodes
867
- };
868
- }
869
-
870
- /**
871
- * Bootstrap the process of restoring the highlights from an index
872
- * @param {Object[]|HighlightEntryKeepEmpty[]|null} highlightIndex
873
- */
874
- function highlightFromIndex(highlightIndex) {
875
- var rootNode = getContainer();
876
- if (rootNode) {
877
- if (!keepEmptyNodes) {
878
- rootNode.normalize();
879
- textNodesIndex = 0;
880
- restoreHighlight(rootNode, highlightIndex);
881
- } else {
882
- restoreHighlightKeepEmpty(rootNode, highlightIndex);
883
- }
884
- }
885
- }
886
-
887
- /**
888
- * Traverse the DOM tree to wraps the text nodes according to the highlight index. Recursive.
889
- * @param {Node} rootNode
890
- * @param {Object[]} highlightIndex
891
- */
892
- function restoreHighlight(rootNode, highlightIndex) {
893
- var childNodes = rootNode.childNodes;
894
- var i, currentNode, parent;
895
- var nodeInfos, nodesToSkip, range, initialChildCount;
896
-
897
- for (i = 0; i < childNodes.length; i++) {
898
- currentNode = childNodes[i];
899
-
900
- if (isBlacklisted(currentNode)) {
901
- continue;
902
- } else if (isWrappable(currentNode)) {
903
- parent = currentNode.parentNode;
904
- initialChildCount = parent.childNodes.length;
905
-
906
- nodeInfos = highlightIndex[textNodesIndex];
907
-
908
- if (nodeInfos.highlighted === true) {
909
- if (_.isArray(nodeInfos.inlineRanges)) {
910
- nodeInfos.inlineRanges.reverse();
911
- nodeInfos.inlineRanges.forEach(function (inlineRange) {
912
- range = document.createRange();
913
- range.setStart(currentNode, inlineRange.startOffset || 0);
914
- range.setEnd(currentNode, inlineRange.endOffset || currentNode.textContent.length);
915
- range.surroundContents(getWrapper(inlineRange.groupId, getClassNameByColor(inlineRange.c)));
916
- });
917
-
918
- // fully highlighted text node
919
- } else {
920
- range = document.createRange();
921
- range.selectNodeContents(currentNode);
922
- range.surroundContents(getWrapper(nodeInfos.groupId, getClassNameByColor(nodeInfos.c)));
923
- }
924
- // we do want to loop over the nodes created by the wrapping operation
925
- nodesToSkip = parent.childNodes.length - initialChildCount;
926
- i += nodesToSkip;
927
- }
928
- textNodesIndex++;
929
- } else if (isElement(currentNode)) {
930
- restoreHighlight(currentNode, highlightIndex);
931
- }
932
- }
933
- }
934
-
935
- /**
936
- * For `keepEmptyNodes` option, wraps the text nodes according to highlights data model.
937
- * Traverses and updates DOM tree. Shouldn't throw errors in case of mismatches.
938
- * @param {Node} rootNode
939
- * @param {HighlightEntryKeepEmpty[]|null} highlightModel
940
- */
941
- function restoreHighlightKeepEmpty(rootNode, highlightModel) {
942
- if (!highlightModel) {
943
- return;
944
- }
945
-
946
- var currentModel;
947
- var range;
948
- for (var k = 0; k < highlightModel.length; k++) {
949
- currentModel = highlightModel[k];
950
-
951
- //find node to wrap - go through nodes until we reach level where node to wrap will be
952
- let childNodes;
953
- let indexInModel;
954
- let currentParentNode = rootNode;
955
- let pathNotFound = false;
956
- if (!currentModel.path || !currentModel.path.length) {
957
- continue; //something went wrong
958
- }
959
- for (var m = 0; m < currentModel.path.length; m++) {
960
- //path was counted among non-empty nodes
961
- childNodes = Array.from(currentParentNode.childNodes).filter(node => !(isText(node) && !node.textContent.length));
962
- indexInModel = currentModel.path[m];
963
- currentParentNode = childNodes[indexInModel];
964
- if (!currentParentNode && m < currentModel.path.length - 1) {
965
- //node on last level may not exist yet, no need to fail. See `nodeAtIndex`
966
- pathNotFound = true;
967
- break;
968
- }
969
- }
970
- if (pathNotFound) {
971
- continue; //something went wrong
972
- }
973
-
974
- //add single highlight
975
- var nodeAtIndex = null;
976
- if (!currentModel.offsetBefore) {
977
- //wrap starts on this node
978
- nodeAtIndex = childNodes[indexInModel];
979
- if (!nodeAtIndex || !isText(nodeAtIndex) || isBlacklisted(nodeAtIndex)) {
980
- continue; //something went wrong
981
- }
982
- } else {
983
- //split previousSibling to create a node for wrapping
984
- var nodeBefore = childNodes[indexInModel - 1];
985
- if (!nodeBefore || !isText(nodeBefore) ||
986
- nodeBefore.textContent.length <= currentModel.offsetBefore || isBlacklisted(nodeBefore)) {
987
- continue; //something went wrong
988
- }
989
- nodeAtIndex = nodeBefore.splitText(currentModel.offsetBefore);
990
- }
991
- //cut off its end
992
- if (nodeAtIndex.textContent.length > currentModel.textLength) {
993
- nodeAtIndex.splitText(currentModel.textLength);
994
- }
995
-
996
- //wrap
997
- const wrapperNode = getWrapper(currentModel.groupId, getClassNameByColor(currentModel.c));
998
- addSplitData(wrapperNode, currentModel.beforeWasSplit, currentModel.afterWasSplit);
999
- range = document.createRange();
1000
- range.selectNodeContents(nodeAtIndex);
1001
- rangeSurroundContentsNoEmptyNodes(range, wrapperNode);
1002
- }
1003
- }
1004
-
1005
- /**
1006
- * Set highlighter color
1007
- * @param {string} color Active highlighter color
1008
- */
1009
- const setActiveColor = color => {
1010
- if (options.colors[color]) {
1011
- className = options.colors[color];
1012
- }
1013
- };
1014
-
1015
- /**
1016
- * Helpers
1017
- */
1018
-
1019
- /**
1020
- * Return the object key contains the given value
1021
- * @param {Object} object
1022
- * @param {any} value
1023
- * @return {string|undefined}
1024
- */
1025
- const getKeyByValue = (object, value) => Object.keys(object).find(key => object[key] === value);
1026
-
1027
- /**
1028
- * Returns color identifier for the given class name
1029
- * @param {string} highlighterClassName Class name of highlighter classes
1030
- * @returns {string|number} Color identifier
1031
- */
1032
- const getColorByClassName = highlighterClassName => {
1033
- if (options.colors) {
1034
- return getKeyByValue(options.colors, highlighterClassName);
1035
- }
1036
-
1037
- return className;
1038
- };
1039
-
1040
- /**
1041
- * Returns class name for the given color identifier
1042
- * @param {string|number} color Color identifier
1043
- * @returns {string} Class name
1044
- */
1045
- const getClassNameByColor = color => {
1046
- if (options.colors && options.colors[color]) {
1047
- return options.colors[color];
1048
- }
1049
-
1050
- return className;
1051
- };
1052
-
1053
- /**
1054
- * Check if the given node is a wrapper
1055
- * @param {Node|Element} node
1056
- * @returns {boolean}
1057
- */
1058
- function isWrappingNode(node) {
1059
- return isElement(node) && node.tagName.toLowerCase() === 'span' && highlightingClasses.includes(node.className);
1060
- }
1061
-
1062
- /**
1063
- * Check if the given node can be wrapped
1064
- * @param {Node} node
1065
- * @returns {boolean}
1066
- */
1067
- function isWrappable(node) {
1068
- return isText(node) && !isBlacklisted(node);
1069
- }
1070
-
1071
- /**
1072
- * Check if the given node is, or is within, a blacklisted container.
1073
- * With `keepEmptyNodes` option, node inside blacklisted container can be whitelisted too.
1074
- * Priority of blacklist or whitelist is decided by which selector is closest to the node.
1075
- * If no match found, node is considered whitelisted.
1076
- * @param {Node} node
1077
- * @returns {boolean}
1078
- */
1079
- function isBlacklisted(node) {
1080
- const closest = $(node).closest(containersBlackAndWhiteListSelector);
1081
- if (!closest.length) {
1082
- return false;
1083
- } else if (!containersWhiteListSelector) {
1084
- return true;
1085
- } else {
1086
- return !closest.get(0).matches(containersWhiteListSelector);
1087
- }
1088
- }
1089
-
1090
- /**
1091
- * Wraps text node to the highlighter wrapper element
1092
- * @param {Node} textNode Text node to wrap
1093
- * @param {string} className Wrapper class name
1094
- * @param {number} groupId Group id
1095
- */
1096
- function wrapNode(textNode, className, groupId) {
1097
- const element = getWrapper(groupId, className);
1098
-
1099
- element.appendChild(textNode);
1100
- return element;
1101
- }
1102
-
1103
- /**
1104
- * Create a wrapping node
1105
- * @param {number} groupId
1106
- * @returns {Element}
1107
- */
1108
- function getWrapper(groupId, wrapperClass) {
1109
- const wrapper = document.createElement('span');
1110
-
1111
- wrapper.className = wrapperClass || className;
1112
- wrapper.setAttribute(GROUP_ATTR, `${groupId}`);
1113
- return wrapper;
1114
- }
1115
-
1116
- /**
1117
- * Returns the first unused group Id
1118
- * @returns {number}
1119
- */
1120
- function getAvailableGroupId() {
1121
- var id = currentGroupId || 1;
1122
- while ($(getContainer()).find('[' + GROUP_ATTR + '=' + id + ']').length !== 0) {
1123
- id++;
1124
- }
1125
- return id;
1126
- }
1127
-
1128
- /**
1129
- * Check if the given node is an element
1130
- * @param {Node} node
1131
- * @returns {boolean}
1132
- */
1133
- function isElement(node) {
1134
- return node && typeof node === 'object' && node.nodeType === window.Node.ELEMENT_NODE;
1135
- }
1136
-
1137
- /**
1138
- * Check if the given node is of type text
1139
- * @param {Node} node
1140
- * @returns {boolean}
1141
- */
1142
- function isText(node) {
1143
- return node && typeof node === 'object' && node.nodeType === window.Node.TEXT_NODE;
1144
- }
1145
-
1146
- /**
1147
- * a "Hot Node" is either wrappable text node or a wrapper
1148
- * @param {Node} node
1149
- * @returns {boolean}
1150
- */
1151
- function isHotNode(node) {
1152
- return isWrappingNode(node) || isWrappable(node);
1153
- }
1154
-
1155
- /**
1156
- * Public API of the highlighter helper
1157
- */
1158
- return {
1159
- highlightRanges: highlightRanges,
1160
- highlightFromIndex: highlightFromIndex,
1161
- getHighlightIndex: getHighlightIndex,
1162
- clearHighlights: clearHighlights,
1163
- clearSingleHighlight: clearSingleHighlight,
1164
- setActiveColor
1165
- };
1166
- }
1
+ /**
2
+ * This program is free software; you can redistribute it and/or
3
+ * modify it under the terms of the GNU General Public License
4
+ * as published by the Free Software Foundation; under version 2
5
+ * of the License (non-upgradable).
6
+ *
7
+ * This program is distributed in the hope that it will be useful,
8
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ * GNU General Public License for more details.
11
+ *
12
+ * You should have received a copy of the GNU General Public License
13
+ * along with this program; if not, write to the Free Software
14
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15
+ *
16
+ * Copyright (c) 2016-2021 (original work) Open Assessment Technologies SA;
17
+ */
18
+ /**
19
+ * Highlighter helper: wraps every text node found within a Range object.
20
+ *
21
+ * @author Christophe Noël <christophe@taotesting.com>
22
+ */
23
+ import _ from 'lodash';
24
+ import $ from 'jquery';
25
+
26
+ /**
27
+ * Data attribute used to logically group the wrapping nodes into a single selection
28
+ * @type {string}
29
+ */
30
+ var GROUP_ATTR = 'data-hl-group';
31
+
32
+ /**
33
+ * Children of those nodes types cannot be highlighted
34
+ * @type {string[]}
35
+ */
36
+ var defaultBlackList = ['textarea', 'math', 'script', '.select2-container'];
37
+
38
+ /**
39
+ * @param {Object} options
40
+ * @param {String} options.className - name of the class that will be used by the wrappers tags to highlight text
41
+ * @param {String} options.containerSelector - allows to select the root Node in which highlighting is allowed
42
+ * @param {Array<String>} [options.containersBlackList] - additional blacklist selectors to be added to module instance's blacklist
43
+ * @param {Array<String>} [options.containersWhiteList] - whitelist selectors; supported only in `keepEmptyNodes` mode.
44
+ * Priority of blacklist or whitelist is decided by which selector is closest to the node. If no match found, node is considered whitelisted.
45
+ * @param {Boolean} [options.clearOnClick] - clear single highlight node on click
46
+ * @param {Object} [options.colors] - keys is keeping as the "c" value of storing/restore the highlighters for indexing, values are wrappers class names
47
+ * @param {Boolean} [options.keepEmptyNodes] - retain original dom structure as far as possible and do not remove empty nodes if they were not created by highlighter
48
+ * @returns {Object} - the highlighter instance
49
+ */
50
+ export default function (options) {
51
+ var className = options.className;
52
+ var containerSelector = options.containerSelector;
53
+ var keepEmptyNodes = options.keepEmptyNodes;
54
+
55
+ let highlightingClasses = [className];
56
+
57
+ // Multi-color mode
58
+ if (options.colors) {
59
+ highlightingClasses = Object.values(options.colors);
60
+ }
61
+
62
+ /**
63
+ * list of node selectors which should NOT receive any highlighting from this instance
64
+ * an optional passed-in blacklist is merged with local defaults
65
+ * @type {Array}
66
+ */
67
+ var containersBlackList = _.union(defaultBlackList, options.containersBlackList);
68
+ var containersBlackListSelector = containersBlackList.join(', ');
69
+ var containersWhiteListSelector = null;
70
+ var containersBlackAndWhiteListSelector = containersBlackListSelector;
71
+ if (options.keepEmptyNodes && options.containersWhiteList) {
72
+ containersWhiteListSelector = options.containersWhiteList.join(', ');
73
+ containersBlackAndWhiteListSelector = _.union(containersBlackList, options.containersWhiteList).join(', ');
74
+ }
75
+
76
+ /**
77
+ * used in recursive loops to decide if we should wrap or not the current node
78
+ * @type {boolean}
79
+ */
80
+ var isWrapping = false;
81
+
82
+ /**
83
+ * performance improvement to break out of a potentially big recursive loop once the wrapping has ended
84
+ * @type {boolean}
85
+ */
86
+ var hasWrapped = false;
87
+
88
+ /**
89
+ * used in recursive loops to assign a group Id to the current wrapped node
90
+ * @type {number}
91
+ */
92
+ var currentGroupId;
93
+
94
+ /**
95
+ * used in recursive loops to build the index of text nodes
96
+ * @type {number}
97
+ */
98
+ var textNodesIndex;
99
+
100
+ /**
101
+ * Returns the node in which highlighting is allowed
102
+ * @returns {Element}
103
+ */
104
+ function getContainer() {
105
+ return $(containerSelector).get(0);
106
+ }
107
+
108
+ /**
109
+ * Returns all highlighted nodes, excluding any inside blacklisted elements
110
+ * @returns {JQuery<HTMLElement>}
111
+ */
112
+ function getHighlightedNodes() {
113
+ return $(containerSelector)
114
+ .find(`.${highlightingClasses.join(',.')}`)
115
+ .filter((i, node) => !isBlacklisted(node));
116
+ }
117
+
118
+ /**
119
+ * Attach data to wrapper node.
120
+ * Use it when deleting this highlight to know if highlight content should be merged with neighbour text nodes or not.
121
+ * Use it when building/restoring index to know if restored highlight content should be split off neighbour text node or not.
122
+ * Needed to keep markup the same as it was before highlighting.
123
+ * @param {HTMLElement} node
124
+ * @param {Boolean} beforeWasSplit
125
+ * @param {Boolean} afterWasSplit
126
+ */
127
+ function addSplitData(node, beforeWasSplit, afterWasSplit) {
128
+ node.dataset.beforeWasSplit = beforeWasSplit;
129
+ node.dataset.afterWasSplit = afterWasSplit;
130
+ }
131
+
132
+ /**
133
+ * Highlight all text nodes within each given range
134
+ * @param {Range[]} ranges - array of ranges to highlight, may be given by the helper selector.getAllRanges()
135
+ */
136
+ function highlightRanges(ranges) {
137
+ ranges.forEach(function (range) {
138
+ var rangeInfos;
139
+
140
+ if (isRangeValid(range)) {
141
+ currentGroupId = getAvailableGroupId();
142
+
143
+ // easy peasy: highlighting a plain text without any DOM nodes
144
+ // NOTE: The condition checks the whole node content and not a selected content in a given range, that allows to wrap whitespace
145
+ if (
146
+ isWrappable(range.commonAncestorContainer) &&
147
+ !isWrappingNode(range.commonAncestorContainer.parentNode)
148
+ ) {
149
+ const wrapperNode = getWrapper(currentGroupId);
150
+ if (!keepEmptyNodes) {
151
+ range.surroundContents(wrapperNode);
152
+ } else {
153
+ addSplitData(wrapperNode, range.startOffset > 0, range.endOffset < range.commonAncestorContainer.length);
154
+ rangeSurroundContentsNoEmptyNodes(range, wrapperNode);
155
+ }
156
+ } else if (
157
+ isWrappable(range.commonAncestorContainer) &&
158
+ isWrappingNode(range.commonAncestorContainer.parentNode) &&
159
+ range.commonAncestorContainer.parentNode !== className
160
+ ) {
161
+ highlightContainerNodes(range.commonAncestorContainer, className, range, currentGroupId);
162
+
163
+ // now the fun stuff: highlighting a mix of text and DOM nodes
164
+ } else {
165
+ rangeInfos = {
166
+ startNode: isElement(range.startContainer)
167
+ ? range.startContainer.childNodes[range.startOffset]
168
+ : range.startContainer,
169
+ startNodeContainer: range.startContainer,
170
+ startOffset: range.startOffset,
171
+
172
+ endNode: isElement(range.endContainer) && range.endOffset > 0
173
+ ? range.endContainer.childNodes[range.endOffset - 1]
174
+ : range.endContainer,
175
+ endNodeContainer: range.endContainer,
176
+ endOffset: range.endOffset,
177
+ commonRange: range
178
+ };
179
+
180
+ isWrapping = false;
181
+ hasWrapped = false;
182
+ wrapTextNodesInRange(range.commonAncestorContainer, rangeInfos);
183
+ }
184
+ }
185
+
186
+ if (!keepEmptyNodes) {
187
+ // clean up the markup after wrapping...
188
+ range.commonAncestorContainer.normalize();
189
+ }
190
+
191
+ currentGroupId = 0;
192
+ isWrapping = false;
193
+ reindexGroups(getContainer());
194
+ mergeAdjacentWrappingNodes(getContainer());
195
+ unWrapEmptyHighlights();
196
+ });
197
+
198
+ if (options.clearOnClick) {
199
+ $(containerSelector + ' .' + className)
200
+ .off('click')
201
+ .on('click', clearSingleHighlight);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Check if a range is valid
207
+ * @param {Range} range
208
+ * @returns {boolean}
209
+ */
210
+ function isRangeValid(range) {
211
+ var rangeInContainer;
212
+ try {
213
+ rangeInContainer =
214
+ $.contains(getContainer(), range.commonAncestorContainer) ||
215
+ getContainer().isSameNode(range.commonAncestorContainer);
216
+
217
+ return rangeInContainer && !range.collapsed;
218
+ } catch (e) {
219
+ return false;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Core wrapping function. Traverse the DOM tree and highlight (= wraps) all text nodes within the given range.
225
+ * Recursive.
226
+ *
227
+ * @param {Node} rootNode - top of the node hierarchy in which text nodes will be searched
228
+ * @param {Object} rangeInfos
229
+ * @param {Node} rangeInfos.startNode - node on which the selection starts
230
+ * @param {Node} rangeInfos.startNodeContainer - container of the startNode, or the start node itself in case of text nodes
231
+ * @param {number} rangeInfos.startOffset - same as range.startOffset, but not read-only to allow override
232
+ * @param {Node} rangeInfos.endNode - node on which the selection ends
233
+ * @param {Node} rangeInfos.endNodeContainer - container of the endNode, or the end node itself in case of text nodes
234
+ * @param {number} rangeInfos.endOffset - same as range.endOffset, but not read-only to allow override
235
+ */
236
+ function wrapTextNodesInRange(rootNode, rangeInfos) {
237
+ var childNodes = rootNode.childNodes;
238
+ var currentNode, i;
239
+ var splitDatas = [];
240
+
241
+ for (i = 0; i < childNodes.length; i++) {
242
+ if (hasWrapped) {
243
+ break;
244
+ }
245
+ currentNode = childNodes[i];
246
+
247
+ if (isBlacklisted(currentNode)) {
248
+ if (isElement(currentNode)) {
249
+ //go deeper in case a descendant of the current blacklisted is whitelisted
250
+ wrapTextNodesInRange(currentNode, rangeInfos);
251
+ }
252
+ } else {
253
+ const isCurrentNodeTextInsideOfAnotherHighlightingWrapper =
254
+ isText(currentNode) &&
255
+ isWrappingNode(currentNode.parentNode) &&
256
+ currentNode.parentNode.className !== className;
257
+
258
+ if (isCurrentNodeTextInsideOfAnotherHighlightingWrapper) {
259
+ const internalRange = new Range();
260
+ internalRange.selectNodeContents(currentNode);
261
+
262
+ if (rangeInfos.startNode === currentNode) {
263
+ internalRange.setStart(currentNode, rangeInfos.startOffset);
264
+ }
265
+
266
+ if (rangeInfos.endNode === currentNode) {
267
+ internalRange.setEnd(currentNode, rangeInfos.endOffset);
268
+ }
269
+
270
+ const isNodeInRange = rangeInfos.commonRange.isPointInRange(currentNode, internalRange.endOffset);
271
+
272
+ // Apply new highlighting color only for selected nodes
273
+ if (isNodeInRange) {
274
+ isWrapping = true;
275
+ highlightContainerNodes(currentNode, className, internalRange, currentGroupId);
276
+ }
277
+ } else {
278
+ // split current node in case the wrapping start/ends on a partially selected text node
279
+ if (currentNode.isSameNode(rangeInfos.startNode)) {
280
+ if (isText(rangeInfos.startNodeContainer) && rangeInfos.startOffset !== 0) {
281
+ // we defer the wrapping to the next iteration of the loop
282
+ //end of node should be highlighted
283
+ rangeInfos.startNode = currentNode.splitText(rangeInfos.startOffset);
284
+ rangeInfos.startOffset = 0;
285
+ splitDatas.push({ node: rangeInfos.startNode, beforeWasSplit: true, afterWasSplit: false });
286
+ } else {
287
+ //whole node should be highlighted
288
+ isWrapping = true;
289
+ splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: false });
290
+ }
291
+ }
292
+
293
+ if (currentNode.isSameNode(rangeInfos.endNode) && isText(rangeInfos.endNodeContainer)) {
294
+ if (rangeInfos.endOffset !== 0) {
295
+ if (rangeInfos.endOffset < currentNode.textContent.length) {
296
+ //start of node should be highlighted
297
+ currentNode.splitText(rangeInfos.endOffset);
298
+ splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: true });
299
+ } else {
300
+ //whole node should be highlighted
301
+ splitDatas.push({ node: currentNode, beforeWasSplit: false, afterWasSplit: false });
302
+ }
303
+ } else {
304
+ isWrapping = false;
305
+ }
306
+ }
307
+
308
+ // wrap the current node...
309
+ if (isText(currentNode)) {
310
+ if (!keepEmptyNodes) {
311
+ wrapTextNode(currentNode, currentGroupId);
312
+ } else if (willHighlightNotBeEmptyAfterMerge(currentNode)) {
313
+ const wrapperNode = wrapTextNode(currentNode, currentGroupId);
314
+ if (wrapperNode) {
315
+ const splitData = splitDatas.find(d => d.node === currentNode);
316
+ addSplitData(wrapperNode,
317
+ splitData ? splitData.beforeWasSplit : false,
318
+ splitData ? splitData.afterWasSplit : false);
319
+ }
320
+ }
321
+
322
+ // ... or continue deeper in the node tree
323
+ } else if (isElement(currentNode)) {
324
+ //some selections end at the very start of the next node, we should end wrapping when we reach such node
325
+ if (!currentNode.isSameNode(rangeInfos.endNode) || rangeInfos.endOffset > 0) {
326
+ wrapTextNodesInRange(currentNode, rangeInfos);
327
+ }
328
+ }
329
+ }
330
+ }
331
+
332
+ // end wrapping ?
333
+ if (currentNode.isSameNode(rangeInfos.endNode)) {
334
+ isWrapping = false;
335
+ hasWrapped = true;
336
+ break;
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Restructure content of the highlighted wrapper according to the selectedRange
343
+ * @param {Node} textNode
344
+ * @param {string} activeClass
345
+ * @param {Range} selectedRange
346
+ * @param {number} currentGroupId
347
+ */
348
+ function highlightContainerNodes(textNode, activeClass, selectedRange, currentGroupId) {
349
+ const container = textNode.parentNode;
350
+ const range = new Range();
351
+ range.selectNodeContents(textNode);
352
+
353
+ const isSelectionCoversNodeStart = range.compareBoundaryPoints(Range.START_TO_START, selectedRange) === 0;
354
+ const isSelectionCoversNodeEnd = range.compareBoundaryPoints(Range.END_TO_END, selectedRange) === 0;
355
+
356
+ /*
357
+ There are 4 possible cases selected area is intersected with already highlighted element.
358
+ In examples below the border is represents the selection, "yellow" is class name of already highlighted
359
+ container, "red" is class name of currently active highlighter
360
+ **********************************************************************************************************
361
+ 1. The container content is completely selected, so that we only have to change the highlighter class name
362
+
363
+ Input:
364
+ __________________________________________________
365
+ | |
366
+ |<span class="yellow"> Lorem ipsum dolor sit</span>|
367
+ |__________________________________________________|
368
+
369
+ Output:
370
+ <span class="red"> Lorem ipsum dolor sit</span>
371
+
372
+ **********************************************************************************************************
373
+ 2. The container content is partially selected from the begging.
374
+
375
+ Input:
376
+ ______________________________
377
+ | |
378
+ |<span class="yellow"> Lorem ip|sum dolor sit</span>
379
+ |______________________________|
380
+
381
+ Output:
382
+ <span class="red"> Lorem ip</span><span class="yellow">sum dolor sit</span>
383
+
384
+ **********************************************************************************************************
385
+ 3. The container content is partially selected at the end.
386
+
387
+ Input:
388
+ ____________________
389
+ | |
390
+ <span class="yellow"> Lorem ip|sum dolor sit</span>|
391
+ |____________________|
392
+
393
+ Output:
394
+ <span class="yellow"> Lorem ip</span><span class="red">sum dolor sit</span>
395
+
396
+ **********************************************************************************************************
397
+ 4. The container content is partially selected in the middle.
398
+
399
+ Input:
400
+ ___________
401
+ | |
402
+ <span class="yellow"> Lorem |ipsum dolor| sit</span>
403
+ |___________|
404
+
405
+ Output:
406
+ <span class="yellow"> Lorem </span><span class="red">ipsum dolor</span><span class="yellow"> sit</span>
407
+ */
408
+ if (isSelectionCoversNodeStart && isSelectionCoversNodeEnd) {
409
+ textNode.parentNode.className = activeClass;
410
+ } else if (isSelectionCoversNodeStart) {
411
+ textNode.splitText(selectedRange.endOffset);
412
+ wrapContainerChildNodes(container, 0, activeClass, currentGroupId);
413
+ } else if (isSelectionCoversNodeEnd) {
414
+ textNode.splitText(selectedRange.startOffset);
415
+ wrapContainerChildNodes(container, 1, activeClass, currentGroupId);
416
+ } else {
417
+ textNode.splitText(selectedRange.startOffset).splitText(selectedRange.endOffset);
418
+ wrapContainerChildNodes(container, 1, activeClass, currentGroupId);
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Wraps all containers text nodes with highlighter element.
424
+ * The child node with index given by indexToWrapNode parameter will be wrap with class given by activeClass parameter
425
+ * @param {Element} container
426
+ * @param {number} indexToWrapNode
427
+ * @param {string} activeClass
428
+ * @param {number} currentGroupId
429
+ */
430
+ function wrapContainerChildNodes(container, indexToWrapNode, activeClass, currentGroupId) {
431
+ const containerClass = container.className;
432
+ const fragment = new DocumentFragment();
433
+ const childNodesLength = container.childNodes.length;
434
+
435
+ container.childNodes.forEach((node, index) => {
436
+ var wrapperNode;
437
+ if (index === indexToWrapNode) {
438
+ wrapperNode = wrapNode(node.cloneNode(), activeClass, currentGroupId);
439
+ } else {
440
+ wrapperNode = wrapNode(node.cloneNode(), containerClass, currentGroupId);
441
+ }
442
+ fragment.appendChild(wrapperNode);
443
+
444
+ if (keepEmptyNodes) {
445
+ addSplitData(wrapperNode,
446
+ index === 0 ? container.dataset.beforeWasSplit : true,
447
+ index === childNodesLength - 1 ? container.dataset.afterWasSplit : true);
448
+ }
449
+ });
450
+
451
+ container.replaceWith(fragment);
452
+ }
453
+
454
+ /**
455
+ * wraps a text node into the highlight span
456
+ * @param {Node} node - the node to wrap
457
+ * @param {number} groupId - the highlight group
458
+ * @returns {Node|null} wrapper node, if it was created
459
+ */
460
+ function wrapTextNode(node, groupId) {
461
+ if (isWrapping && !isWrappingNode(node.parentNode) && isWrappable(node)) {
462
+ $(node).wrap(getWrapper(groupId));
463
+ return node.parentNode;
464
+ }
465
+ return null;
466
+ }
467
+
468
+ /**
469
+ * We need to re-index the groups after a user highlight: either to merge groups or to resolve inconsistencies
470
+ * Recursive.
471
+ *
472
+ * @param {Node} rootNode
473
+ */
474
+ function reindexGroups(rootNode) {
475
+ if (!rootNode) {
476
+ return;
477
+ }
478
+
479
+ var childNodes = rootNode.childNodes;
480
+ var i, currentNode, parent;
481
+
482
+ for (i = 0; i < childNodes.length; i++) {
483
+ currentNode = childNodes[i];
484
+
485
+ if (isWrappable(currentNode)) {
486
+ parent = currentNode.parentNode;
487
+ if (isWrappingNode(parent)) {
488
+ if (isWrapping === false) {
489
+ currentGroupId++;
490
+ }
491
+ isWrapping = true;
492
+ parent.setAttribute(GROUP_ATTR, currentGroupId); // set the new group Id
493
+ } else {
494
+ isWrapping = false;
495
+ }
496
+ } else if (isElement(currentNode)) {
497
+ reindexGroups(currentNode);
498
+ }
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Some highlights may result in having adjacent wrapping nodes. We remove them here to get a cleaner markup.
504
+ *
505
+ * @param {Node} rootNode
506
+ */
507
+ function mergeAdjacentWrappingNodes(rootNode) {
508
+ if (!rootNode) {
509
+ return;
510
+ }
511
+
512
+ var childNodes = rootNode.childNodes;
513
+ var i, currentNode;
514
+
515
+ for (i = 0; i < childNodes.length; i++) {
516
+ currentNode = childNodes[i];
517
+
518
+ if (isWrappingNode(currentNode)) {
519
+ if (keepEmptyNodes) {
520
+ currentNode.normalize();
521
+ }
522
+ while (
523
+ isWrappingNode(currentNode.nextSibling) &&
524
+ currentNode.className === currentNode.nextSibling.className
525
+ ) {
526
+ if (keepEmptyNodes) {
527
+ currentNode.nextSibling.normalize();
528
+ }
529
+ currentNode.firstChild.textContent += currentNode.nextSibling.firstChild.textContent;
530
+ if (keepEmptyNodes) {
531
+ addSplitData(currentNode, currentNode.dataset.beforeWasSplit, currentNode.nextSibling.dataset.afterWasSplit);
532
+ }
533
+ currentNode.parentNode.removeChild(currentNode.nextSibling);
534
+ }
535
+ } else if (isElement(currentNode)) {
536
+ mergeAdjacentWrappingNodes(currentNode);
537
+ }
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Unwraps highlighted nodes with a line break or with an empty content
543
+ */
544
+ function unWrapEmptyHighlights() {
545
+ getHighlightedNodes().each((index, node) => {
546
+ const nodeContent = node.textContent;
547
+
548
+ if (nodeContent.trim().length === 0) {
549
+ if (nodeContent.length === 0 || /\r|\n/.exec(nodeContent)) {
550
+ clearSingleHighlight({ target: node });
551
+ }
552
+ }
553
+ });
554
+ }
555
+
556
+ /**
557
+ * Check condition to avoid the work of `unwrapEmptyHighlights` ahead of time, before `mergeAdjacentNodes` runs,
558
+ * because in `keepEmptyNodes` case we do not want to add nodes to dom unless necessary.
559
+ * Also be more strict and don't allow to select nodes with spaces only, because they may appear in unexpected places in markup
560
+ * (here it's not exactly same as `unwrapEmptyHighlights`).
561
+ * @param {Node} node - node which will be wrapped (highlighted)
562
+ * @returns {Boolean}
563
+ */
564
+ function willHighlightNotBeEmptyAfterMerge(node) {
565
+ if (!node.textContent.length) {
566
+ return false;
567
+ }
568
+ if (node.textContent.trim().length) {
569
+ return true;
570
+ }
571
+ const prevNode = node.previousSibling;
572
+ const canWrapperBeMergedWithPreviousSibling = prevNode && isWrappingNode(prevNode) && prevNode.className === className;
573
+ if (canWrapperBeMergedWithPreviousSibling) {
574
+ return true;
575
+ }
576
+ const nextNode = node.nextSibling;
577
+ const canWrapperBeMergedWithNextSibling = nextNode && isWrappingNode(nextNode) && nextNode.className === className;
578
+ if (canWrapperBeMergedWithNextSibling) {
579
+ return true;
580
+ }
581
+ return false;
582
+ }
583
+
584
+ /**
585
+ * `range.surroundContents` can create empty text nodes,
586
+ * which will cause trouble in `mergeAdjacentNodes` later (in `keepEmptyNodes` case).
587
+ * This method surrounds range, then removes those nodes
588
+ * @param {Range} range
589
+ * @param {Node} wrapperNode
590
+ */
591
+ function rangeSurroundContentsNoEmptyNodes(range, wrapperNode) {
592
+ const containerPreviousSibling = range.commonAncestorContainer.previousSibling;
593
+ const containerNextSibling = range.commonAncestorContainer.nextSibling;
594
+
595
+ range.surroundContents(wrapperNode);
596
+
597
+ removeEmptyTextNodeIfDifferent(wrapperNode.previousSibling, containerPreviousSibling);
598
+ removeEmptyTextNodeIfDifferent(wrapperNode.nextSibling, containerNextSibling);
599
+ }
600
+
601
+ /**
602
+ * Remove `node`, if it's an empty text node and is *not* the same node as `nodeToCompare`
603
+ * @param {Node} node
604
+ * @param {Node} nodeToCompare
605
+ */
606
+ function removeEmptyTextNodeIfDifferent(node, nodeToCompare) {
607
+ if (node && node !== nodeToCompare && isText(node) && node.textContent.length === 0) {
608
+ node.remove();
609
+ }
610
+ }
611
+
612
+ /**
613
+ * Remove all wrapping nodes from markup
614
+ */
615
+ function clearHighlights() {
616
+ getHighlightedNodes().each(function (i, elem) {
617
+ if (!keepEmptyNodes) {
618
+ var $wrapped = $(this);
619
+ $wrapped.replaceWith($wrapped.text());
620
+ } else {
621
+ clearSingleHighlight({ target: elem });
622
+ }
623
+ });
624
+ }
625
+
626
+ /**
627
+ * Remove unwrap dom node
628
+ */
629
+ function clearSingleHighlight(e) {
630
+ if (!keepEmptyNodes) {
631
+ const $wrapped = $(e.target);
632
+ const text = $wrapped.text();
633
+
634
+ // NOTE: JQuery replaceWith is not working with empty string https://bugs.jquery.com/ticket/13401
635
+ if (text === '') {
636
+ $wrapped.remove();
637
+ } else {
638
+ $wrapped.replaceWith(text);
639
+ }
640
+ } else {
641
+ const nodeToRemove = e.target;
642
+ const nodeToRemoveText = nodeToRemove.textContent;
643
+ const beforeWasSplit = nodeToRemove.dataset.beforeWasSplit === 'true';
644
+ const afterWasSplit = nodeToRemove.dataset.afterWasSplit === 'true';
645
+ const prevNode = nodeToRemove.previousSibling;
646
+ const nextNode = nodeToRemove.nextSibling;
647
+
648
+ if (beforeWasSplit && prevNode && isText(prevNode) && prevNode.textContent) {
649
+ //append text to previous sibling
650
+ prevNode.textContent += nodeToRemoveText;
651
+ nodeToRemove.remove();
652
+
653
+ if (afterWasSplit && prevNode.nextSibling && isText(prevNode.nextSibling) && prevNode.nextSibling.textContent) {
654
+ //merge it with next sibling
655
+ prevNode.textContent += prevNode.nextSibling.textContent;
656
+ prevNode.nextSibling.remove();
657
+ }
658
+ }
659
+ else if (afterWasSplit && nextNode && isText(nextNode) && nextNode.textContent) {
660
+ //append text to next sibling
661
+ nextNode.textContent = nodeToRemoveText + nextNode.textContent;
662
+ nodeToRemove.remove();
663
+ } else if (nodeToRemoveText) {
664
+ //keep text in a separate text node
665
+ nodeToRemove.replaceWith(document.createTextNode(nodeToRemoveText));
666
+ } else {
667
+ //text is empty, just remove it
668
+ nodeToRemove.remove();
669
+ }
670
+ }
671
+ }
672
+
673
+ /**
674
+ * Index-related functions:
675
+ * ========================
676
+ * To allow saving and restoring highlights on an equivalent, but different, DOM tree (for example if the markup is deleted and re-created)
677
+ * we build an index containing the status of each text node:
678
+ * - not highlighted
679
+ * - fully highlighted
680
+ * - partially highlighted (= with inline ranges)
681
+ *
682
+ * This index will be used to restore a selection on the new DOM tree
683
+ */
684
+
685
+ /**
686
+ * Bootstrap the process of building the highlight index
687
+ * @returns {Object[]|BuildModelResultKeepEmpty|null}
688
+ */
689
+ function getHighlightIndex() {
690
+ var rootNode = getContainer();
691
+ if (!keepEmptyNodes) {
692
+ var highlightIndex = [];
693
+ if (rootNode) {
694
+ rootNode.normalize();
695
+ textNodesIndex = 0;
696
+ buildHighlightIndex(rootNode, highlightIndex);
697
+ }
698
+ return highlightIndex;
699
+ } else {
700
+ if (rootNode) {
701
+ return buildHighlightModelKeepEmpty(rootNode);
702
+ } else {
703
+ return null;
704
+ }
705
+ }
706
+ }
707
+
708
+ /**
709
+ * Traverse the DOM tree to create the text Nodes index. Recursive.
710
+ * @param {Node} rootNode
711
+ * @param {Object[]} highlightIndex
712
+ */
713
+ function buildHighlightIndex(rootNode, highlightIndex) {
714
+ var childNodes = rootNode.childNodes;
715
+ var i, currentNode;
716
+ var nodeInfos, inlineRange, inlineOffset, nodesToSkip;
717
+
718
+ for (i = 0; i < childNodes.length; i++) {
719
+ currentNode = childNodes[i];
720
+
721
+ // Skip blacklisted nodes
722
+ if (isBlacklisted(currentNode)) {
723
+ continue;
724
+ }
725
+ // A simple node not highlighted and isolated (= not followed by an wrapped text)
726
+ else if (isWrappable(currentNode) && !isWrappingNode(currentNode.nextSibling)) {
727
+ highlightIndex[textNodesIndex] = { highlighted: false };
728
+ textNodesIndex++;
729
+
730
+ // an isolated node (= not followed by a highlight table text) with its whole content highlighted
731
+ } else if (
732
+ isWrappingNode(currentNode) &&
733
+ !isText(currentNode.nextSibling) &&
734
+ (!isWrappingNode(currentNode.nextSibling) ||
735
+ currentNode.className === currentNode.nextSibling.className)
736
+ ) {
737
+ highlightIndex[textNodesIndex] = {
738
+ highlighted: true,
739
+ groupId: currentNode.getAttribute(GROUP_ATTR),
740
+ c: getColorByClassName(currentNode.className)
741
+ };
742
+ textNodesIndex++;
743
+
744
+ // less straightforward: a succession of (at least) 1 wrapping node with 1 wrappable text node, in either order, and possibly more
745
+ // the trick is to create a unique text node on which we will be able to re-apply multiple partial highlights
746
+ // for this, we use 'inlineRanges'
747
+ } else if (isHotNode(currentNode)) {
748
+ nodeInfos = {
749
+ highlighted: true,
750
+ inlineRanges: []
751
+ };
752
+
753
+ nodesToSkip = -1;
754
+ inlineOffset = 0;
755
+
756
+ while (currentNode) {
757
+ if (isWrappingNode(currentNode)) {
758
+ inlineRange = {
759
+ groupId: currentNode.getAttribute(GROUP_ATTR),
760
+ c: getColorByClassName(currentNode.className)
761
+ };
762
+
763
+ if (isText(currentNode.previousSibling) || isWrappingNode(currentNode.previousSibling)) {
764
+ inlineRange.startOffset = inlineOffset;
765
+ }
766
+ if (isText(currentNode.nextSibling) || isWrappingNode(currentNode.nextSibling)) {
767
+ inlineRange.endOffset = inlineOffset + currentNode.textContent.length;
768
+ }
769
+ nodeInfos.inlineRanges.push(inlineRange);
770
+ }
771
+
772
+ inlineOffset += currentNode.textContent.length;
773
+ currentNode =
774
+ isHotNode(currentNode.nextSibling) || isText(currentNode.nextSibling)
775
+ ? currentNode.nextSibling
776
+ : null;
777
+ nodesToSkip++;
778
+ }
779
+ i += nodesToSkip; // we increase the loop counter to avoid looping over the nodes that we just analyzed
780
+
781
+ highlightIndex[textNodesIndex] = nodeInfos;
782
+ textNodesIndex++;
783
+
784
+ // go deeper in the node tree...
785
+ } else if (isElement(currentNode)) {
786
+ buildHighlightIndex(currentNode, highlightIndex);
787
+ }
788
+ }
789
+ }
790
+
791
+ /**
792
+ * @typedef HighlightEntryKeepEmpty
793
+ * @property {String} groupId
794
+ * @property {String} c - color
795
+ * @property {Number} offsetBefore
796
+ * @property {Number} textLength
797
+ * @property {String} beforeWasSplit
798
+ * @property {String} afterWasSplit
799
+ * @property {Array<Number>} path - on each level from root container to highlight, index among siblings
800
+ */
801
+ /**
802
+ * @typedef BuildModelResultKeepEmpty
803
+ * @property {HighlightEntryKeepEmpty[]} highlightModel
804
+ * @property {NodeList} wrapperNodes
805
+ */
806
+ /**
807
+ * For `keepEmptyNodes` option, creates data model of highlights.
808
+ * Additionally returns array of highlight nodes. Traverses DOM tree.
809
+ * @param {Node} rootNode
810
+ * @returns {BuildModelResultKeepEmpty|null} result
811
+ */
812
+ function buildHighlightModelKeepEmpty(rootNode) {
813
+ const classNames = options.colors ? Object.values(options.colors) : [className];
814
+ const wrapperNodesSelector = classNames.map(cls => containerSelector + ' .' + cls).join(', ');
815
+ const wrapperNodes = Array.from(document.querySelectorAll(wrapperNodesSelector))
816
+ .filter(node => !isBlacklisted(node));
817
+
818
+ if (!wrapperNodes.length) {
819
+ return null;
820
+ }
821
+
822
+ var highlightModel = [];
823
+ const indexCache = new Map();
824
+ for (var k = 0; k < wrapperNodes.length; k++) {
825
+ var wrapperNode = wrapperNodes[k];
826
+
827
+ //get info about highlight itself
828
+ var offsetBefore = 0;
829
+ var prevNode = wrapperNode.previousSibling;
830
+ if (prevNode && isText(prevNode)) {
831
+ const beforeWasSplit = wrapperNode.dataset.beforeWasSplit === 'true';
832
+ if (beforeWasSplit) {
833
+ offsetBefore = prevNode.textContent.length;
834
+ }
835
+ }
836
+ var highlightData = {
837
+ groupId: wrapperNode.getAttribute(GROUP_ATTR),
838
+ c: getColorByClassName(wrapperNode.className),
839
+ offsetBefore,
840
+ textLength: wrapperNode.textContent.length,
841
+ beforeWasSplit: wrapperNode.dataset.beforeWasSplit,
842
+ afterWasSplit: wrapperNode.dataset.afterWasSplit,
843
+ path: []
844
+ };
845
+
846
+ //get info about its position in the tree: path through all parents from rootNode to highlight
847
+ let currentNode = wrapperNode;
848
+ while (currentNode && currentNode !== rootNode) {
849
+ let indexInModel = indexCache.get(currentNode);
850
+ if (!indexInModel && indexInModel !== 0) {
851
+ //should be more reliable to ignore empty nodes when indexing
852
+ const childNodes = Array.from(currentNode.parentNode.childNodes).filter(node => !(isText(node) && !node.textContent.length));
853
+ //index among its non-empty siblings
854
+ indexInModel = childNodes.indexOf(currentNode);
855
+ indexCache.set(currentNode, indexInModel);
856
+ }
857
+ highlightData.path.unshift(indexInModel);
858
+ currentNode = currentNode.parentNode;
859
+ }
860
+
861
+ //add info about highlight and its position to model
862
+ highlightModel.push(highlightData);
863
+ }
864
+ return {
865
+ highlightModel,
866
+ wrapperNodes
867
+ };
868
+ }
869
+
870
+ /**
871
+ * Bootstrap the process of restoring the highlights from an index
872
+ * @param {Object[]|HighlightEntryKeepEmpty[]|null} highlightIndex
873
+ */
874
+ function highlightFromIndex(highlightIndex) {
875
+ var rootNode = getContainer();
876
+ if (rootNode) {
877
+ if (!keepEmptyNodes) {
878
+ rootNode.normalize();
879
+ textNodesIndex = 0;
880
+ restoreHighlight(rootNode, highlightIndex);
881
+ } else {
882
+ restoreHighlightKeepEmpty(rootNode, highlightIndex);
883
+ }
884
+ }
885
+ }
886
+
887
+ /**
888
+ * Traverse the DOM tree to wraps the text nodes according to the highlight index. Recursive.
889
+ * @param {Node} rootNode
890
+ * @param {Object[]} highlightIndex
891
+ */
892
+ function restoreHighlight(rootNode, highlightIndex) {
893
+ var childNodes = rootNode.childNodes;
894
+ var i, currentNode, parent;
895
+ var nodeInfos, nodesToSkip, range, initialChildCount;
896
+
897
+ for (i = 0; i < childNodes.length; i++) {
898
+ currentNode = childNodes[i];
899
+
900
+ if (isBlacklisted(currentNode)) {
901
+ continue;
902
+ } else if (isWrappable(currentNode)) {
903
+ parent = currentNode.parentNode;
904
+ initialChildCount = parent.childNodes.length;
905
+
906
+ nodeInfos = highlightIndex[textNodesIndex];
907
+
908
+ if (nodeInfos.highlighted === true) {
909
+ if (_.isArray(nodeInfos.inlineRanges)) {
910
+ nodeInfos.inlineRanges.reverse();
911
+ nodeInfos.inlineRanges.forEach(function (inlineRange) {
912
+ range = document.createRange();
913
+ range.setStart(currentNode, inlineRange.startOffset || 0);
914
+ range.setEnd(currentNode, inlineRange.endOffset || currentNode.textContent.length);
915
+ range.surroundContents(getWrapper(inlineRange.groupId, getClassNameByColor(inlineRange.c)));
916
+ });
917
+
918
+ // fully highlighted text node
919
+ } else {
920
+ range = document.createRange();
921
+ range.selectNodeContents(currentNode);
922
+ range.surroundContents(getWrapper(nodeInfos.groupId, getClassNameByColor(nodeInfos.c)));
923
+ }
924
+ // we do want to loop over the nodes created by the wrapping operation
925
+ nodesToSkip = parent.childNodes.length - initialChildCount;
926
+ i += nodesToSkip;
927
+ }
928
+ textNodesIndex++;
929
+ } else if (isElement(currentNode)) {
930
+ restoreHighlight(currentNode, highlightIndex);
931
+ }
932
+ }
933
+ }
934
+
935
+ /**
936
+ * For `keepEmptyNodes` option, wraps the text nodes according to highlights data model.
937
+ * Traverses and updates DOM tree. Shouldn't throw errors in case of mismatches.
938
+ * @param {Node} rootNode
939
+ * @param {HighlightEntryKeepEmpty[]|null} highlightModel
940
+ */
941
+ function restoreHighlightKeepEmpty(rootNode, highlightModel) {
942
+ if (!highlightModel) {
943
+ return;
944
+ }
945
+
946
+ var currentModel;
947
+ var range;
948
+ for (var k = 0; k < highlightModel.length; k++) {
949
+ currentModel = highlightModel[k];
950
+
951
+ //find node to wrap - go through nodes until we reach level where node to wrap will be
952
+ let childNodes;
953
+ let indexInModel;
954
+ let currentParentNode = rootNode;
955
+ let pathNotFound = false;
956
+ if (!currentModel.path || !currentModel.path.length) {
957
+ continue; //something went wrong
958
+ }
959
+ for (var m = 0; m < currentModel.path.length; m++) {
960
+ //path was counted among non-empty nodes
961
+ childNodes = Array.from(currentParentNode.childNodes).filter(node => !(isText(node) && !node.textContent.length));
962
+ indexInModel = currentModel.path[m];
963
+ currentParentNode = childNodes[indexInModel];
964
+ if (!currentParentNode && m < currentModel.path.length - 1) {
965
+ //node on last level may not exist yet, no need to fail. See `nodeAtIndex`
966
+ pathNotFound = true;
967
+ break;
968
+ }
969
+ }
970
+ if (pathNotFound) {
971
+ continue; //something went wrong
972
+ }
973
+
974
+ //add single highlight
975
+ var nodeAtIndex = null;
976
+ if (!currentModel.offsetBefore) {
977
+ //wrap starts on this node
978
+ nodeAtIndex = childNodes[indexInModel];
979
+ if (!nodeAtIndex || !isText(nodeAtIndex) || isBlacklisted(nodeAtIndex)) {
980
+ continue; //something went wrong
981
+ }
982
+ } else {
983
+ //split previousSibling to create a node for wrapping
984
+ var nodeBefore = childNodes[indexInModel - 1];
985
+ if (!nodeBefore || !isText(nodeBefore) ||
986
+ nodeBefore.textContent.length <= currentModel.offsetBefore || isBlacklisted(nodeBefore)) {
987
+ continue; //something went wrong
988
+ }
989
+ nodeAtIndex = nodeBefore.splitText(currentModel.offsetBefore);
990
+ }
991
+ //cut off its end
992
+ if (nodeAtIndex.textContent.length > currentModel.textLength) {
993
+ nodeAtIndex.splitText(currentModel.textLength);
994
+ }
995
+
996
+ //wrap
997
+ const wrapperNode = getWrapper(currentModel.groupId, getClassNameByColor(currentModel.c));
998
+ addSplitData(wrapperNode, currentModel.beforeWasSplit, currentModel.afterWasSplit);
999
+ range = document.createRange();
1000
+ range.selectNodeContents(nodeAtIndex);
1001
+ rangeSurroundContentsNoEmptyNodes(range, wrapperNode);
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * Set highlighter color
1007
+ * @param {string} color Active highlighter color
1008
+ */
1009
+ const setActiveColor = color => {
1010
+ if (options.colors[color]) {
1011
+ className = options.colors[color];
1012
+ }
1013
+ };
1014
+
1015
+ /**
1016
+ * Helpers
1017
+ */
1018
+
1019
+ /**
1020
+ * Return the object key contains the given value
1021
+ * @param {Object} object
1022
+ * @param {any} value
1023
+ * @return {string|undefined}
1024
+ */
1025
+ const getKeyByValue = (object, value) => Object.keys(object).find(key => object[key] === value);
1026
+
1027
+ /**
1028
+ * Returns color identifier for the given class name
1029
+ * @param {string} highlighterClassName Class name of highlighter classes
1030
+ * @returns {string|number} Color identifier
1031
+ */
1032
+ const getColorByClassName = highlighterClassName => {
1033
+ if (options.colors) {
1034
+ return getKeyByValue(options.colors, highlighterClassName);
1035
+ }
1036
+
1037
+ return className;
1038
+ };
1039
+
1040
+ /**
1041
+ * Returns class name for the given color identifier
1042
+ * @param {string|number} color Color identifier
1043
+ * @returns {string} Class name
1044
+ */
1045
+ const getClassNameByColor = color => {
1046
+ if (options.colors && options.colors[color]) {
1047
+ return options.colors[color];
1048
+ }
1049
+
1050
+ return className;
1051
+ };
1052
+
1053
+ /**
1054
+ * Check if the given node is a wrapper
1055
+ * @param {Node|Element} node
1056
+ * @returns {boolean}
1057
+ */
1058
+ function isWrappingNode(node) {
1059
+ return isElement(node) && node.tagName.toLowerCase() === 'span' && highlightingClasses.includes(node.className);
1060
+ }
1061
+
1062
+ /**
1063
+ * Check if the given node can be wrapped
1064
+ * @param {Node} node
1065
+ * @returns {boolean}
1066
+ */
1067
+ function isWrappable(node) {
1068
+ return isText(node) && !isBlacklisted(node);
1069
+ }
1070
+
1071
+ /**
1072
+ * Check if the given node is, or is within, a blacklisted container.
1073
+ * With `keepEmptyNodes` option, node inside blacklisted container can be whitelisted too.
1074
+ * Priority of blacklist or whitelist is decided by which selector is closest to the node.
1075
+ * If no match found, node is considered whitelisted.
1076
+ * @param {Node} node
1077
+ * @returns {boolean}
1078
+ */
1079
+ function isBlacklisted(node) {
1080
+ const closest = $(node).closest(containersBlackAndWhiteListSelector);
1081
+ if (!closest.length) {
1082
+ return false;
1083
+ } else if (!containersWhiteListSelector) {
1084
+ return true;
1085
+ } else {
1086
+ return !closest.get(0).matches(containersWhiteListSelector);
1087
+ }
1088
+ }
1089
+
1090
+ /**
1091
+ * Wraps text node to the highlighter wrapper element
1092
+ * @param {Node} textNode Text node to wrap
1093
+ * @param {string} className Wrapper class name
1094
+ * @param {number} groupId Group id
1095
+ */
1096
+ function wrapNode(textNode, className, groupId) {
1097
+ const element = getWrapper(groupId, className);
1098
+
1099
+ element.appendChild(textNode);
1100
+ return element;
1101
+ }
1102
+
1103
+ /**
1104
+ * Create a wrapping node
1105
+ * @param {number} groupId
1106
+ * @returns {Element}
1107
+ */
1108
+ function getWrapper(groupId, wrapperClass) {
1109
+ const wrapper = document.createElement('span');
1110
+
1111
+ wrapper.className = wrapperClass || className;
1112
+ wrapper.setAttribute(GROUP_ATTR, `${groupId}`);
1113
+ return wrapper;
1114
+ }
1115
+
1116
+ /**
1117
+ * Returns the first unused group Id
1118
+ * @returns {number}
1119
+ */
1120
+ function getAvailableGroupId() {
1121
+ var id = currentGroupId || 1;
1122
+ while ($(getContainer()).find('[' + GROUP_ATTR + '=' + id + ']').length !== 0) {
1123
+ id++;
1124
+ }
1125
+ return id;
1126
+ }
1127
+
1128
+ /**
1129
+ * Check if the given node is an element
1130
+ * @param {Node} node
1131
+ * @returns {boolean}
1132
+ */
1133
+ function isElement(node) {
1134
+ return node && typeof node === 'object' && node.nodeType === window.Node.ELEMENT_NODE;
1135
+ }
1136
+
1137
+ /**
1138
+ * Check if the given node is of type text
1139
+ * @param {Node} node
1140
+ * @returns {boolean}
1141
+ */
1142
+ function isText(node) {
1143
+ return node && typeof node === 'object' && node.nodeType === window.Node.TEXT_NODE;
1144
+ }
1145
+
1146
+ /**
1147
+ * a "Hot Node" is either wrappable text node or a wrapper
1148
+ * @param {Node} node
1149
+ * @returns {boolean}
1150
+ */
1151
+ function isHotNode(node) {
1152
+ return isWrappingNode(node) || isWrappable(node);
1153
+ }
1154
+
1155
+ /**
1156
+ * Public API of the highlighter helper
1157
+ */
1158
+ return {
1159
+ highlightRanges: highlightRanges,
1160
+ highlightFromIndex: highlightFromIndex,
1161
+ getHighlightIndex: getHighlightIndex,
1162
+ clearHighlights: clearHighlights,
1163
+ clearSingleHighlight: clearSingleHighlight,
1164
+ setActiveColor
1165
+ };
1166
+ }