@innovastudio/contentbuilder 1.4.124 → 1.4.126

Sign up to get free protection for your applications and to get access to all the features.
@@ -1439,7 +1439,7 @@ class Util {
1439
1439
  const selection = this.builder.win.getSelection();
1440
1440
  const container = this.builder.dom.getElm();
1441
1441
  if (!container) return;
1442
- const sameSelection = container && container.innerText === selection.toString();
1442
+ const sameSelection = container && container.innerText === selection.toString().trim();
1443
1443
  if (sameSelection || selection.toString().trim() === '') {
1444
1444
  this.builder.selectionElm = container;
1445
1445
 
@@ -3395,7 +3395,7 @@ class Dom {
3395
3395
  if (callback) callback(false);
3396
3396
  return;
3397
3397
  }
3398
- const sameSelection = container && container.innerText === selection.toString();
3398
+ const sameSelection = container && container.innerText === selection.toString().trim();
3399
3399
  let newElement;
3400
3400
  if (sameSelection || selection.toString().trim() === '') {
3401
3401
  newElement = this.updateSelection(action, value, container);
@@ -3519,7 +3519,7 @@ class Dom {
3519
3519
  const selection = this.builder.win.getSelection();
3520
3520
  let container = this.getElm();
3521
3521
  if (!container) return;
3522
- const sameSelection = container && container.innerText === selection.toString();
3522
+ const sameSelection = container && container.innerText === selection.toString().trim();
3523
3523
  if (currentElement) {
3524
3524
  if (mode === 'block') if (this.getStyle(container, 'display') === 'inline') {
3525
3525
  container = this.getParentBlock(container);
@@ -3592,7 +3592,7 @@ class Dom {
3592
3592
  }
3593
3593
  }
3594
3594
  let blocks = [];
3595
- const blockElms = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'pre'];
3595
+ const blockElms = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'pre', 'td', 'th'];
3596
3596
  elements.forEach(item => {
3597
3597
  const tagName = item.tagName.toLowerCase();
3598
3598
  if (blockElms.includes(tagName)) {
@@ -3600,6 +3600,21 @@ class Dom {
3600
3600
  blocks.push(item);
3601
3601
  }
3602
3602
  });
3603
+
3604
+ /*
3605
+ If a block is double clicked to select the block, the selection goes to the next block as well.
3606
+ This seems a default behavior (tested with simple contentEditable div). So the blocks contains 2 blocks.
3607
+ To fix this, perform an extra test here:
3608
+ */
3609
+ if (blocks.length === 2) {
3610
+ // console.log(blocks[0].innerText.trim());
3611
+ // console.log(selection.toString().trim());
3612
+ if (blocks[0].innerText.trim() === selection.toString().trim()) {
3613
+ blocks.pop();
3614
+ // console.log('remove last')
3615
+ }
3616
+ }
3617
+
3603
3618
  if (multiSelectBlocks) {
3604
3619
  return blocks;
3605
3620
  } else {
@@ -3733,7 +3748,7 @@ class Dom {
3733
3748
  const selection = this.builder.win.getSelection();
3734
3749
  const container = this.getElm();
3735
3750
  if (!container) return;
3736
- const sameSelection = container && container.innerText === selection.toString();
3751
+ const sameSelection = container && container.innerText === selection.toString().trim();
3737
3752
  if (sameSelection || selection.toString().trim() === '') {
3738
3753
  this.updateSelectionToggle(action, value, config, container);
3739
3754
  } else {
@@ -4155,7 +4170,7 @@ class Dom {
4155
4170
  const anchorNode = selection.anchorNode;
4156
4171
  if (!anchorNode) return;
4157
4172
  const container = anchorNode.nodeType !== Node.TEXT_NODE && anchorNode.nodeType !== Node.COMMENT_NODE ? anchorNode : anchorNode.parentElement;
4158
- const sameSelection = container && container.innerText === selection.toString();
4173
+ const sameSelection = container && container.innerText === selection.toString().trim();
4159
4174
  if (sameSelection || selection.toString().trim() === '') {
4160
4175
  this.cleanElement(container);
4161
4176
  } else {
@@ -66285,7 +66300,7 @@ class Rte {
66285
66300
  } else if (num === '+' || num === '-') {
66286
66301
  for (let i = 0; i < Object.keys(classes).length; i++) {
66287
66302
  let className = Object.values(classes)[i];
66288
- if (dom.hasClass(container, className)) {
66303
+ if (container.closest('.' + className) && !container.classList.contains('is-builder')) {
66289
66304
  if (num === '+') {
66290
66305
  if (i + 1 === Object.keys(classes).length) return;
66291
66306
  newClassName = Object.values(classes)[i + 1];
@@ -76867,8 +76882,10 @@ class ContentBuilder {
76867
76882
  // ON PASTE
76868
76883
  // col.addEventListener('paste', this.handleCellPaste.bind(this));
76869
76884
  col.addEventListener('paste', e => {
76870
- const clipboardData = (e.clipboardData || window.clipboardData).getData('text');
76871
- this.handleCellPaste(clipboardData);
76885
+ e.preventDefault();
76886
+ const clipboardDataText = (e.clipboardData || window.clipboardData).getData('text');
76887
+ const clipboardDataHtml = (e.clipboardData || window.clipboardData).getData('text/html');
76888
+ this.handleCellPaste(clipboardDataText, clipboardDataHtml);
76872
76889
  });
76873
76890
  col.setAttribute('data-click', true);
76874
76891
  }
@@ -79046,6 +79063,7 @@ class ContentBuilder {
79046
79063
  // CMD-B
79047
79064
 
79048
79065
  if (this.opts.useCssClasses) {
79066
+ this.uo.saveForUndo();
79049
79067
  this.dom.execCommandToggle('fontWeight', '', this.opts.cssClasses);
79050
79068
  this.opts.onChange();
79051
79069
  e.preventDefault();
@@ -79056,6 +79074,7 @@ class ContentBuilder {
79056
79074
  // CMD-I
79057
79075
 
79058
79076
  if (this.opts.useCssClasses) {
79077
+ this.uo.saveForUndo();
79059
79078
  this.dom.execCommandToggle('fontStyle', '', this.opts.cssClasses);
79060
79079
  this.opts.onChange();
79061
79080
  e.preventDefault();
@@ -79066,6 +79085,7 @@ class ContentBuilder {
79066
79085
  // CMD-U
79067
79086
 
79068
79087
  if (this.opts.useCssClasses) {
79088
+ this.uo.saveForUndo();
79069
79089
  this.dom.execCommandToggle('textUnderline', '', this.opts.cssClasses);
79070
79090
  this.opts.onChange();
79071
79091
  e.preventDefault();
@@ -79076,6 +79096,7 @@ class ContentBuilder {
79076
79096
  // CMD-S
79077
79097
 
79078
79098
  if (this.opts.useCssClasses) {
79099
+ this.uo.saveForUndo();
79079
79100
  this.dom.execCommandToggle('textLinethrough', '', this.opts.cssClasses);
79080
79101
  this.opts.onChange();
79081
79102
  e.preventDefault();
@@ -79083,6 +79104,8 @@ class ContentBuilder {
79083
79104
  }
79084
79105
  }
79085
79106
  if (isCmd && e.which === 221) {
79107
+ this.uo.saveForUndo();
79108
+
79086
79109
  // CMD-]
79087
79110
  e.preventDefault();
79088
79111
  document.execCommand('indent', false, null);
@@ -79091,6 +79114,8 @@ class ContentBuilder {
79091
79114
  markSpan();
79092
79115
  }
79093
79116
  if (isCmd && e.which === 219) {
79117
+ this.uo.saveForUndo();
79118
+
79094
79119
  // CMD-]
79095
79120
  e.preventDefault();
79096
79121
  document.execCommand('outdent', false, null);
@@ -79322,307 +79347,326 @@ class ContentBuilder {
79322
79347
  this.opts.onChange();
79323
79348
  }, 2000);
79324
79349
  }
79325
- handleCellPaste(clipboardData) {
79350
+ handleCellPaste(clipboardData, clipboardDataHtml) {
79326
79351
  this.uo.saveForUndo();
79327
79352
  const util = this.util;
79328
79353
  util.saveSelection(); //required. Without this, CTRL-A (select element) & CTRL-V won't replace the element, but will paste at the end of the element.
79329
79354
 
79330
- let contentword = this.doc.querySelector('#idContentWord');
79331
- if (contentword) contentword.parentNode.removeChild(contentword);
79332
- var el;
79333
- var curr;
79334
- if (this.win.getSelection) {
79335
- curr = this.win.getSelection().getRangeAt(0).commonAncestorContainer;
79336
- if (curr.nodeType === 3) {
79337
- //ini text node
79338
- el = curr.parentNode;
79339
- } else {
79340
- el = curr;
79341
- }
79342
- } else if (this.doc.selection) {
79343
- curr = this.doc.selection.createRange();
79344
- el = this.doc.selection.createRange().parentElement();
79345
- }
79346
- var tmptop = el.getBoundingClientRect().top + this.win.pageYOffset;
79347
- const html = '<div style="position:absolute;z-index:-1000;top:' + tmptop + 'px;left:-1000px;width:100px;height:100px;overflow:auto;" name="idContentWord" id="idContentWord" contenteditable="true"></div>';
79348
- if (!this.iframe) {
79349
- this.dom.appendHtml(this.builderStuff, html);
79350
- } else {
79351
- this.dom.appendHtml(this.contentStuff, html);
79352
- }
79353
- contentword = this.doc.querySelector('#idContentWord');
79354
- contentword.focus();
79355
- setTimeout(() => {
79356
- try {
79357
- var sPastedText = '';
79358
- let contentword = this.doc.querySelector('#idContentWord');
79359
-
79360
- //Check video embed
79361
- var bPasteObject = false;
79362
- var src = contentword.innerText;
79363
- if (!this.opts.disableAutoEmbedVideo) {
79364
- //var youRegex = /^http[s]?:\/\/(((www.youtube.com\/watch\?(feature=player_detailpage&)?)v=)|(youtu.be\/))([^#\&\?]*)/;
79365
- var youRegex = /^http[s]?:\/\/(((www.youtube.com\/watch\?(feature=player_detailpage&)?)v=)|(youtu.be\/))([^#&?]*)/;
79366
- var vimeoRegex = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/)|(video\/))?([0-9]+)\/?/;
79367
- var youRegexMatches = youRegex.exec(src);
79368
- var vimeoRegexMatches = vimeoRegex.exec(src);
79369
- if (youRegexMatches !== null || vimeoRegexMatches !== null) {
79370
- if (youRegexMatches !== null && youRegexMatches.length >= 7) {
79371
- var youMatch = youRegexMatches[6];
79372
- src = 'https://www.youtube.com/embed/' + youMatch + '?rel=0';
79373
- }
79374
- if (vimeoRegexMatches !== null && vimeoRegexMatches.length >= 7) {
79375
- var vimeoMatch = vimeoRegexMatches[6];
79376
- src = 'https://player.vimeo.com/video/' + vimeoMatch;
79377
- }
79378
- sPastedText = '<div class="embed-responsive embed-responsive-16by9"><iframe tabindex="0" width="560" height="315" src="' + src + '" frameborder="0" allowfullscreen=""></iframe></div>';
79379
- bPasteObject = true;
79355
+ // Create a temporary div to hold the pasted HTML
79356
+ let contentword = document.createElement('div');
79357
+ contentword.innerHTML = clipboardDataHtml;
79358
+ try {
79359
+ var sPastedText = '';
79360
+
79361
+ //Check video embed
79362
+ var bPasteObject = false;
79363
+ var src = contentword.innerText;
79364
+ if (!this.opts.disableAutoEmbedVideo) {
79365
+ //var youRegex = /^http[s]?:\/\/(((www.youtube.com\/watch\?(feature=player_detailpage&)?)v=)|(youtu.be\/))([^#\&\?]*)/;
79366
+ var youRegex = /^http[s]?:\/\/(((www.youtube.com\/watch\?(feature=player_detailpage&)?)v=)|(youtu.be\/))([^#&?]*)/;
79367
+ var vimeoRegex = /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/)|(video\/))?([0-9]+)\/?/;
79368
+ var youRegexMatches = youRegex.exec(src);
79369
+ var vimeoRegexMatches = vimeoRegex.exec(src);
79370
+ if (youRegexMatches !== null || vimeoRegexMatches !== null) {
79371
+ if (youRegexMatches !== null && youRegexMatches.length >= 7) {
79372
+ var youMatch = youRegexMatches[6];
79373
+ src = 'https://www.youtube.com/embed/' + youMatch + '?rel=0';
79374
+ }
79375
+ if (vimeoRegexMatches !== null && vimeoRegexMatches.length >= 7) {
79376
+ var vimeoMatch = vimeoRegexMatches[6];
79377
+ src = 'https://player.vimeo.com/video/' + vimeoMatch;
79380
79378
  }
79379
+ sPastedText = '<div class="embed-responsive embed-responsive-16by9"><iframe tabindex="0" width="560" height="315" src="' + src + '" frameborder="0" allowfullscreen=""></iframe></div>';
79380
+ bPasteObject = true;
79381
79381
  }
79382
- if (!bPasteObject) {
79383
- if (this.opts.paste === 'text') {
79384
- /*
79385
- let elms = contentword.querySelectorAll('p,h1,h2,h3,h4,h5,h6');
79386
- Array.prototype.forEach.call(elms, (elm) => {
79387
- elm.innerHTML = elm.innerHTML + ' '; //add space (&nbsp;)
79388
- });
79389
- // sPastedText = contentword.innerText;
79390
- sPastedText = contentword.innerHTML;
79391
- sPastedText = sPastedText.replace(/(<([^>]+)>)/ig,'<br>');
79392
- sPastedText = sPastedText.replace(/(<br\s*\/?>){3,}/gi, '<br>');
79393
- if(sPastedText.indexOf('<br>')===0) {
79394
- sPastedText = sPastedText.substring(4);
79395
- }
79396
- if(sPastedText.substring(sPastedText.length-4)==='<br>'){
79397
- sPastedText = sPastedText.substring(0, sPastedText.length-4);
79398
- }
79399
- sPastedText = sPastedText.trim();
79400
- */
79382
+ }
79383
+ if (!bPasteObject) {
79384
+ if (this.opts.paste === 'text') {
79385
+ /*
79386
+ let elms = contentword.querySelectorAll('p,h1,h2,h3,h4,h5,h6');
79387
+ Array.prototype.forEach.call(elms, (elm) => {
79388
+ elm.innerHTML = elm.innerHTML + ' '; //add space (&nbsp;)
79389
+ });
79390
+ // sPastedText = contentword.innerText;
79391
+ sPastedText = contentword.innerHTML;
79392
+ sPastedText = sPastedText.replace(/(<([^>]+)>)/ig,'<br>');
79393
+ sPastedText = sPastedText.replace(/(<br\s*\/?>){3,}/gi, '<br>');
79394
+ if(sPastedText.indexOf('<br>')===0) {
79395
+ sPastedText = sPastedText.substring(4);
79396
+ }
79397
+ if(sPastedText.substring(sPastedText.length-4)==='<br>'){
79398
+ sPastedText = sPastedText.substring(0, sPastedText.length-4);
79399
+ }
79400
+ sPastedText = sPastedText.trim();
79401
+ */
79401
79402
 
79402
- sPastedText = clipboardData;
79403
- // sPastedText = sPastedText.replace(/(?:\r\n|\r|\n)/g, '<br>');
79403
+ sPastedText = clipboardData;
79404
+ // sPastedText = sPastedText.replace(/(?:\r\n|\r|\n)/g, '<br>');
79405
+ } else {
79406
+ sPastedText = contentword.innerHTML;
79407
+ if (this.opts.paste === 'html') {
79408
+ //with styles
79409
+ sPastedText = util.cleanHTML(sPastedText, false);
79404
79410
  } else {
79405
- sPastedText = contentword.innerHTML;
79406
- if (this.opts.paste === 'html') {
79407
- //with styles
79408
- sPastedText = util.cleanHTML(sPastedText, false);
79409
- } else {
79410
- //html-without-styles (default)
79411
- sPastedText = util.cleanHTML(sPastedText, true);
79412
- }
79413
- contentword.innerHTML = sPastedText;
79414
-
79415
- /*
79416
- // remove attributes
79417
- if(this.opts.paste === 'html'){//with styles
79418
- let elms = contentword.querySelectorAll('*');
79419
- Array.prototype.forEach.call(elms, (elm) => {
79420
- for(let n = 0;n<elm.attributes.length;n++) {
79421
- if(elm.attributes[n].name!=='style') elm.removeAttribute(elm.attributes[n].name);
79422
- }
79423
- });
79424
- } else { //html-without-styles (default)
79425
-
79426
- const removeAttributes = (element) => {
79427
- while (element.attributes.length > 0) {
79428
- element.removeAttribute(element.attributes[0].name);
79429
- }
79430
- };
79431
- let elms = contentword.querySelectorAll('*');
79432
- Array.prototype.forEach.call(elms, (elm) => {
79433
- removeAttributes(elm);
79434
- });
79435
- }
79436
- */
79437
-
79438
- /*
79439
- Additional Cleanup:
79440
- - Remove p inside li
79441
- - Remove li with white-space: pre
79442
- */
79443
- let elms = contentword.querySelectorAll('li');
79444
- Array.prototype.forEach.call(elms, elm => {
79445
- elm.style.whiteSpace = '';
79446
- const childNodes = elm.childNodes;
79447
- let i = childNodes.length;
79448
- while (i--) {
79449
- if (childNodes[i].tagName === 'P') {
79450
- childNodes[i].outerHTML = childNodes[i].innerHTML;
79451
- }
79452
- }
79453
- });
79454
-
79455
- // NOTE: paste <h1><p> jadi nempel
79456
-
79457
- // // Source: https://gist.github.com/sbrin/6801034
79458
- // jQuery('p', $editor).each(function(){
79459
- // var str = jQuery(this).attr('style');
79460
- // var matches = /mso-list:\w+ \w+([0-9]+)/.exec(str);
79461
- // if (matches) {
79462
- // jQuery(this).data('_listLevel', parseInt(matches[1], 10));
79463
- // }
79464
- // });
79465
- // var last_level=0;
79466
- // var pnt = null;
79467
- // jQuery('p', $editor).each(function(){
79468
- // var cur_level = jQuery(this).data('_listLevel');
79469
- // if(cur_level !== undefined){
79470
- // var txt = jQuery(this).text();
79471
- // var list_tag = '<ul></ul>';
79472
- // if (/^\s*\w+\./.test(txt)) {
79473
- // var matches = /([0-9])\./.exec(txt);
79474
- // if (matches) {
79475
- // var start = parseInt(matches[1], 10);
79476
- // list_tag = start>1 ? '<ol start="' + start + '"></ol>' : '<ol></ol>';
79477
- // }else{
79478
- // list_tag = '<ol></ol>';
79479
- // }
79480
- // }
79481
-
79482
- // if(cur_level>last_level){
79483
- // if(last_level===0){
79484
- // jQuery(this).before(list_tag);
79485
- // pnt = jQuery(this).prev();
79486
- // }else{
79487
- // pnt = jQuery(list_tag).appendTo(pnt);
79488
- // }
79489
- // }
79490
- // if(cur_level<last_level){
79491
- // for(var i=0; i<last_level-cur_level; i++){
79492
- // pnt = pnt.parent();
79493
- // }
79494
- // }
79495
- // jQuery('span:first', this).remove();
79496
- // pnt.append('<li>' + jQuery(this).html() + '</li>');
79497
- // jQuery(this).remove();
79498
- // last_level = cur_level;
79499
- // }else{
79500
- // last_level = 0;
79501
- // }
79502
- // });
79503
- // //jQuery('[style]', $editor).removeAttr('style'); //done (see cleanHTML)
79504
- // jQuery('[align]', $editor).removeAttr('align');
79505
- // //jQuery('span', $editor).replaceWith(function() {return jQuery(this).contents();}); //done (see cleanHTML)
79506
- // jQuery('span:empty', $editor).remove();
79507
- // //jQuery("[class^='Mso']", $editor).removeAttr('class'); //done (see cleanHTML)
79508
- // jQuery('p:empty', $editor).remove();
79509
-
79510
- sPastedText = contentword.innerHTML;
79411
+ //html-without-styles (default)
79412
+ sPastedText = util.cleanHTML(sPastedText, true);
79511
79413
  }
79512
- }
79513
- contentword = this.doc.querySelector('#idContentWord');
79514
- if (contentword) contentword.parentNode.removeChild(contentword);
79515
- util.restoreSelection();
79516
- var oSel = this.win.getSelection();
79517
- var range = oSel.getRangeAt(0);
79518
- range.extractContents();
79519
- range.collapse(true);
79520
- var docFrag = range.createContextualFragment(sPastedText);
79521
- var lastNode = docFrag.lastChild;
79522
- range.insertNode(docFrag);
79523
- if (this.activeCol) {
79414
+ contentword.innerHTML = sPastedText;
79415
+
79524
79416
  /*
79525
- Additional Cleanup:
79526
- - Remove empty elements (empty p, etc)
79417
+ // remove attributes
79418
+ if(this.opts.paste === 'html'){//with styles
79419
+ let elms = contentword.querySelectorAll('*');
79420
+ Array.prototype.forEach.call(elms, (elm) => {
79421
+ for(let n = 0;n<elm.attributes.length;n++) {
79422
+ if(elm.attributes[n].name!=='style') elm.removeAttribute(elm.attributes[n].name);
79423
+ }
79424
+ });
79425
+ } else { //html-without-styles (default)
79426
+
79427
+ const removeAttributes = (element) => {
79428
+ while (element.attributes.length > 0) {
79429
+ element.removeAttribute(element.attributes[0].name);
79430
+ }
79431
+ };
79432
+ let elms = contentword.querySelectorAll('*');
79433
+ Array.prototype.forEach.call(elms, (elm) => {
79434
+ removeAttributes(elm);
79435
+ });
79436
+ }
79527
79437
  */
79528
- // this.activeCol.find('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').remove();
79529
- // this.activeCol.querySelectorAll('*:empty').forEach((x)=>{x.remove();}); // Makes <img> removed
79530
- this.activeCol.querySelectorAll('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').forEach(x => {
79531
- x.remove();
79532
- });
79533
79438
 
79534
79439
  /*
79535
79440
  Additional Cleanup:
79536
- Fix HTML structure. The problem:
79537
- <p class="elm-active">
79538
- ...Sometimes h1, h2, p can be pasted here..
79539
- </p>
79441
+ - Remove p inside li
79442
+ - Remove li with white-space: pre
79540
79443
  */
79541
- let elmActive = this.activeCol.querySelector('p.elm-active,h1.elm-active,h2.elm-active,h3.elm-active,h4.elm-active,h5.elm-active,h6.elm-active');
79542
- if (elmActive) {
79543
- let elms = elmActive.querySelectorAll('p,h1,h2,h3,h4,h5,h6');
79544
- if (elms.length > 0) {
79545
- let elmClosestElement = elmActive.nextElementSibling;
79546
-
79547
- //Fix text that doesn't have paragraph
79548
- let textNodes = Array.from(elmActive.childNodes).filter(node => node.nodeType === 3 && node.textContent.trim().length > 1);
79549
- textNodes.forEach(node => {
79550
- const span = document.createElement('p');
79551
- node.after(span);
79552
- span.appendChild(node);
79553
- });
79554
- if (elmActive.firstElementChild && elmActive.childNodes.length === 1) {
79555
- if (elmActive.firstElementChild.tagName === 'SPAN') {
79556
- // Paste HTML with styles
79444
+ let elms = contentword.querySelectorAll('li');
79445
+ Array.prototype.forEach.call(elms, elm => {
79446
+ elm.style.whiteSpace = '';
79447
+ const childNodes = elm.childNodes;
79448
+ let i = childNodes.length;
79449
+ while (i--) {
79450
+ if (childNodes[i].tagName === 'P') {
79451
+ childNodes[i].outerHTML = childNodes[i].innerHTML;
79452
+ }
79453
+ }
79454
+ });
79557
79455
 
79558
- elmActive.outerHTML = elmActive.firstElementChild.innerHTML; //fix
79456
+ // NOTE: paste <h1><p> jadi nempel
79559
79457
 
79560
- // Re-clean empty elements
79561
- this.activeCol.querySelectorAll('*:empty').forEach(x => {
79562
- x.remove();
79563
- });
79458
+ // // Source: https://gist.github.com/sbrin/6801034
79459
+ // jQuery('p', $editor).each(function(){
79460
+ // var str = jQuery(this).attr('style');
79461
+ // var matches = /mso-list:\w+ \w+([0-9]+)/.exec(str);
79462
+ // if (matches) {
79463
+ // jQuery(this).data('_listLevel', parseInt(matches[1], 10));
79464
+ // }
79465
+ // });
79466
+ // var last_level=0;
79467
+ // var pnt = null;
79468
+ // jQuery('p', $editor).each(function(){
79469
+ // var cur_level = jQuery(this).data('_listLevel');
79470
+ // if(cur_level !== undefined){
79471
+ // var txt = jQuery(this).text();
79472
+ // var list_tag = '<ul></ul>';
79473
+ // if (/^\s*\w+\./.test(txt)) {
79474
+ // var matches = /([0-9])\./.exec(txt);
79475
+ // if (matches) {
79476
+ // var start = parseInt(matches[1], 10);
79477
+ // list_tag = start>1 ? '<ol start="' + start + '"></ol>' : '<ol></ol>';
79478
+ // }else{
79479
+ // list_tag = '<ol></ol>';
79480
+ // }
79481
+ // }
79482
+
79483
+ // if(cur_level>last_level){
79484
+ // if(last_level===0){
79485
+ // jQuery(this).before(list_tag);
79486
+ // pnt = jQuery(this).prev();
79487
+ // }else{
79488
+ // pnt = jQuery(list_tag).appendTo(pnt);
79489
+ // }
79490
+ // }
79491
+ // if(cur_level<last_level){
79492
+ // for(var i=0; i<last_level-cur_level; i++){
79493
+ // pnt = pnt.parent();
79494
+ // }
79495
+ // }
79496
+ // jQuery('span:first', this).remove();
79497
+ // pnt.append('<li>' + jQuery(this).html() + '</li>');
79498
+ // jQuery(this).remove();
79499
+ // last_level = cur_level;
79500
+ // }else{
79501
+ // last_level = 0;
79502
+ // }
79503
+ // });
79504
+ // //jQuery('[style]', $editor).removeAttr('style'); //done (see cleanHTML)
79505
+ // jQuery('[align]', $editor).removeAttr('align');
79506
+ // //jQuery('span', $editor).replaceWith(function() {return jQuery(this).contents();}); //done (see cleanHTML)
79507
+ // jQuery('span:empty', $editor).remove();
79508
+ // //jQuery("[class^='Mso']", $editor).removeAttr('class'); //done (see cleanHTML)
79509
+ // jQuery('p:empty', $editor).remove();
79564
79510
 
79565
- //place cursor
79566
- if (elmClosestElement) this.dom.moveCursorToElement(elmClosestElement.previousElementSibling);else this.dom.moveCursorToElement(this.activeCol);
79567
- let builderActive = this.doc.querySelector('.builder-active');
79568
- if (builderActive) this.applyBehaviorOn(builderActive);
79511
+ sPastedText = contentword.innerHTML;
79512
+ }
79513
+ }
79514
+ contentword = this.doc.querySelector('#idContentWord');
79515
+ if (contentword) contentword.parentNode.removeChild(contentword);
79516
+ util.restoreSelection();
79569
79517
 
79570
- //Trigger Change event
79571
- this.opts.onChange();
79518
+ /*
79519
+ var oSel = this.win.getSelection();
79520
+ var range = oSel.getRangeAt(0);
79521
+ range.extractContents();
79522
+ range.collapse(true);
79523
+ var docFrag = range.createContextualFragment(sPastedText);
79524
+ var lastNode = docFrag.lastChild;
79525
+ range.insertNode(docFrag);
79526
+ */
79572
79527
 
79573
- //Trigger Render event
79574
- this.opts.onRender();
79575
- return;
79576
- }
79577
- }
79528
+ /*
79529
+ When selection is made by double clicking text (to select the entire block),
79530
+ the actual selection goes to the next block as well (default behavior, tested using a simple contentEditable div).
79531
+ To fix this, re-select the contents inside.
79532
+ */
79533
+
79534
+ const blocks = this.dom.getSelectedBlocks();
79535
+ const selection = this.win.getSelection();
79536
+
79537
+ /*
79538
+ Check same selection for the block first (this is more accurate than the sameSelection checking below).
79539
+ In case of double click (to select the entire block), for example, on this element:
79540
+ <h2 class="font-light size-54"><span class="size-24">Heading</span> 2 here</h2>
79541
+ The sameSelection checking below is failed, since the container = this.getElm() will return not the entire h2, but only
79542
+ <span class="size-24">Heading</span>
79543
+ So, blocks checking below is made to fix it.
79544
+ */
79545
+ let blockSelection = false;
79546
+ if (blocks.length === 1) {
79547
+ if (blocks[0].innerText.trim() === selection.toString().trim()) {
79548
+ let range = document.createRange();
79549
+ range.selectNodeContents(blocks[0]);
79550
+ selection.removeAllRanges();
79551
+ selection.addRange(range);
79552
+ blockSelection = true;
79553
+ }
79554
+ }
79555
+ if (!blockSelection) {
79556
+ const container = this.dom.getElm();
79557
+ const sameSelection = container && container.innerText === selection.toString().trim();
79558
+ if (sameSelection || selection.toString().trim() === '') {
79559
+ let range = document.createRange();
79560
+ range.selectNodeContents(container);
79561
+ selection.removeAllRanges();
79562
+ selection.addRange(range);
79563
+ }
79564
+ }
79565
+ document.execCommand('insertHTML', false, sPastedText);
79566
+ if (this.activeCol) {
79567
+ /*
79568
+ Additional Cleanup:
79569
+ - Remove empty elements (empty p, etc)
79570
+ */
79571
+ // this.activeCol.find('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').remove();
79572
+ // this.activeCol.querySelectorAll('*:empty').forEach((x)=>{x.remove();}); // Makes <img> removed
79573
+ this.activeCol.querySelectorAll('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').forEach(x => {
79574
+ x.remove();
79575
+ });
79578
79576
 
79579
- // Paste HTML without styles
79580
- elmActive.outerHTML = elmActive.innerHTML; //fix
79577
+ /*
79578
+ Additional Cleanup:
79579
+ Fix HTML structure. The problem:
79580
+ <p class="elm-active">
79581
+ ...Sometimes h1, h2, p can be pasted here..
79582
+ </p>
79583
+ */
79584
+ let elmActive = this.activeCol.querySelector('p.elm-active,h1.elm-active,h2.elm-active,h3.elm-active,h4.elm-active,h5.elm-active,h6.elm-active');
79585
+ if (elmActive) {
79586
+ let elms = elmActive.querySelectorAll('p,h1,h2,h3,h4,h5,h6');
79587
+ if (elms.length > 0) {
79588
+ let elmClosestElement = elmActive.nextElementSibling;
79589
+
79590
+ //Fix text that doesn't have paragraph
79591
+ let textNodes = Array.from(elmActive.childNodes).filter(node => node.nodeType === 3 && node.textContent.trim().length > 1);
79592
+ textNodes.forEach(node => {
79593
+ const span = document.createElement('p');
79594
+ node.after(span);
79595
+ span.appendChild(node);
79596
+ });
79597
+ if (elmActive.firstElementChild && elmActive.childNodes.length === 1) {
79598
+ if (elmActive.firstElementChild.tagName === 'SPAN') {
79599
+ // Paste HTML with styles
79581
79600
 
79582
- // Re-clean empty elements
79583
- // this.activeCol.querySelectorAll('*:empty').forEach((x)=>{x.remove();}); // Makes <img> removed
79584
- this.activeCol.find('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').remove();
79601
+ elmActive.outerHTML = elmActive.firstElementChild.innerHTML; //fix
79585
79602
 
79586
- //place cursor
79587
- if (elmClosestElement) this.dom.moveCursorToElement(elmClosestElement.previousElementSibling);else this.dom.moveCursorToElement(this.activeCol);
79588
- let builderActive = this.doc.querySelector('.builder-active');
79589
- if (builderActive) this.applyBehaviorOn(builderActive);
79603
+ // Re-clean empty elements
79604
+ this.activeCol.querySelectorAll('*:empty').forEach(x => {
79605
+ x.remove();
79606
+ });
79590
79607
 
79591
- //Trigger Change event
79592
- this.opts.onChange();
79608
+ //place cursor
79609
+ if (elmClosestElement) this.dom.moveCursorToElement(elmClosestElement.previousElementSibling);else this.dom.moveCursorToElement(this.activeCol);
79610
+ let builderActive = this.doc.querySelector('.builder-active');
79611
+ if (builderActive) this.applyBehaviorOn(builderActive);
79593
79612
 
79594
- //Trigger Render event
79595
- this.opts.onRender();
79596
- return;
79613
+ //Trigger Change event
79614
+ this.opts.onChange();
79615
+
79616
+ //Trigger Render event
79617
+ this.opts.onRender();
79618
+ return;
79619
+ }
79597
79620
  }
79621
+
79622
+ // Paste HTML without styles
79623
+ elmActive.outerHTML = elmActive.innerHTML; //fix
79624
+
79625
+ // Re-clean empty elements
79626
+ // this.activeCol.querySelectorAll('*:empty').forEach((x)=>{x.remove();}); // Makes <img> removed
79627
+ this.activeCol.find('h1:empty,h2:empty,h3:empty,h4:empty,h5:empty,h6:empty,p:empty').remove();
79628
+
79629
+ //place cursor
79630
+ if (elmClosestElement) this.dom.moveCursorToElement(elmClosestElement.previousElementSibling);else this.dom.moveCursorToElement(this.activeCol);
79631
+ let builderActive = this.doc.querySelector('.builder-active');
79632
+ if (builderActive) this.applyBehaviorOn(builderActive);
79633
+
79634
+ //Trigger Change event
79635
+ this.opts.onChange();
79636
+
79637
+ //Trigger Render event
79638
+ this.opts.onRender();
79639
+ return;
79598
79640
  }
79599
79641
  }
79600
- range.setStartAfter(lastNode);
79601
- range.setEndAfter(lastNode);
79602
- range.collapse(false);
79603
- var comCon = range.commonAncestorContainer;
79604
- if (comCon && comCon.parentNode) {
79605
- try {
79606
- comCon.parentNode.normalize();
79607
- } catch (e) {
79608
- // Do Nothing
79642
+ }
79643
+
79644
+ /*
79645
+ range.setStartAfter(lastNode);
79646
+ range.setEndAfter(lastNode);
79647
+ range.collapse(false);
79648
+ var comCon = range.commonAncestorContainer;
79649
+ if (comCon && comCon.parentNode) {
79650
+ try { comCon.parentNode.normalize(); } catch (e) {
79651
+ // Do Nothing
79609
79652
  }
79610
- }
79611
- oSel.removeAllRanges();
79612
- oSel.addRange(range);
79613
- let builderActive = this.doc.querySelector('.builder-active');
79614
- if (builderActive) this.applyBehaviorOn(builderActive);
79653
+ }
79654
+ oSel.removeAllRanges();
79655
+ oSel.addRange(range);
79656
+ */
79615
79657
 
79616
- //Trigger Change event
79617
- this.opts.onChange();
79658
+ let builderActive = this.doc.querySelector('.builder-active');
79659
+ if (builderActive) this.applyBehaviorOn(builderActive);
79618
79660
 
79619
- //Trigger Render event
79620
- this.opts.onRender();
79621
- } catch (e) {
79622
- let contentword = this.doc.querySelector('#idContentWord');
79623
- if (contentword) contentword.parentNode.removeChild(contentword);
79624
- }
79625
- }, 800);
79661
+ //Trigger Change event
79662
+ this.opts.onChange();
79663
+
79664
+ //Trigger Render event
79665
+ this.opts.onRender();
79666
+ } catch (e) {
79667
+
79668
+ // Do Nothing
79669
+ }
79626
79670
  }
79627
79671
  cellSelected() {
79628
79672
  const util = this.util;