suneditor 2.42.0 → 2.43.2

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/src/lib/util.js CHANGED
@@ -16,6 +16,7 @@ const util = {
16
16
  isIE: null,
17
17
  isIE_Edge: null,
18
18
  isOSX_IOS: null,
19
+ isChromium: null,
19
20
  _propertiesInit: function () {
20
21
  if (this._d) return;
21
22
  this._d = document;
@@ -23,6 +24,7 @@ const util = {
23
24
  this.isIE = navigator.userAgent.indexOf('Trident') > -1;
24
25
  this.isIE_Edge = (navigator.userAgent.indexOf('Trident') > -1) || (navigator.appVersion.indexOf('Edge') > -1);
25
26
  this.isOSX_IOS = /(Mac|iPhone|iPod|iPad)/.test(navigator.platform);
27
+ this.isChromium = !!window.chrome;
26
28
  },
27
29
 
28
30
  _allowedEmptyNodeList: '.se-component, pre, blockquote, hr, li, table, img, iframe, video, audio, canvas',
@@ -61,6 +63,7 @@ const util = {
61
63
  * @returns {Boolean}
62
64
  */
63
65
  onlyZeroWidthSpace: function (text) {
66
+ if (text === null || text === undefined) return false;
64
67
  if (typeof text !== 'string') text = text.textContent;
65
68
  return text === '' || this.onlyZeroWidthRegExp.test(text);
66
69
  },
@@ -92,6 +95,30 @@ const util = {
92
95
  }
93
96
  },
94
97
 
98
+ /**
99
+ * @description Object.values
100
+ * @param {Object|null} obj Object parameter.
101
+ * @returns {Array}
102
+ */
103
+ getValues: function (obj) {
104
+ return !obj ? [] : this._w.Object.keys(obj).map(function (i) {
105
+ return obj[i];
106
+ });
107
+ },
108
+
109
+ /**
110
+ * @description Convert the CamelCase To the KebabCase.
111
+ * @param {String|Array} param [Camel string]
112
+ * @returns {String|Array}
113
+ */
114
+ camelToKebabCase: function (param) {
115
+ if (typeof param === 'string') {
116
+ return param.replace(/[A-Z]/g, function (letter) { return "-" + letter.toLowerCase(); });
117
+ } else {
118
+ return param.map(function(str) { return util.camelToKebabCase(str); });
119
+ }
120
+ },
121
+
95
122
  /**
96
123
  * @description Create Element node
97
124
  * @param {String} elementName Element name
@@ -1733,7 +1760,10 @@ const util = {
1733
1760
 
1734
1761
  // wrong position
1735
1762
  const wrongTags = this.getListChildNodes(documentFragment, function (current) {
1736
- if (current.nodeType !== 1) return false;
1763
+ if (current.nodeType !== 1) {
1764
+ if (this.isList(current.parentNode)) removeTags.push(current);
1765
+ return false;
1766
+ }
1737
1767
 
1738
1768
  // white list
1739
1769
  if (htmlCheckBlacklistRegExp.test(current.nodeName) || (!htmlCheckWhitelistRegExp.test(current.nodeName) && current.childNodes.length === 0 && this.isNotCheckingNode(current))) {
package/src/options.d.ts CHANGED
@@ -24,6 +24,10 @@ export interface SunEditorOptions {
24
24
  * If not, the value of the "target textarea".
25
25
  */
26
26
  value?: string;
27
+ /**
28
+ * When recording the history stack, this is the delay time (miliseconds) since the last input. default: 400
29
+ */
30
+ historyStackDelayTime?: number;
27
31
  /**
28
32
  * Whitelist
29
33
  * ======
@@ -221,8 +225,19 @@ export interface SunEditorOptions {
221
225
  */
222
226
  maxHeight?: string;
223
227
  /**
224
- * Editing area default style
228
+ * Editing area
229
+ * ===================
225
230
  */
231
+ /**
232
+ * Add a "class" to the editing area[.sun-editor-editable]
233
+ */
234
+ className?: string;
235
+ /**
236
+ * You can define the style of the editing area[.sun-editor-editable].
237
+ * It affects the entire editing area.
238
+ * ('z-index', 'position' and 'width' properties apply to the top div.)
239
+ * @example 'font-family: cursive; font-size: 10px;'
240
+ */
226
241
  defaultStyle?: string;
227
242
  /**
228
243
  * Defining menu items
@@ -118,6 +118,7 @@ export default {
118
118
 
119
119
  try {
120
120
  const oA = this.plugins.anchor.createAnchor.call(this, this.context.anchor.caller.link, false);
121
+ if (oA === null) return;
121
122
 
122
123
  if (!this.context.dialog.updateModal) {
123
124
  const selectedFormats = this.getSelectedElements();
@@ -817,12 +817,13 @@ export default {
817
817
 
818
818
  if (!onlyH) w = this.util.getNumber(w, 0);
819
819
  if (!onlyW) h = this.util.isNumber(h) ? h + contextVideo.sizeUnit : !h ? '' : h;
820
+ w ? w + contextVideo.sizeUnit : '';
820
821
 
821
- if (!onlyH) contextVideo._element.style.width = w ? w + contextVideo.sizeUnit : '';
822
+ if (!onlyH) contextVideo._element.style.width = w;
822
823
  if (!onlyW) contextVideo._cover.style.paddingBottom = contextVideo._cover.style.height = h;
823
824
 
824
825
  if (!onlyH && !/%$/.test(w)) {
825
- contextVideo._cover.style.width = '';
826
+ contextVideo._cover.style.width = w;
826
827
  contextVideo._container.style.width = '';
827
828
  }
828
829
 
@@ -119,25 +119,32 @@ export default {
119
119
  },
120
120
 
121
121
  on: function (contextAnchor, update) {
122
+ const anchorPlugin = this.plugins.anchor;
123
+
122
124
  if (!update) {
123
- this.plugins.anchor.init.call(this, contextAnchor);
125
+ anchorPlugin.init.call(this, contextAnchor);
124
126
  contextAnchor.anchorText.value = this.getSelection().toString().trim();
125
127
  contextAnchor.newWindowCheck.checked = this.options.linkTargetNewWindow;
126
128
  } else if (contextAnchor.linkAnchor) {
127
129
  this.context.dialog.updateModal = true;
128
130
  const href = this.options.linkNoPrefix ? contextAnchor.linkAnchor.href.replace(contextAnchor.linkAnchor.origin + '/', '') : contextAnchor.linkAnchor.href;
129
- contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = /\#.+$/.test(href) ? href.substr(href.lastIndexOf('#')) : href;
131
+ contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = anchorPlugin.selfPathBookmark.call(this, href) ? href.substr(href.lastIndexOf('#')) : href;
130
132
  contextAnchor.anchorText.value = contextAnchor.linkAnchor.textContent || contextAnchor.linkAnchor.getAttribute('alt');
131
133
  contextAnchor.newWindowCheck.checked = (/_blank/i.test(contextAnchor.linkAnchor.target) ? true : false);
132
134
  contextAnchor.downloadCheck.checked = contextAnchor.linkAnchor.download;
133
135
  }
134
136
 
135
137
  this.context.anchor.callerContext = contextAnchor;
136
- this.plugins.anchor.setRel.call(this, contextAnchor, (update && contextAnchor.linkAnchor) ? contextAnchor.linkAnchor.rel : contextAnchor.defaultRel);
137
- this.plugins.anchor.setLinkPreview.call(this, contextAnchor, contextAnchor.linkValue);
138
+ anchorPlugin.setRel.call(this, contextAnchor, (update && contextAnchor.linkAnchor) ? contextAnchor.linkAnchor.rel : contextAnchor.defaultRel);
139
+ anchorPlugin.setLinkPreview.call(this, contextAnchor, contextAnchor.linkValue);
138
140
  this.plugins.selectMenu.on.call(this, contextAnchor.callerName, this.plugins.anchor.setHeaderBookmark);
139
141
  },
140
142
 
143
+ selfPathBookmark: function(path) {
144
+ const href = this._w.location.href.replace(/\/$/, '');
145
+ return path.indexOf('#') === 0 || (path.indexOf(href) === 0 && path.indexOf('#') === (href.indexOf("#") === -1 ? href.length : href.substr(0, href.indexOf("#")).length));
146
+ },
147
+
141
148
  _closeRelMenu: null,
142
149
  toggleRelList: function (contextAnchor, show) {
143
150
  if (!show) {
@@ -278,13 +285,13 @@ export default {
278
285
  const value = e.target.value.trim();
279
286
  this.plugins.anchor.setLinkPreview.call(this, contextAnchor, value);
280
287
 
281
- if (/^#/.test(value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, value);
288
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, value);
282
289
  else this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
283
290
  },
284
291
 
285
292
  onFocusUrlInput: function (contextAnchor, contextLink) {
286
293
  const value = contextAnchor.urlInput.value;
287
- if (/^#/.test(value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, contextLink, value);
294
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, contextLink, value);
288
295
  },
289
296
 
290
297
  onBlurUrlInput: function (contextList) {
@@ -297,9 +304,9 @@ export default {
297
304
  const noPrefix = this.options.linkNoPrefix;
298
305
  const reservedProtocol = /^(mailto\:|tel\:|sms\:|https*\:\/\/|#)/.test(value);
299
306
  const sameProtocol = !protocol ? false : this._w.RegExp('^' + value.substr(0, protocol.length)).test(protocol);
300
- context.linkValue = preview.textContent = !value ? '' : noPrefix ? value : (protocol && !reservedProtocol && !sameProtocol) ? protocol + value : reservedProtocol ? value : /^www\./.test(value) ? 'http://' + value : this.context.anchor.host + (/^\//.test(value) ? '' : '/') + value;
307
+ value = context.linkValue = preview.textContent = !value ? '' : noPrefix ? value : (protocol && !reservedProtocol && !sameProtocol) ? protocol + value : reservedProtocol ? value : /^www\./.test(value) ? 'http://' + value : this.context.anchor.host + (/^\//.test(value) ? '' : '/') + value;
301
308
 
302
- if (value.indexOf('#') === 0) {
309
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) {
303
310
  context.bookmark.style.display = 'block';
304
311
  this.util.addClass(context.bookmarkButton, 'active');
305
312
  } else {
@@ -307,7 +314,7 @@ export default {
307
314
  this.util.removeClass(context.bookmarkButton, 'active');
308
315
  }
309
316
 
310
- if (value.indexOf('#') === -1 && context.downloadCheck.checked) {
317
+ if (!this.plugins.anchor.selfPathBookmark.call(this, value) && context.downloadCheck.checked) {
311
318
  context.download.style.display = 'block';
312
319
  } else {
313
320
  context.download.style.display = 'none';
@@ -323,7 +330,7 @@ export default {
323
330
 
324
331
  updateAnchor: function (anchor, url, alt, contextAnchor, notText) {
325
332
  // download
326
- if (!/^\#/.test(url) && contextAnchor.downloadCheck.checked) {
333
+ if (!this.plugins.anchor.selfPathBookmark.call(this, url) && contextAnchor.downloadCheck.checked) {
327
334
  anchor.setAttribute('download', alt || url);
328
335
  } else {
329
336
  anchor.removeAttribute('download');
@@ -356,7 +363,7 @@ export default {
356
363
  const anchorText = anchor.value.length === 0 ? url : anchor.value;
357
364
 
358
365
  const oA = contextAnchor.linkAnchor || this.util.createElement('A');
359
- this.plugins.anchor.updateAnchor(oA, url, anchorText, contextAnchor, notText);
366
+ this.plugins.anchor.updateAnchor.call(this, oA, url, anchorText, contextAnchor, notText);
360
367
 
361
368
  contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = contextAnchor.anchorText.value = '';
362
369
 
@@ -365,7 +372,7 @@ export default {
365
372
 
366
373
  onClick_bookmarkButton: function (contextAnchor) {
367
374
  let url = contextAnchor.urlInput.value;
368
- if (/^\#/.test(url)) {
375
+ if (this.plugins.anchor.selfPathBookmark.call(this, url)) {
369
376
  url = url.substr(1);
370
377
  contextAnchor.bookmark.style.display = 'none';
371
378
  this.util.removeClass(contextAnchor.bookmarkButton, 'active');
@@ -61,7 +61,7 @@ export default {
61
61
  */
62
62
  active: function (element) {
63
63
  if (!element) {
64
- this.util.changeTxt(this.context.fontSize.targetText, this.hasFocus ? this.wwComputedStyle.fontSize : this.lang.toolbar.fontSize);
64
+ this.util.changeTxt(this.context.fontSize.targetText, this.hasFocus ? (this.options.__defaultFontSize || this.wwComputedStyle.fontSize) : this.lang.toolbar.fontSize);
65
65
  } else if (element.style && element.style.fontSize.length > 0) {
66
66
  this.util.changeTxt(this.context.fontSize.targetText, element.style.fontSize);
67
67
  return true;
@@ -65,11 +65,7 @@ export default {
65
65
  const icon = button.firstElementChild;
66
66
  const util = this.util;
67
67
 
68
- if (!element) {
69
- button.removeAttribute('data-focus');
70
- util.changeElement(icon, this.context.list.icons.number);
71
- util.removeClass(button, 'active');
72
- } else if (util.isList(element)) {
68
+ if (util.isList(element)) {
73
69
  const nodeName = element.nodeName;
74
70
  button.setAttribute('data-focus', nodeName);
75
71
  util.addClass(button, 'active');
@@ -80,6 +76,10 @@ export default {
80
76
  }
81
77
 
82
78
  return true;
79
+ } else {
80
+ button.removeAttribute('data-focus');
81
+ util.changeElement(icon, this.context.list.icons.number);
82
+ util.removeClass(button, 'active');
83
83
  }
84
84
 
85
85
  return false;
@@ -234,6 +234,14 @@ export default {
234
234
 
235
235
  newCell = util.createElement('LI');
236
236
  util.copyFormatAttributes(newCell, fTag);
237
+
238
+ if (i === 0 && originRange.sc === fTag) {
239
+ originRange.sc = newCell;
240
+ }
241
+ if (i === len - 1 && originRange.ec === fTag) {
242
+ originRange.ec = newCell;
243
+ }
244
+
237
245
  if (util.isComponent(fTag)) {
238
246
  const isHR = /^HR$/i.test(fTag.nodeName);
239
247
  if (!isHR) newCell.innerHTML = '<br>';
@@ -12,7 +12,9 @@ export default {
12
12
  display: 'submenu',
13
13
  add: function (core, targetElement) {
14
14
  const context = core.context;
15
- context.template = {};
15
+ context.template = {
16
+ selectedIndex: -1
17
+ };
16
18
 
17
19
  /** set submenu */
18
20
  let templateDiv = this.setSubmenu(core);
@@ -55,7 +57,8 @@ export default {
55
57
  e.preventDefault();
56
58
  e.stopPropagation();
57
59
 
58
- const temp = this.options.templates[e.target.getAttribute('data-value')];
60
+ this.context.template.selectedIndex = e.target.getAttribute('data-value') * 1;
61
+ const temp = this.options.templates[this.context.template.selectedIndex];
59
62
 
60
63
  if (temp.html) {
61
64
  this.setContents(temp.html);