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