suneditor 2.47.7 → 2.47.9
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/README.md +11 -11
- package/dist/suneditor.min.js +2 -2
- package/package.json +13 -2
- package/src/lang/pt_br.js +9 -9
- package/src/lib/core.js +93 -19
- package/src/options.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "suneditor",
|
|
3
|
-
"version": "2.47.
|
|
3
|
+
"version": "2.47.9",
|
|
4
4
|
"description": "Vanilla JavaScript WYSIWYG web editor (2.x legacy version, actively maintained)",
|
|
5
5
|
"author": "JiHong.Lee",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "src/suneditor.js",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"tag": "legacy"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": "<13.0.0"
|
|
13
|
+
},
|
|
8
14
|
"sideEffects": [
|
|
9
15
|
"./src/assets/css/*.css"
|
|
10
16
|
],
|
|
@@ -18,7 +24,7 @@
|
|
|
18
24
|
"bugs": {
|
|
19
25
|
"url": "https://github.com/JiHong88/SunEditor/issues"
|
|
20
26
|
},
|
|
21
|
-
"homepage": "
|
|
27
|
+
"homepage": "https://legacy.suneditor.com",
|
|
22
28
|
"repository": {
|
|
23
29
|
"type": "git",
|
|
24
30
|
"url": "https://github.com/JiHong88/SunEditor.git"
|
|
@@ -44,7 +50,12 @@
|
|
|
44
50
|
"jshint": "~2.13.4",
|
|
45
51
|
"karma": "~6.3.19",
|
|
46
52
|
"karma-chrome-launcher": "~2.2.0",
|
|
53
|
+
"karma-firefox-launcher": "^2.1.2",
|
|
54
|
+
"karma-ie-launcher": "^1.0.0",
|
|
47
55
|
"karma-jasmine": "~1.1.2",
|
|
56
|
+
"karma-opera-launcher": "^1.0.0",
|
|
57
|
+
"karma-safari-launcher": "^1.0.0",
|
|
58
|
+
"karma-webpack": "^4.0.2",
|
|
48
59
|
"katex": "^0.16.21",
|
|
49
60
|
"mini-css-extract-plugin": "~0.4.5",
|
|
50
61
|
"optimize-css-assets-webpack-plugin": "~5.0.4",
|
package/src/lang/pt_br.js
CHANGED
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
bold: 'Negrito',
|
|
34
34
|
underline: 'Sublinhado',
|
|
35
35
|
italic: 'Itálico',
|
|
36
|
-
strike: '
|
|
37
|
-
subscript: '
|
|
36
|
+
strike: 'Tachado',
|
|
37
|
+
subscript: 'Subscrito',
|
|
38
38
|
superscript: 'Sobrescrito',
|
|
39
|
-
removeFormat: 'Remover
|
|
40
|
-
fontColor: 'Cor da
|
|
39
|
+
removeFormat: 'Remover formatação',
|
|
40
|
+
fontColor: 'Cor da fonte',
|
|
41
41
|
hiliteColor: 'Cor de destaque',
|
|
42
42
|
indent: 'Recuo',
|
|
43
43
|
outdent: 'Avançar',
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
redo: 'Refazer',
|
|
67
67
|
preview: 'Prever',
|
|
68
68
|
print: 'Imprimir',
|
|
69
|
-
tag_p: '
|
|
69
|
+
tag_p: 'Parágrafo',
|
|
70
70
|
tag_div: '(DIV) Normal',
|
|
71
71
|
tag_h: 'Cabeçalho',
|
|
72
72
|
tag_blockquote: 'Citar',
|
|
@@ -83,9 +83,9 @@
|
|
|
83
83
|
dialogBox: {
|
|
84
84
|
linkBox: {
|
|
85
85
|
title: 'Inserir link',
|
|
86
|
-
url: '
|
|
87
|
-
text: 'Texto
|
|
88
|
-
newWindowCheck: 'Abrir em nova guia',
|
|
86
|
+
url: 'Endereço do link',
|
|
87
|
+
text: 'Texto exibido',
|
|
88
|
+
newWindowCheck: 'Abrir em uma nova guia',
|
|
89
89
|
downloadLinkCheck: 'Link para Download',
|
|
90
90
|
bookmark: 'marcar páginas'
|
|
91
91
|
},
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
audioBox: {
|
|
110
110
|
title: 'Inserir áudio',
|
|
111
111
|
file: 'Selecionar arquivos',
|
|
112
|
-
url: 'URL
|
|
112
|
+
url: 'URL do áudio'
|
|
113
113
|
},
|
|
114
114
|
browser: {
|
|
115
115
|
tags: 'Tag',
|
package/src/lib/core.js
CHANGED
|
@@ -1057,20 +1057,22 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
1057
1057
|
*/
|
|
1058
1058
|
setRange: function (startCon, startOff, endCon, endOff) {
|
|
1059
1059
|
if (!startCon || !endCon) return;
|
|
1060
|
-
if (startOff > startCon.textContent.length) startOff = startCon.textContent.length;
|
|
1061
|
-
|
|
1060
|
+
if ((util.isBreak(startCon) || startCon.nodeType === 3) && startOff > startCon.textContent.length) startOff = startCon.textContent.length;
|
|
1061
|
+
if ((util.isBreak(endCon) || endCon.nodeType === 3) && endOff > endCon.textContent.length) endOff = endCon.textContent.length;
|
|
1062
1062
|
if (util.isFormatElement(startCon)) {
|
|
1063
|
-
startCon = startCon.childNodes[startOff
|
|
1064
|
-
startOff = startOff > 0 ? startCon.nodeType === 1 ? 1 : startCon.textContent ? startCon.textContent.length : 0 : 0;
|
|
1063
|
+
startCon = startCon.childNodes[startOff > 0 ? startCon.childNodes.length - 1 : 0] || startCon;
|
|
1064
|
+
startOff = startOff > 0 ? (startCon.nodeType === 1 && !util.isBreak(startCon) ? 1 : startCon.textContent ? startCon.textContent.length : 0) : 0;
|
|
1065
1065
|
}
|
|
1066
1066
|
if (util.isFormatElement(endCon)) {
|
|
1067
|
-
endCon = endCon.childNodes[endOff
|
|
1068
|
-
endOff = endOff > 0 ? endCon.nodeType === 1 ? 1 : endCon.textContent ? endCon.textContent.length : 0 : 0;
|
|
1067
|
+
endCon = endCon.childNodes[endOff > 0 ? endCon.childNodes.length - 1 : 0] || endCon;
|
|
1068
|
+
endOff = endOff > 0 ? (endCon.nodeType === 1 && !util.isBreak(endCon) ? 1 : endCon.textContent ? endCon.textContent.length : 0) : 0;
|
|
1069
1069
|
}
|
|
1070
1070
|
|
|
1071
1071
|
const range = this._wd.createRange();
|
|
1072
1072
|
|
|
1073
1073
|
try {
|
|
1074
|
+
if (startOff > startCon.textContent.length) startOff = startCon.textContent.length;
|
|
1075
|
+
if (endOff > endCon.textContent.length) endOff = endCon.textContent.length;
|
|
1074
1076
|
range.setStart(startCon, startOff);
|
|
1075
1077
|
range.setEnd(endCon, endOff);
|
|
1076
1078
|
} catch (error) {
|
|
@@ -1574,7 +1576,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
1574
1576
|
let formatEl = util.getFormatElement(selectionNode, null);
|
|
1575
1577
|
|
|
1576
1578
|
if (util.isListCell(formatEl)) {
|
|
1577
|
-
this.insertNode(element, selectionNode === formatEl ? null : r.container.nextSibling, false);
|
|
1579
|
+
this.insertNode(element, selectionNode === formatEl ? null : (selectionNode || r.container).nextSibling, false);
|
|
1578
1580
|
if (!element.nextSibling) element.parentNode.appendChild(util.createElement('BR'));
|
|
1579
1581
|
} else {
|
|
1580
1582
|
if (this.getRange().collapsed && (r.container.nodeType === 3 || util.isBreak(r.container))) {
|
|
@@ -1669,7 +1671,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
1669
1671
|
const isList = util.isListCell(container.parentNode);
|
|
1670
1672
|
let componentTop, wScroll, w;
|
|
1671
1673
|
// top
|
|
1672
|
-
if (isList ? !container.previousSibling : !util.isFormatElement(container.previousElementSibling)) {
|
|
1674
|
+
if (isList ? !container.previousSibling || util.isComponent(container.previousElementSibling) : !util.isFormatElement(container.previousElementSibling)) {
|
|
1673
1675
|
this._variable._lineBreakComp = container;
|
|
1674
1676
|
wScroll = context.element.wysiwyg.scrollTop;
|
|
1675
1677
|
componentTop = util.getOffset(element, context.element.wysiwygFrame).top + wScroll;
|
|
@@ -1682,7 +1684,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
1682
1684
|
t_style.display = 'none';
|
|
1683
1685
|
}
|
|
1684
1686
|
// bottom
|
|
1685
|
-
if (isList ? !container.nextSibling : !util.isFormatElement(container.nextElementSibling)) {
|
|
1687
|
+
if (isList ? !container.nextSibling || util.isComponent(container.nextElementSibling) : !util.isFormatElement(container.nextElementSibling)) {
|
|
1686
1688
|
if (!componentTop) {
|
|
1687
1689
|
this._variable._lineBreakComp = container;
|
|
1688
1690
|
wScroll = context.element.wysiwyg.scrollTop;
|
|
@@ -1803,7 +1805,9 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
1803
1805
|
const depthFormat = util.getParentElement(container, function (current) { return this.isRangeFormatElement(current) || this.isListCell(current); }.bind(util));
|
|
1804
1806
|
afterNode = util.splitElement(container, r.offset, !depthFormat ? 0 : util.getElementDepth(depthFormat) + 1);
|
|
1805
1807
|
if (!afterNode) {
|
|
1806
|
-
|
|
1808
|
+
if (!util.isListCell(line)) {
|
|
1809
|
+
tempAfterNode = afterNode = line;
|
|
1810
|
+
}
|
|
1807
1811
|
} else if (insertListCell) {
|
|
1808
1812
|
if (line.contains(container)) {
|
|
1809
1813
|
const subList = util.isList(line.lastElementChild);
|
|
@@ -5352,7 +5356,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
5352
5356
|
|
|
5353
5357
|
// blacklist
|
|
5354
5358
|
const bAttr = this._attributesTagsBlacklist[tagName];
|
|
5355
|
-
m = m.replace(/\s(?:on[a-z]+)\s*=\s*(")[^"]*\1/
|
|
5359
|
+
m = m.replace(/\s(?:on[a-z]+)\s*=\s*(?:(["'])[^"']*\1|\S+)/gi, '');
|
|
5356
5360
|
if (bAttr) m = m.replace(bAttr, '');
|
|
5357
5361
|
else m = m.replace(this._attributesBlacklistRegExp, '');
|
|
5358
5362
|
|
|
@@ -5394,7 +5398,7 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
5394
5398
|
if (v) {
|
|
5395
5399
|
for (let i = 0, len = v.length, a; i < len; i++) {
|
|
5396
5400
|
// if (lowLevelCheck && /^class="(?!(__se__|se-|katex))/.test(v[i].trim())) continue;
|
|
5397
|
-
a = (
|
|
5401
|
+
a = (_isSafeAttribute(v[i].trim()) ? v[i] : '');
|
|
5398
5402
|
t += (/^\s/.test(a) ? '' : ' ') + a;
|
|
5399
5403
|
}
|
|
5400
5404
|
}
|
|
@@ -6232,9 +6236,22 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
6232
6236
|
commonCon.parentNode.insertBefore(format, commonCon);
|
|
6233
6237
|
format.appendChild(commonCon);
|
|
6234
6238
|
}
|
|
6239
|
+
|
|
6240
|
+
if (util.isWysiwygDiv(commonCon)) {
|
|
6241
|
+
format = util.createElement(formatName || options.defaultTag);
|
|
6242
|
+
format.innerHTML = commonCon.innerHTML;
|
|
6243
|
+
commonCon.innerHTML = '';
|
|
6244
|
+
commonCon.appendChild(format);
|
|
6245
|
+
this.effectNode = null;
|
|
6246
|
+
this.setRange(format, 1, format, 1);
|
|
6247
|
+
return;
|
|
6248
|
+
}
|
|
6235
6249
|
|
|
6236
|
-
if (
|
|
6237
|
-
|
|
6250
|
+
if (format) {
|
|
6251
|
+
if (util.isBreak(format.nextSibling)) util.removeItem(format.nextSibling);
|
|
6252
|
+
if (util.isBreak(format.previousSibling)) util.removeItem(format.previousSibling);
|
|
6253
|
+
}
|
|
6254
|
+
|
|
6238
6255
|
if (util.isBreak(focusNode)) {
|
|
6239
6256
|
const zeroWidth = util.createTextNode(util.zeroWidthSpace);
|
|
6240
6257
|
focusNode.parentNode.insertBefore(zeroWidth, focusNode);
|
|
@@ -7196,16 +7213,20 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
7196
7213
|
|
|
7197
7214
|
// component
|
|
7198
7215
|
if (!selectRange && formatEl && (range.startOffset === 0 || (selectionNode === formatEl ? !!formatEl.childNodes[range.startOffset] : false))) {
|
|
7216
|
+
const isList = util.isListCell(formatEl);
|
|
7199
7217
|
const sel = selectionNode === formatEl ? formatEl.childNodes[range.startOffset] : selectionNode;
|
|
7200
|
-
const prev = formatEl.previousSibling;
|
|
7218
|
+
const prev = isList ? sel.previousSibling : formatEl.previousSibling;
|
|
7201
7219
|
// select file component
|
|
7202
|
-
const ignoreZWS = (commonCon.nodeType === 3 || util.isBreak(commonCon)) && !commonCon.previousSibling && range.startOffset === 0;
|
|
7203
|
-
if (sel && !sel.previousSibling && (
|
|
7220
|
+
const ignoreZWS = isList || (commonCon.nodeType === 3 || util.isBreak(commonCon)) && !commonCon.previousSibling && range.startOffset === 0;
|
|
7221
|
+
if (sel && ((isList || !sel.previousSibling)) && ((commonCon && util.isComponent(commonCon.previousSibling)) || (ignoreZWS && util.isComponent(prev)))) {
|
|
7204
7222
|
const fileComponentInfo = core.getFileComponent(prev);
|
|
7205
7223
|
if (fileComponentInfo) {
|
|
7206
7224
|
e.preventDefault();
|
|
7207
7225
|
e.stopPropagation();
|
|
7208
|
-
|
|
7226
|
+
|
|
7227
|
+
if (isList) util.removeItem(sel);
|
|
7228
|
+
else if (formatEl.textContent.length === 0) util.removeItem(formatEl);
|
|
7229
|
+
|
|
7209
7230
|
if (core.selectComponent(fileComponentInfo.target, fileComponentInfo.pluginName) === false) core.blur();
|
|
7210
7231
|
} else if (util.isComponent(prev)) {
|
|
7211
7232
|
e.preventDefault();
|
|
@@ -7736,6 +7757,8 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
7736
7757
|
break;
|
|
7737
7758
|
}
|
|
7738
7759
|
|
|
7760
|
+
if (core.currentFileComponentInfo) core.controllersOff();
|
|
7761
|
+
|
|
7739
7762
|
if (shift && keyCode === 16) {
|
|
7740
7763
|
e.preventDefault();
|
|
7741
7764
|
e.stopPropagation();
|
|
@@ -7876,7 +7899,6 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
7876
7899
|
|
|
7877
7900
|
const selectedComponentInfo = core.getFileComponent(selectionNodeDeepestFirstChild);
|
|
7878
7901
|
if (!(e.keyCode === 16 || e.shiftKey) && selectedComponentInfo) core.selectComponent(selectedComponentInfo.target, selectedComponentInfo.pluginName);
|
|
7879
|
-
else if (core.currentFileComponentInfo) core.controllersOff();
|
|
7880
7902
|
|
|
7881
7903
|
/** when format tag deleted */
|
|
7882
7904
|
if (keyCode === 8 && util.isWysiwygDiv(selectionNode) && selectionNode.textContent === '' && selectionNode.children.length === 0) {
|
|
@@ -9430,4 +9452,56 @@ export default function (context, pluginCallButtons, plugins, lang, options, _re
|
|
|
9430
9452
|
}
|
|
9431
9453
|
|
|
9432
9454
|
return functions;
|
|
9455
|
+
}
|
|
9456
|
+
|
|
9457
|
+
/** @description Safe URL protocol whitelist */
|
|
9458
|
+
const _SAFE_URL_PROTOCOL = /^(?:https?|ftps?|mailto|tel|blob|sms|geo|webcal|callto):|^[#/]|^data:image\//i;
|
|
9459
|
+
const _URL_ATTR_PATTERN = /^(?:href|src)\s*=/i;
|
|
9460
|
+
const _RE_ATTR_VALUE = /=\s*(?:"([^"]*)"|'([^']*)'|(\S+))/;
|
|
9461
|
+
const _RE_COLON = /:/i;
|
|
9462
|
+
|
|
9463
|
+
/**
|
|
9464
|
+
* @description Normalize a URL by decoding HTML entities, URL-encoded characters,
|
|
9465
|
+
* and stripping whitespace/control characters. Detects obfuscated dangerous protocols.
|
|
9466
|
+
* @param {string} url
|
|
9467
|
+
* @returns {string}
|
|
9468
|
+
*/
|
|
9469
|
+
function _normalizeURL(url) {
|
|
9470
|
+
let prev, limit = 5;
|
|
9471
|
+
do {
|
|
9472
|
+
prev = url;
|
|
9473
|
+
url = url.replace(/&(#x([0-9a-f]+)|#([0-9]+)|([a-z]+));/gi, function (_, __, hex, dec) {
|
|
9474
|
+
if (hex) return String.fromCharCode(parseInt(hex, 16));
|
|
9475
|
+
if (dec) return String.fromCharCode(parseInt(dec, 10));
|
|
9476
|
+
return '';
|
|
9477
|
+
});
|
|
9478
|
+
} while (url !== prev && --limit);
|
|
9479
|
+
|
|
9480
|
+
try { url = decodeURIComponent(url); } catch (e) { /* malformed URI */ }
|
|
9481
|
+
|
|
9482
|
+
url = url.replace(/[\u0000-\u0020]+/g, '');
|
|
9483
|
+
return url;
|
|
9484
|
+
}
|
|
9485
|
+
|
|
9486
|
+
/**
|
|
9487
|
+
* @description Check if a URL is safe (matches the allowed protocol whitelist).
|
|
9488
|
+
* @param {string} url
|
|
9489
|
+
* @returns {boolean}
|
|
9490
|
+
*/
|
|
9491
|
+
function _isSafeURL(url) {
|
|
9492
|
+
var normalized = _normalizeURL(url);
|
|
9493
|
+
return _SAFE_URL_PROTOCOL.test(normalized) || !_RE_COLON.test(normalized);
|
|
9494
|
+
}
|
|
9495
|
+
|
|
9496
|
+
/**
|
|
9497
|
+
* @description Check if an attribute string (e.g. 'href="..."') contains a safe URL.
|
|
9498
|
+
* @param {string} attr
|
|
9499
|
+
* @returns {boolean}
|
|
9500
|
+
*/
|
|
9501
|
+
function _isSafeAttribute(attr) {
|
|
9502
|
+
if (!_URL_ATTR_PATTERN.test(attr)) return true;
|
|
9503
|
+
var urlMatch = attr.match(_RE_ATTR_VALUE);
|
|
9504
|
+
if (!urlMatch) return true;
|
|
9505
|
+
var url = urlMatch[1] || urlMatch[2] || urlMatch[3] || '';
|
|
9506
|
+
return _isSafeURL(url);
|
|
9433
9507
|
}
|
package/src/options.d.ts
CHANGED
|
@@ -452,7 +452,7 @@ export interface SunEditorOptions {
|
|
|
452
452
|
videoMultipleFile?: boolean;
|
|
453
453
|
/**
|
|
454
454
|
* Define "Attributes" of the video tag.
|
|
455
|
-
* @example { poster: "
|
|
455
|
+
* @example { poster: "https://suneditor-files.s3.ap-northeast-2.amazonaws.com/sample/v2/docs/loading.gif", autoplay: true }
|
|
456
456
|
*/
|
|
457
457
|
videoTagAttrs?: Record<string, string | boolean>;
|
|
458
458
|
/**
|