jodit 3.8.2 → 3.8.3
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/.idea/workspace.xml +153 -155
- package/CHANGELOG.MD +36 -9
- package/app.css +2 -1
- package/build/jodit.css +73 -73
- package/build/jodit.es2018.css +56 -56
- package/build/jodit.es2018.en.css +56 -56
- package/build/jodit.es2018.en.js +172 -84
- package/build/jodit.es2018.en.min.css +1 -1
- package/build/jodit.es2018.en.min.js +2 -2
- package/build/jodit.es2018.js +172 -84
- package/build/jodit.es2018.min.css +1 -1
- package/build/jodit.es2018.min.js +2 -2
- package/build/jodit.js +835 -742
- package/build/jodit.min.css +2 -2
- package/build/jodit.min.js +2 -2
- package/package.json +7 -7
- package/src/config.ts +4 -4
- package/src/core/create.ts +15 -15
- package/src/core/helpers/css.ts +3 -5
- package/src/core/helpers/html/strip-tags.ts +36 -4
- package/src/core/helpers/selector.ts +6 -8
- package/src/core/helpers/size/get-scroll-parent.ts +28 -0
- package/src/core/helpers/size/index.ts +1 -0
- package/src/core/selection/select.ts +35 -45
- package/src/core/ui/button/button/button.less +4 -1
- package/src/core/ui/button/button/button.ts +6 -8
- package/src/core/ui/popup/popup.less +4 -4
- package/src/core/ui/popup/popup.ts +17 -7
- package/src/core/view/view-with-toolbar.less +3 -2
- package/src/jodit.ts +6 -3
- package/src/modules/dialog/dialog.ts +1 -2
- package/src/modules/file-browser/builders/context-menu.ts +1 -1
- package/src/modules/file-browser/listeners/state-listeners.ts +2 -1
- package/src/modules/file-browser/styles/preview.less +17 -14
- package/src/modules/widget/color-picker/color-picker.less +12 -11
- package/src/modules/widget/color-picker/color-picker.ts +20 -30
- package/src/plugins/clipboard/paste-storage/paste-storage.ts +9 -20
- package/src/plugins/fix/clean-html.ts +36 -3
- package/src/plugins/iframe.ts +12 -2
- package/src/plugins/link/link.ts +14 -2
- package/src/plugins/resizer/resizer.ts +22 -4
- package/src/plugins/size/resize-handler.ts +1 -1
- package/src/plugins/size/size.less +10 -7
- package/src/plugins/symbols/symbols.less +12 -7
- package/src/plugins/symbols/symbols.ts +9 -6
- package/src/plugins/xpath/xpath.ts +1 -1
- package/src/styles/jodit.less +1 -1
- package/src/types/ui.d.ts +9 -1
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from '../../helpers';
|
|
30
30
|
import { eventEmitter, getContainer } from '../../global';
|
|
31
31
|
import { UIElement } from '../element';
|
|
32
|
-
import { autobind } from '../../decorators';
|
|
32
|
+
import { autobind, throttle } from '../../decorators';
|
|
33
33
|
|
|
34
34
|
type getBoundFunc = () => IBound;
|
|
35
35
|
|
|
@@ -106,9 +106,6 @@ export class Popup extends UIElement implements IPopup {
|
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* Open popup near with some bound
|
|
109
|
-
*
|
|
110
|
-
* @param getBound
|
|
111
|
-
* @param keepPosition
|
|
112
109
|
*/
|
|
113
110
|
open(getBound: getBoundFunc, keepPosition: boolean = false): this {
|
|
114
111
|
markOwner(this.jodit, this.container);
|
|
@@ -135,7 +132,6 @@ export class Popup extends UIElement implements IPopup {
|
|
|
135
132
|
|
|
136
133
|
/**
|
|
137
134
|
* Calculate static bound for point
|
|
138
|
-
* @param getBound
|
|
139
135
|
*/
|
|
140
136
|
protected getKeepBound(getBound: getBoundFunc): getBoundFunc {
|
|
141
137
|
const oldBound = getBound();
|
|
@@ -193,6 +189,12 @@ export class Popup extends UIElement implements IPopup {
|
|
|
193
189
|
return this;
|
|
194
190
|
}
|
|
195
191
|
|
|
192
|
+
@throttle(10)
|
|
193
|
+
@autobind
|
|
194
|
+
throttleUpdatePosition(): void {
|
|
195
|
+
this.updatePosition();
|
|
196
|
+
}
|
|
197
|
+
|
|
196
198
|
/**
|
|
197
199
|
* Calculate start point
|
|
198
200
|
*
|
|
@@ -333,7 +335,7 @@ export class Popup extends UIElement implements IPopup {
|
|
|
333
335
|
}
|
|
334
336
|
|
|
335
337
|
private addGlobalListeners(): void {
|
|
336
|
-
const up = this.
|
|
338
|
+
const up = this.throttleUpdatePosition,
|
|
337
339
|
ow = this.ow;
|
|
338
340
|
|
|
339
341
|
eventEmitter.on('closeAllPopups', this.close);
|
|
@@ -351,10 +353,14 @@ export class Popup extends UIElement implements IPopup {
|
|
|
351
353
|
.on(this.container, 'scroll mousewheel', up)
|
|
352
354
|
.on(ow, 'scroll', up)
|
|
353
355
|
.on(ow, 'resize', up);
|
|
356
|
+
|
|
357
|
+
Dom.up(this.j.container, box => {
|
|
358
|
+
box && this.j.e.on(box, 'scroll mousewheel', up);
|
|
359
|
+
});
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
private removeGlobalListeners(): void {
|
|
357
|
-
const up = this.
|
|
363
|
+
const up = this.throttleUpdatePosition,
|
|
358
364
|
ow = this.ow;
|
|
359
365
|
|
|
360
366
|
eventEmitter.off('closeAllPopups', this.close);
|
|
@@ -373,6 +379,10 @@ export class Popup extends UIElement implements IPopup {
|
|
|
373
379
|
.off(this.container, 'scroll mousewheel', up)
|
|
374
380
|
.off(ow, 'scroll', up)
|
|
375
381
|
.off(ow, 'resize', up);
|
|
382
|
+
|
|
383
|
+
Dom.up(this.j.container, box => {
|
|
384
|
+
box && this.j.e.off(box, 'scroll mousewheel', up);
|
|
385
|
+
});
|
|
376
386
|
}
|
|
377
387
|
|
|
378
388
|
/**
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
&__box {
|
|
11
11
|
&:not(:empty) {
|
|
12
12
|
--color-background-default: var(--color-panel);
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
overflow: hidden;
|
|
15
|
+
border-bottom: 1px solid var(--color-border);
|
|
16
|
+
background-color: var(--color-panel);
|
|
15
17
|
border-radius: var(--border-radius-default)
|
|
16
18
|
var(--border-radius-default) 0 0;
|
|
17
|
-
border-bottom: 1px solid var(--color-border);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
}
|
package/src/jodit.ts
CHANGED
|
@@ -402,15 +402,18 @@ export class Jodit extends ViewWithToolbar implements IJodit {
|
|
|
402
402
|
|
|
403
403
|
/**
|
|
404
404
|
* Set value to native editor
|
|
405
|
-
* @param value
|
|
406
405
|
*/
|
|
407
406
|
setNativeEditorValue(value: string): void {
|
|
408
|
-
|
|
407
|
+
const data = {
|
|
408
|
+
value
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
if (this.e.fire('beforeSetNativeEditorValue', data)) {
|
|
409
412
|
return;
|
|
410
413
|
}
|
|
411
414
|
|
|
412
415
|
if (this.editor) {
|
|
413
|
-
this.editor.innerHTML = value;
|
|
416
|
+
this.editor.innerHTML = data.value;
|
|
414
417
|
}
|
|
415
418
|
}
|
|
416
419
|
|
|
@@ -660,7 +660,7 @@ export class Dialog extends ViewWithToolbar implements IDialog {
|
|
|
660
660
|
* //You can close dialog two ways
|
|
661
661
|
* var dialog = new Jodit.modules.Dialog();
|
|
662
662
|
* dialog.open('Hello world!', 'Title');
|
|
663
|
-
* var $close =
|
|
663
|
+
* var $close = dialog.create.fromHTML('<a href="#" style="float:left;" class="jodit-button">
|
|
664
664
|
* <i class="icon icon-check"></i> ' + Jodit.prototype.i18n('Ok') + '</a>');
|
|
665
665
|
* $close.addEventListener('click', function () {
|
|
666
666
|
* dialog.close();
|
|
@@ -690,7 +690,6 @@ export class Dialog extends ViewWithToolbar implements IDialog {
|
|
|
690
690
|
* Called up to close the window
|
|
691
691
|
*
|
|
692
692
|
* @event beforeClose
|
|
693
|
-
* @this {Dialog} current dialog
|
|
694
693
|
*/
|
|
695
694
|
if (this.e) {
|
|
696
695
|
this.e.fire('beforeClose', this);
|
|
@@ -17,7 +17,7 @@ import { openImageEditor } from '../../image-editor/image-editor';
|
|
|
17
17
|
|
|
18
18
|
const CLASS_PREVIEW = F_CLASS + '_preview_',
|
|
19
19
|
preview_tpl_next = (next = 'next', right = 'right') =>
|
|
20
|
-
`<
|
|
20
|
+
`<div class="${CLASS_PREVIEW}navigation ${CLASS_PREVIEW}navigation-${next}">` +
|
|
21
21
|
'' +
|
|
22
22
|
Icon.get('angle-' + right) +
|
|
23
23
|
'</a>';
|
|
@@ -123,7 +123,7 @@ export function stateListeners(this: FileBrowser): void {
|
|
|
123
123
|
F_CLASS + '__tree-item',
|
|
124
124
|
{
|
|
125
125
|
draggable: 'draggable',
|
|
126
|
-
href: '
|
|
126
|
+
href: '#',
|
|
127
127
|
'data-path': normalizePath(
|
|
128
128
|
source.path,
|
|
129
129
|
name + '/'
|
|
@@ -144,6 +144,7 @@ export function stateListeners(this: FileBrowser): void {
|
|
|
144
144
|
});
|
|
145
145
|
|
|
146
146
|
e.stopPropagation();
|
|
147
|
+
e.preventDefault();
|
|
147
148
|
};
|
|
148
149
|
|
|
149
150
|
this.e.on(folderElm, 'click', action('openFolder'));
|
|
@@ -7,50 +7,53 @@
|
|
|
7
7
|
@import (reference) '../../../styles/variables';
|
|
8
8
|
|
|
9
9
|
.jodit-filebrowser_preview {
|
|
10
|
-
|
|
10
|
+
position: relative;
|
|
11
|
+
display: flex;
|
|
12
|
+
|
|
11
13
|
min-width: 600px;
|
|
12
14
|
max-width: 1000px;
|
|
13
15
|
min-height: 700px;
|
|
14
16
|
max-height: 100%;
|
|
15
|
-
|
|
16
|
-
display: flex;
|
|
17
|
-
justify-content: center;
|
|
17
|
+
|
|
18
18
|
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
|
|
21
|
+
text-align: center;
|
|
19
22
|
|
|
20
23
|
@media (max-width: @screen-sm) {
|
|
21
|
-
max-width: 100%;
|
|
22
|
-
max-height: 100%;
|
|
23
24
|
min-width: auto;
|
|
24
|
-
|
|
25
|
+
max-width: 100%;
|
|
25
26
|
height: 100%;
|
|
27
|
+
min-height: auto;
|
|
28
|
+
max-height: 100%;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
&_box {
|
|
29
|
-
flex-grow: 1;
|
|
30
32
|
display: flex;
|
|
31
|
-
|
|
33
|
+
flex-grow: 1;
|
|
32
34
|
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
&_navigation {
|
|
36
39
|
position: absolute;
|
|
37
40
|
top: 0;
|
|
38
|
-
height: 100%;
|
|
39
41
|
left: 0;
|
|
42
|
+
height: 100%;
|
|
40
43
|
|
|
41
44
|
&-next {
|
|
42
|
-
left: auto;
|
|
43
45
|
right: 0;
|
|
46
|
+
left: auto;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
svg {
|
|
47
|
-
width: 45px;
|
|
48
|
-
height: 45px;
|
|
49
50
|
position: relative;
|
|
50
51
|
top: 50%;
|
|
52
|
+
width: 45px;
|
|
53
|
+
height: 45px;
|
|
51
54
|
margin-top: -22px;
|
|
52
|
-
transition: fill 0.3s linear;
|
|
53
55
|
fill: #9e9ba7;
|
|
56
|
+
transition: fill 0.3s linear;
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
&:hover svg {
|
|
@@ -11,33 +11,34 @@
|
|
|
11
11
|
--color-cell-size: 24px;
|
|
12
12
|
--color-cell-icon-size: 12px;
|
|
13
13
|
|
|
14
|
-
text-align: left;
|
|
15
14
|
margin: 0;
|
|
15
|
+
text-align: left;
|
|
16
16
|
user-select: none;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
margin-bottom: calc(var(--padding-default) / 2);
|
|
20
|
-
white-space: normal;
|
|
21
|
-
max-width: calc(var(--color-cell-size) * 10);
|
|
18
|
+
&__group {
|
|
22
19
|
display: flex;
|
|
20
|
+
max-width: calc(var(--color-cell-size) * 10);
|
|
23
21
|
flex-wrap: wrap;
|
|
22
|
+
margin-bottom: calc(var(--padding-default) / 2);
|
|
23
|
+
white-space: normal;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
&__color-item {
|
|
27
|
+
display: block;
|
|
28
|
+
|
|
27
29
|
width: var(--color-cell-size);
|
|
28
30
|
height: var(--color-cell-size);
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
border: 1px solid transparent;
|
|
33
|
+
text-align: center;
|
|
31
34
|
text-decoration: none;
|
|
32
35
|
vertical-align: middle;
|
|
33
|
-
text-align: center;
|
|
34
|
-
border: 1px solid transparent;
|
|
35
36
|
|
|
36
37
|
&:hover {
|
|
37
38
|
border-color: #000;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
&_active_true,
|
|
41
42
|
&:active {
|
|
42
43
|
border: 2px solid var(--color-border-selected);
|
|
43
44
|
}
|
|
@@ -45,9 +46,9 @@
|
|
|
45
46
|
|
|
46
47
|
&__native {
|
|
47
48
|
svg {
|
|
49
|
+
display: inline-block;
|
|
48
50
|
width: 16px;
|
|
49
51
|
height: 16px;
|
|
50
|
-
display: inline-block;
|
|
51
52
|
margin-right: 4px;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -41,8 +41,9 @@ export const ColorPickerWidget = (
|
|
|
41
41
|
callback: (newColor: string) => void,
|
|
42
42
|
coldColor: string
|
|
43
43
|
): HTMLDivElement => {
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const cn = 'jodit-color-picker',
|
|
45
|
+
valueHex = normalizeColor(coldColor),
|
|
46
|
+
form = editor.c.div(cn),
|
|
46
47
|
iconPalette: string = editor.o.textIcons
|
|
47
48
|
? `<span>${editor.i18n('palette')}</span>`
|
|
48
49
|
: Icon.get('palette'),
|
|
@@ -52,9 +53,7 @@ export const ColorPickerWidget = (
|
|
|
52
53
|
if (isPlainObject(colors)) {
|
|
53
54
|
Object.keys(colors).forEach(key => {
|
|
54
55
|
stack.push(
|
|
55
|
-
|
|
56
|
-
key +
|
|
57
|
-
'">'
|
|
56
|
+
`<div class="${cn}__group ${cn}__group-${key}">`
|
|
58
57
|
);
|
|
59
58
|
stack.push(eachColor((colors as any)[key]));
|
|
60
59
|
stack.push('</div>');
|
|
@@ -62,35 +61,26 @@ export const ColorPickerWidget = (
|
|
|
62
61
|
} else if (isArray(colors)) {
|
|
63
62
|
colors.forEach(color => {
|
|
64
63
|
stack.push(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
?
|
|
68
|
-
: ''
|
|
69
|
-
|
|
70
|
-
color +
|
|
71
|
-
'" style="background-color:' +
|
|
72
|
-
color +
|
|
73
|
-
'" data-color="' +
|
|
74
|
-
color +
|
|
75
|
-
'" href="javascript:void(0)"></a>'
|
|
64
|
+
`<span class='${cn}__color-item ${
|
|
65
|
+
valueHex === color
|
|
66
|
+
? cn + '__color-item_active_true'
|
|
67
|
+
: ''
|
|
68
|
+
}' title="${color}" style="background-color:${color}" data-color="${color}"></span>`
|
|
76
69
|
);
|
|
77
70
|
});
|
|
78
71
|
}
|
|
72
|
+
|
|
79
73
|
return stack.join('');
|
|
80
74
|
};
|
|
81
75
|
|
|
82
76
|
form.appendChild(
|
|
83
77
|
editor.c.fromHTML(
|
|
84
|
-
|
|
85
|
-
eachColor(editor.o.colors) +
|
|
86
|
-
'</div>'
|
|
78
|
+
`<div class="${cn}__groups">${eachColor(editor.o.colors)}</div>`
|
|
87
79
|
)
|
|
88
80
|
);
|
|
89
81
|
|
|
90
82
|
form.appendChild(
|
|
91
|
-
editor.c.fromHTML(
|
|
92
|
-
'<div data-ref="extra" class="jodit-color-picker__extra"></div>'
|
|
93
|
-
)
|
|
83
|
+
editor.c.fromHTML(`<div data-ref="extra" class="${cn}__extra"></div>`)
|
|
94
84
|
);
|
|
95
85
|
|
|
96
86
|
const { extra } = refs(form);
|
|
@@ -98,17 +88,14 @@ export const ColorPickerWidget = (
|
|
|
98
88
|
if (editor.o.showBrowserColorPicker && hasBrowserColorPicker()) {
|
|
99
89
|
extra.appendChild(
|
|
100
90
|
editor.c.fromHTML(
|
|
101
|
-
|
|
102
|
-
iconPalette +
|
|
103
|
-
'<input type="color" value="#ffffff"/>' +
|
|
104
|
-
'</div>'
|
|
91
|
+
`<div class="${cn}__native">${iconPalette}<input type="color" value="#ffffff"/></div>`
|
|
105
92
|
)
|
|
106
93
|
);
|
|
107
94
|
|
|
108
95
|
editor.e.on(form, 'change', (e: MouseEvent) => {
|
|
109
96
|
e.stopPropagation();
|
|
110
97
|
|
|
111
|
-
const target
|
|
98
|
+
const target = e.target as HTMLInputElement;
|
|
112
99
|
|
|
113
100
|
if (!target || !target.tagName || !Dom.isTag(target, 'input')) {
|
|
114
101
|
return;
|
|
@@ -138,18 +125,21 @@ export const ColorPickerWidget = (
|
|
|
138
125
|
) {
|
|
139
126
|
target = Dom.closest(
|
|
140
127
|
target.parentNode,
|
|
141
|
-
'
|
|
128
|
+
'span',
|
|
142
129
|
editor.editor
|
|
143
130
|
) as HTMLElement;
|
|
144
131
|
}
|
|
145
132
|
|
|
146
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
!Dom.isTag(target, 'span') ||
|
|
135
|
+
!target.classList.contains(cn + '__color-item')
|
|
136
|
+
) {
|
|
147
137
|
return;
|
|
148
138
|
}
|
|
149
139
|
|
|
150
140
|
const color: string = attr(target, '-color') || '';
|
|
151
141
|
|
|
152
|
-
if (callback &&
|
|
142
|
+
if (callback && isFunction(callback)) {
|
|
153
143
|
callback(color);
|
|
154
144
|
}
|
|
155
145
|
|
|
@@ -16,6 +16,7 @@ import { Dialog } from '../../../modules/dialog';
|
|
|
16
16
|
import { Plugin } from '../../../core/plugin';
|
|
17
17
|
import { Dom } from '../../../core/dom';
|
|
18
18
|
import { attr, toArray } from '../../../core/helpers';
|
|
19
|
+
import { Button } from '../../../core/ui';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Show dialog choose content to paste
|
|
@@ -121,7 +122,7 @@ export class pasteStorage extends Plugin {
|
|
|
121
122
|
|
|
122
123
|
this.j.e.on(a, 'keydown', this.onKeyDown);
|
|
123
124
|
|
|
124
|
-
attr(a, 'href', '
|
|
125
|
+
attr(a, 'href', '#');
|
|
125
126
|
attr(a, 'data-index', index.toString());
|
|
126
127
|
attr(a, 'tab-index', '-1');
|
|
127
128
|
|
|
@@ -140,25 +141,13 @@ export class pasteStorage extends Plugin {
|
|
|
140
141
|
language: this.j.o.language
|
|
141
142
|
});
|
|
142
143
|
|
|
143
|
-
const pasteButton
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
this.j.e.on(pasteButton, 'click', this.paste);
|
|
152
|
-
|
|
153
|
-
const cancelButton: HTMLAnchorElement = this.j.c.fromHTML(
|
|
154
|
-
'<a href="javascript:void(0)" style="float:right; margin-right: 10px;" class="jodit-button">' +
|
|
155
|
-
'<span>' +
|
|
156
|
-
this.j.i18n('Cancel') +
|
|
157
|
-
'</span>' +
|
|
158
|
-
'</a>'
|
|
159
|
-
) as HTMLAnchorElement;
|
|
160
|
-
|
|
161
|
-
this.j.e.on(cancelButton, 'click', this.dialog.close);
|
|
144
|
+
const pasteButton = Button(this.j, 'paste', 'Paste', 'primary');
|
|
145
|
+
|
|
146
|
+
pasteButton.onAction(this.paste);
|
|
147
|
+
|
|
148
|
+
const cancelButton = Button(this.j, '', 'Cancel');
|
|
149
|
+
|
|
150
|
+
cancelButton.onAction(this.dialog.close);
|
|
162
151
|
|
|
163
152
|
this.container = this.j.c.div();
|
|
164
153
|
this.container.classList.add('jodit-paste-storage');
|
|
@@ -18,9 +18,9 @@ import {
|
|
|
18
18
|
INSEPARABLE_TAGS
|
|
19
19
|
} from '../../core/constants';
|
|
20
20
|
import { Dom, Select } from '../../modules';
|
|
21
|
-
import { isString, keys, trim } from '../../core/helpers';
|
|
21
|
+
import { isString, keys, safeHTML, trim } from '../../core/helpers';
|
|
22
22
|
import { Plugin } from '../../core/plugin';
|
|
23
|
-
import { autobind, debounce } from '../../core/decorators';
|
|
23
|
+
import { watch, autobind, debounce } from '../../core/decorators';
|
|
24
24
|
import { findNotEmptySibling } from '../keyboard/helpers';
|
|
25
25
|
|
|
26
26
|
declare module '../../config' {
|
|
@@ -36,6 +36,16 @@ declare module '../../config' {
|
|
|
36
36
|
removeEmptyElements: boolean;
|
|
37
37
|
replaceOldTags: IDictionary<HTMLTagNames> | false;
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Remove onError attributes
|
|
41
|
+
*/
|
|
42
|
+
removeOnError: boolean;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Safe href="javascript:" links
|
|
46
|
+
*/
|
|
47
|
+
safeJavaScriptLink: boolean;
|
|
48
|
+
|
|
39
49
|
/**
|
|
40
50
|
* The allowTags option defines which elements will remain in the
|
|
41
51
|
* edited text when the editor saves. You can use this limit the returned HTML.
|
|
@@ -96,7 +106,10 @@ Config.prototype.cleanHTML = {
|
|
|
96
106
|
b: 'strong'
|
|
97
107
|
},
|
|
98
108
|
allowTags: false,
|
|
99
|
-
denyTags: false
|
|
109
|
+
denyTags: false,
|
|
110
|
+
|
|
111
|
+
removeOnError: true,
|
|
112
|
+
safeJavaScriptLink: true
|
|
100
113
|
};
|
|
101
114
|
|
|
102
115
|
Config.prototype.controls.eraser = {
|
|
@@ -139,6 +152,8 @@ export class cleanHtml extends Plugin {
|
|
|
139
152
|
|
|
140
153
|
const editor = this.j;
|
|
141
154
|
|
|
155
|
+
this.onSafeHTML(editor.editor);
|
|
156
|
+
|
|
142
157
|
const current = editor.s.current();
|
|
143
158
|
|
|
144
159
|
const replaceOldTags = editor.o.cleanHTML.replaceOldTags;
|
|
@@ -528,6 +543,24 @@ export class cleanHtml extends Plugin {
|
|
|
528
543
|
return false;
|
|
529
544
|
}
|
|
530
545
|
|
|
546
|
+
/**
|
|
547
|
+
* Event handler when manually assigning a value to the HTML editor.
|
|
548
|
+
*/
|
|
549
|
+
@watch(':beforeSetNativeEditorValue')
|
|
550
|
+
protected onBeforeSetNativeEditorValue(data: { value: string }): boolean {
|
|
551
|
+
const sandBox = this.j.createInside.div();
|
|
552
|
+
sandBox.innerHTML = data.value;
|
|
553
|
+
this.onSafeHTML(sandBox);
|
|
554
|
+
data.value = sandBox.innerHTML;
|
|
555
|
+
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
@watch(':safeHTML')
|
|
560
|
+
protected onSafeHTML(sandBox: HTMLElement): void {
|
|
561
|
+
safeHTML(sandBox, this.j.o.cleanHTML);
|
|
562
|
+
}
|
|
563
|
+
|
|
531
564
|
/** @override */
|
|
532
565
|
protected override beforeDestruct(): void {
|
|
533
566
|
this.j.e.off('.cleanHtml');
|
package/src/plugins/iframe.ts
CHANGED
|
@@ -321,7 +321,7 @@ export function iframe(editor: IJodit): void {
|
|
|
321
321
|
)
|
|
322
322
|
.on(
|
|
323
323
|
'beforeSetNativeEditorValue',
|
|
324
|
-
(value: string): boolean => {
|
|
324
|
+
({ value }: { value: string }): boolean => {
|
|
325
325
|
if (editor.isLocked) {
|
|
326
326
|
return false;
|
|
327
327
|
}
|
|
@@ -340,16 +340,26 @@ export function iframe(editor: IJodit): void {
|
|
|
340
340
|
);
|
|
341
341
|
doc.close();
|
|
342
342
|
editor.editor = doc.body;
|
|
343
|
+
editor.e.fire(
|
|
344
|
+
'safeHTML',
|
|
345
|
+
editor.editor
|
|
346
|
+
);
|
|
343
347
|
|
|
344
348
|
toggleEditable();
|
|
345
349
|
editor.e.fire('prepareWYSIWYGEditor');
|
|
350
|
+
|
|
351
|
+
editor.e.stopPropagation(
|
|
352
|
+
'beforeSetNativeEditorValue'
|
|
353
|
+
);
|
|
346
354
|
}
|
|
347
355
|
} else {
|
|
348
356
|
doc.body.innerHTML = value;
|
|
349
357
|
}
|
|
350
358
|
|
|
351
359
|
return true;
|
|
352
|
-
}
|
|
360
|
+
},
|
|
361
|
+
undefined,
|
|
362
|
+
true
|
|
353
363
|
);
|
|
354
364
|
}
|
|
355
365
|
|
package/src/plugins/link/link.ts
CHANGED
|
@@ -360,6 +360,7 @@ export class link extends Plugin {
|
|
|
360
360
|
|
|
361
361
|
if (unlink) {
|
|
362
362
|
jodit.e.on(unlink, 'click', (e: MouseEvent) => {
|
|
363
|
+
jodit.s.restore();
|
|
363
364
|
jodit.observer.snapshot.restore(snapshot);
|
|
364
365
|
|
|
365
366
|
if (link) {
|
|
@@ -382,6 +383,7 @@ export class link extends Plugin {
|
|
|
382
383
|
|
|
383
384
|
let links: HTMLAnchorElement[];
|
|
384
385
|
|
|
386
|
+
jodit.s.restore();
|
|
385
387
|
jodit.s.removeMarkers();
|
|
386
388
|
jodit.editor.normalize();
|
|
387
389
|
jodit.observer.snapshot.restore(snapshot);
|
|
@@ -405,6 +407,8 @@ export class link extends Plugin {
|
|
|
405
407
|
jodit.s.insertNode(a, false, false);
|
|
406
408
|
links = [a];
|
|
407
409
|
}
|
|
410
|
+
|
|
411
|
+
links.forEach(link => jodit.s.select(link));
|
|
408
412
|
} else {
|
|
409
413
|
links = [link];
|
|
410
414
|
}
|
|
@@ -445,12 +449,20 @@ export class link extends Plugin {
|
|
|
445
449
|
}
|
|
446
450
|
|
|
447
451
|
if (!isImageContent) {
|
|
452
|
+
let newContent = a.textContent;
|
|
453
|
+
|
|
448
454
|
if (content_input.value.trim().length) {
|
|
449
455
|
if (textWasChanged) {
|
|
450
|
-
|
|
456
|
+
newContent = content_input.value;
|
|
451
457
|
}
|
|
452
458
|
} else {
|
|
453
|
-
|
|
459
|
+
newContent = url_input.value;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const content = a.textContent;
|
|
463
|
+
|
|
464
|
+
if (newContent !== content) {
|
|
465
|
+
a.textContent = newContent;
|
|
454
466
|
}
|
|
455
467
|
}
|
|
456
468
|
|
|
@@ -42,6 +42,11 @@ declare module '../../config' {
|
|
|
42
42
|
showSize: boolean;
|
|
43
43
|
hideSizeTimeout: number;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* When resizing images, change not the styles but the width and height attributes
|
|
47
|
+
*/
|
|
48
|
+
forImageChangeAttributes: boolean;
|
|
49
|
+
|
|
45
50
|
/**
|
|
46
51
|
* The minimum width for the editable element
|
|
47
52
|
*/
|
|
@@ -60,6 +65,7 @@ Config.prototype.allowResizeTags = ['img', 'iframe', 'table', 'jodit'];
|
|
|
60
65
|
Config.prototype.resizer = {
|
|
61
66
|
showSize: true,
|
|
62
67
|
hideSizeTimeout: 1000,
|
|
68
|
+
forImageChangeAttributes: true,
|
|
63
69
|
min_width: 10,
|
|
64
70
|
min_height: 10
|
|
65
71
|
};
|
|
@@ -119,7 +125,7 @@ export class resizer extends Plugin {
|
|
|
119
125
|
'afterGetValueFromEditor.resizer',
|
|
120
126
|
(data: { value: string }) => {
|
|
121
127
|
const rgx =
|
|
122
|
-
/<jodit[^>]+data-jodit_iframe_wrapper[^>]+>(.*?<iframe[^>]
|
|
128
|
+
/<jodit[^>]+data-jodit_iframe_wrapper[^>]+>(.*?<iframe[^>]*>.*?<\/iframe>.*?)<\/jodit>/gi;
|
|
123
129
|
|
|
124
130
|
if (rgx.test(data.value)) {
|
|
125
131
|
data.value = data.value.replace(rgx, '$1');
|
|
@@ -276,14 +282,14 @@ export class resizer extends Plugin {
|
|
|
276
282
|
|
|
277
283
|
if (new_w > this.j.o.resizer.min_width) {
|
|
278
284
|
if (new_w < (this.rect.parentNode as HTMLElement).offsetWidth) {
|
|
279
|
-
|
|
285
|
+
this.applySize(this.element, 'width', new_w);
|
|
280
286
|
} else {
|
|
281
|
-
|
|
287
|
+
this.applySize(this.element, 'width', '100%');
|
|
282
288
|
}
|
|
283
289
|
}
|
|
284
290
|
|
|
285
291
|
if (new_h > this.j.o.resizer.min_height) {
|
|
286
|
-
|
|
292
|
+
this.applySize(this.element, 'height', new_h);
|
|
287
293
|
}
|
|
288
294
|
|
|
289
295
|
this.updateSize();
|
|
@@ -297,6 +303,18 @@ export class resizer extends Plugin {
|
|
|
297
303
|
}
|
|
298
304
|
};
|
|
299
305
|
|
|
306
|
+
private applySize(
|
|
307
|
+
element: HTMLElement,
|
|
308
|
+
key: 'width' | 'height',
|
|
309
|
+
value: number | string
|
|
310
|
+
): void {
|
|
311
|
+
if (Dom.isImage(element) && this.j.o.resizer.forImageChangeAttributes) {
|
|
312
|
+
attr(element, key, value);
|
|
313
|
+
} else {
|
|
314
|
+
css(element, key, value);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
300
318
|
private onClickOutside = (e: MouseEvent) => {
|
|
301
319
|
if (this.isShown) {
|
|
302
320
|
if (this.isResized) {
|