@innovastudio/contentbuilder 1.5.47 → 1.5.49

Sign up to get free protection for your applications and to get access to all the features.
package/index.d.ts CHANGED
@@ -1,28 +1,233 @@
1
1
  interface ContentBuilderOptions {
2
+ page?: string;
2
3
  container?: string;
4
+ row?: string;
5
+ cols?: any[];
6
+ colequal?: any[];
7
+ colsizes?: any[];
8
+ imageQuality?: number;
9
+ elementSelection?: boolean;
10
+ paste?: string;
11
+ snippetJSON?: object;
12
+ screenMode?: string;
13
+ onPreviewOpen?: () => void;
14
+ previewURL?: string;
15
+ onPreviewClose?: () => void;
16
+ previewStyle?: string;
17
+ livePreviewOpen?: boolean;
18
+ livePreviewAlwaysReload?: boolean;
19
+ livePreviewReloadEvery?: number;
20
+ scriptPath?: string;
21
+ plugins?: any[];
22
+ pluginPath?: string;
23
+ disableConfig?: boolean;
24
+ modulePath?: string;
25
+ assetPath?: string;
26
+ fontAssetPath?: string;
27
+ fontPath?: string;
3
28
  snippetModal?: boolean;
4
29
  snippetModalLeft?: boolean;
30
+ snippetData?: string;
31
+ snippetUrl?: string;
32
+ snippetPath?: string;
33
+ snippetPathReplace?: any[];
34
+ snippetCategories?: any[];
35
+ defaultSnippetCategory?: number;
36
+ snippetHandle?: boolean;
37
+ sidePanel?: string;
38
+ snippetList?: string;
39
+ onRender?: () => void;
40
+ onContentClick?: () => void;
41
+ onChange?: () => void;
42
+ onAdd?: () => void;
43
+ largerImageHandler?: string;
44
+ onLargerImageUpload?: () => void;
45
+ imageHandler?: string;
46
+ onImageUpload?: () => void;
47
+ fileHandler?: string;
48
+ onFileUpload?: () => void;
49
+ mediaHandler?: string;
50
+ onMediaUpload?: () => void;
51
+ videoHandler?: string;
52
+ onVideoUpload?: () => void;
53
+ audioHandler?: string;
54
+ onAudioUpload?: () => void;
55
+ colors?: any[];
56
+ builderMode?: string;
57
+ rowTool?: string;
58
+ rowcolOutline?: boolean;
59
+ columnTool?: boolean;
60
+ outlineMode?: string;
61
+ toolStyle?: string;
62
+ outlineStyle?: string;
63
+ snippetAddTool?: boolean;
64
+ elementTool?: boolean;
65
+ elementHighlight?: boolean;
66
+ columnHtmlEditor?: boolean;
67
+ rowHtmlEditor?: boolean;
68
+ htmlSyntaxHighlighting?: boolean;
69
+ snippetOpen?: boolean;
70
+ toolbar?: string;
71
+ toolbarDisplay?: string;
72
+ shortenHTML?: boolean;
73
+ imageResizeOnBlock?: boolean;
74
+ simpleEditingBreakpoint?: string;
75
+ resizeHeight?: boolean;
76
+ snippetsSidebarDisplay?: string;
77
+ snippetDisplay?: string;
78
+ onImageSelectClick?: () => void;
79
+ onFileSelectClick?: () => void;
80
+ onVideoSelectClick?: () => void;
81
+ onAudioSelectClick?: () => void;
82
+ onMediaSelectClick?: () => void;
83
+ onPluginsLoaded?: () => void;
84
+ onImageBrowseClick?: () => void;
85
+ onImageSettingClick?: () => void;
86
+ onImageEditClick?: () => void;
87
+ setCropperConfig?: () => void;
88
+ imageEmbed?: boolean;
89
+ imageselect?: string;
90
+ fileselect?: string;
91
+ videoselect?: string;
92
+ imageSelect?: string;
93
+ fileSelect?: string;
94
+ videoSelect?: string;
95
+ audioSelect?: string;
96
+ mediaSelect?: string;
97
+ selectIcon?: string;
98
+ otherSelect?: string;
99
+ otherSelectCaption?: string;
100
+ otherSelectIcon?: string;
101
+ imageSelectWidth?: string;
102
+ imageSelectHeight?: string;
103
+ fileSelectWidth?: string;
104
+ fileSelectHeight?: string;
105
+ videoSelectWidth?: string;
106
+ videoSelectHeight?: string;
107
+ audioSelectWidth?: string;
108
+ audioSelectHeight?: string;
109
+ mediaSelectWidth?: string;
110
+ mediaSelectHeight?: string;
111
+ otherSelectWidth?: string;
112
+ otherSelectHeight?: string;
113
+ imageSelectMaxWidth?: string;
114
+ fileSelectMaxWidth?: string;
115
+ videoSelectMaxWidth?: string;
116
+ audioSelectMaxWidth?: string;
117
+ mediaSelectMaxWidth?: string;
118
+ otherSelectMaxWidth?: string;
119
+ assetPanelFullScreen?: boolean;
120
+ codeEditorWidth?: string;
121
+ codeEditorHeight?: string;
122
+ codeEditorMaxWidth?: string;
123
+ blockCodeEditorWidth?: string;
124
+ blockCodeEditorHeight?: string;
125
+ blockCodeEditorMaxWidth?: string;
126
+ assetRefresh?: boolean;
127
+ customTags?: any[];
128
+ buttons?: any[];
129
+ buttonsMore?: any[];
130
+ elementButtons?: any[];
131
+ elementButtonsMore?: any[];
132
+ iconButtons?: any[];
133
+ iconButtonsMore?: any[];
134
+ lang?: any[];
135
+ checkLang?: boolean;
136
+ clearPreferences?: boolean;
137
+ toolbarAddSnippetButton?: boolean;
138
+ animateModal?: boolean;
139
+ defaultFontSizes?: any[];
140
+ fontSizeClassValues?: any[];
141
+ gradientcolors?: any[];
142
+ elementEditor?: boolean;
143
+ customval?: string;
144
+ moduleConfig?: any[];
145
+ elementAnimate?: boolean;
146
+ cleanAOS?: boolean;
147
+ framework?: string;
148
+ cellFormat?: string;
149
+ rowFormat?: string;
150
+ emailMode?: boolean;
151
+ absolutePath?: boolean;
152
+ emailSnippetCategories?: any[];
153
+ defaultEmailSnippetCategory?: number;
154
+ undoRedoStyles?: boolean;
155
+ specialElementClasses?: any[];
156
+ onUndo?: () => void;
157
+ onRedo?: () => void;
158
+ onBlockCanvasAdd?: () => void;
159
+ docContainer?: string;
160
+ blockContainer?: string;
161
+ pageSize?: string;
162
+ pageSizes?: any[];
163
+ maxEmbedImageWidth?: number;
164
+ zoom?: number;
165
+ useLightbox?: boolean;
166
+ lightboxArrow?: boolean;
167
+ imageRenameOnEdit?: boolean;
168
+ disableAutoEmbedVideo?: boolean;
169
+ deleteConfirm?: boolean;
170
+ disableBootstrapIcons?: boolean;
171
+ sectionTemplate?: string;
172
+ onZoomStart?: () => void;
173
+ onZoom?: () => void;
174
+ onZoomEnd?: () => void;
175
+ themes?: any[];
176
+ colHeight?: any[];
177
+ maxColumns?: number;
178
+ leadingPreset?: any[];
179
+ cssClasses?: object;
180
+ useCssClasses?: boolean;
181
+ useButtonPlugin?: boolean;
182
+ enableDragResize?: boolean;
183
+ simpleTextSettings?: boolean;
184
+ enableColumnsPerLine?: boolean;
185
+ startAIAssistant?: boolean;
186
+ isContentBox?: boolean;
187
+ sendCommandUrl?: string;
188
+ speechTranscribeUrl?: string;
189
+ onlineDemo?: boolean;
190
+ autoSendDelay?: number;
191
+ autoEditBlock?: boolean;
192
+ disclaimerAI?: string;
193
+ showDisclaimer?: boolean;
194
+ AIModalStyle?: string;
195
+ enableShortCommands?: boolean;
196
+ speechRecognitionLang?: string;
197
+ assistantMode?: string;
198
+ triggerWords?: object;
199
+ temperature?: number;
200
+ topP?: number;
201
+ useMediaRecorder?: boolean;
202
+ encoderPath?: string;
203
+ imageAutoUpscale?: boolean;
204
+ headlineList?: any[];
205
+ mediaPath?: string;
206
+ media?: object;
207
+ shortCommandList?: object;
208
+ similarityThreshold?: number;
209
+ commandList?: object;
5
210
  }
6
211
 
7
212
  declare class ContentBuilder {
8
213
  constructor(options?: ContentBuilderOptions);
9
214
 
10
- html(element: HTMLElement): void;
11
- loadSnippets(snippetFile: string, snippetOpen: boolean): void;
215
+ html(element?: HTMLElement): void;
216
+ loadSnippets(snippetFile: string, snippetOpen?: boolean): void;
12
217
  viewSnippets(): void;
13
- saveImages(handler: string, onComplete: () => void, onBase64Upload: () => void): void;
218
+ saveImages(handler?: string, onComplete?: () => void, onBase64Upload?: () => void): void;
14
219
  applyBehavior(): void;
15
220
  applyBehaviorOn(element: HTMLElement): void;
16
221
  viewConfig(): void;
17
- setUIColor(mode: string, csslink: string): void;
222
+ setUIColor(mode: string, csslink?: string): void;
18
223
  viewHtml(): void;
19
- loadHtml(html: string, area: HTMLElement): void;
20
- pasteHtmlAtCaret(html: string, selectPastedContent: boolean): void;
21
- addSnippetl(html: string, bSnippet: boolean, noedit: boolean): void;
224
+ loadHtml(html: string, area?: HTMLElement): void;
225
+ pasteHtmlAtCaret(html: string, selectPastedContent?: boolean): void;
226
+ addSnippet(html: string, bSnippet?: boolean, noedit?: boolean): void;
22
227
  undo(): void;
23
228
  redo(): void;
24
229
  destroy(): void;
25
- returnUrl(mode: string): void;
230
+ returnUrl(s: string): void;
26
231
  toggleSnippetModal(): void;
27
232
  openSnippetModal(): void;
28
233
  closeSnippetModal(): void;
@@ -30,10 +235,10 @@ declare class ContentBuilder {
30
235
  openAIAssistant(): void;
31
236
  closeAIAssistant(): void;
32
237
  saveForUndo(): void;
33
- selectAsset(s: string, f: boolean)
34
- addBlock(html: string, blockContainer: HTMLElement)
35
- addPage(box: HTMLElement)
36
- setPageSize(s: string)
238
+ selectAsset(s: string, f?: boolean)
239
+ addBlock(html: string, blockContainer?: HTMLElement)
240
+ addPage(box?: HTMLElement)
241
+ setPageSize(s?: string)
37
242
  openPageOptions(): void;
38
243
  print(): void;
39
244
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@innovastudio/contentbuilder",
3
3
  "type": "module",
4
- "version": "1.5.47",
4
+ "version": "1.5.49",
5
5
  "description": "",
6
6
  "main": "public/contentbuilder/contentbuilder.esm.js",
7
7
  "types": "index.d.ts",
@@ -3711,6 +3711,24 @@ class Dom {
3711
3711
  constructor(builder) {
3712
3712
  this.builder = builder;
3713
3713
  }
3714
+ detectMobileOrTablet() {
3715
+ const userAgent = navigator.userAgent.toLowerCase();
3716
+ const isIOS = /ipad|iphone|ipod/.test(userAgent) && !window.MSStream;
3717
+ const isAndroid = /android/.test(userAgent);
3718
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
3719
+ const screenWidth = window.innerWidth <= 1600;
3720
+ if (isIOS || isAndroid) {
3721
+ return true; // Definitely a mobile or tablet
3722
+ }
3723
+
3724
+ // Additional check for other touch devices with screen width under 1600px
3725
+ if (isTouchDevice && screenWidth) {
3726
+ return true; // Likely a mobile or tablet
3727
+ }
3728
+
3729
+ return false; // Likely not a mobile or tablet
3730
+ }
3731
+
3714
3732
  getScale(container) {
3715
3733
  let matrix = window.getComputedStyle(container).transform;
3716
3734
  if (matrix === 'none') return 1;
@@ -17223,7 +17241,19 @@ const renderSnippetPanel = (builder, snippetOpen) => {
17223
17241
 
17224
17242
  let activeBuilderArea;
17225
17243
  let itemHeight;
17226
- new Sortable(snippetlist, {
17244
+ const isMobile = dom.detectMobileOrTablet();
17245
+ let useClick = false;
17246
+ if (isMobile && builder.isContentBox) {
17247
+ const items = snippetlist.querySelectorAll('.snippet-item');
17248
+ items.forEach(item => {
17249
+ item.addEventListener('click', () => {
17250
+ const snippetid = item.getAttribute('data-id');
17251
+ builder.dropSnippet(snippetid);
17252
+ });
17253
+ });
17254
+ useClick = true;
17255
+ }
17256
+ if (!useClick) new Sortable(snippetlist, {
17227
17257
  // forceFallback: safariAgent,
17228
17258
  group: {
17229
17259
  name: 'shared',
@@ -66679,6 +66709,7 @@ class Rte {
66679
66709
  btnFront.forEach(btn => {
66680
66710
  btn.addEventListener('click', () => {
66681
66711
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66712
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66682
66713
  if (!activeBlock) return;
66683
66714
  this.builder.forward(activeBlock);
66684
66715
  });
@@ -66687,6 +66718,7 @@ class Rte {
66687
66718
  btnBackward.forEach(btn => {
66688
66719
  btn.addEventListener('click', () => {
66689
66720
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66721
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66690
66722
  if (!activeBlock) return;
66691
66723
  if (activeBlock.style.zIndex === '0') {
66692
66724
  this.builder.moveUp(activeBlock);
@@ -66714,6 +66746,7 @@ class Rte {
66714
66746
  btnDuplicate.forEach(btn => {
66715
66747
  btn.addEventListener('click', () => {
66716
66748
  let activeBlock = this.builder.doc.querySelector('.is-block.cloned');
66749
+ if (!activeBlock) activeBlock = this.builder.doc.querySelector('.is-block.active');
66717
66750
  if (!activeBlock) return;
66718
66751
  this.builder.duplicate(activeBlock);
66719
66752
  });
@@ -83995,7 +84028,8 @@ class EditableBlocks {
83995
84028
  }
83996
84029
  if (block.classList.contains('is-group')) return; // do not clone if block is shape
83997
84030
 
83998
- if (!block.classList.contains('clone')) {
84031
+ const isMobileOrTablet = this.detectMobileOrTablet();
84032
+ if (!block.classList.contains('clone') & !isMobileOrTablet) {
83999
84033
  let clonedDiv = block.cloneNode(true);
84000
84034
  clonedDiv.classList.add('clone');
84001
84035
  block.parentNode.appendChild(clonedDiv);
@@ -84003,6 +84037,24 @@ class EditableBlocks {
84003
84037
  this.refresh();
84004
84038
  }
84005
84039
  }
84040
+ detectMobileOrTablet() {
84041
+ const userAgent = navigator.userAgent.toLowerCase();
84042
+ const isIOS = /ipad|iphone|ipod/.test(userAgent) && !window.MSStream;
84043
+ const isAndroid = /android/.test(userAgent);
84044
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
84045
+ const screenWidth = window.innerWidth <= 1600;
84046
+ if (isIOS || isAndroid) {
84047
+ return true; // Definitely a mobile or tablet
84048
+ }
84049
+
84050
+ // Additional check for other touch devices with screen width under 1600px
84051
+ if (isTouchDevice && screenWidth) {
84052
+ return true; // Likely a mobile or tablet
84053
+ }
84054
+
84055
+ return false; // Likely not a mobile or tablet
84056
+ }
84057
+
84006
84058
  selectClear() {
84007
84059
  this.doc.querySelectorAll('.clone').forEach(elm => elm.parentNode.removeChild(elm));
84008
84060
  this.doc.querySelectorAll('.cloned').forEach(elm => elm.classList.remove('cloned'));
@@ -92316,6 +92368,203 @@ Add an image for each feature.`, 'Create a new content showcasing a photo galler
92316
92368
  // return currentScript.replace(currentScriptFile, '');
92317
92369
  }
92318
92370
 
92371
+ dropSnippet(snippetid) {
92372
+ // snippetJSON is snippet's JSON (from assets/minimalist-blocks/content.js) that store all snippets' html
92373
+ const result = this.opts.snippetJSON.snippets.filter(item => {
92374
+ if (item.id + '' === snippetid) return item;else return false;
92375
+ });
92376
+ let html = result[0].html;
92377
+ let noedit = result[0].noedit;
92378
+ let bSnippet;
92379
+ if (html.indexOf('"row') === -1) {
92380
+ bSnippet = true; // Just snippet (without row/column grid)
92381
+ } else {
92382
+ bSnippet = false; // Snippet is wrapped in row/colum
92383
+ }
92384
+
92385
+ if (this.opts.emailMode) bSnippet = false;
92386
+
92387
+ // check if is block
92388
+ let isBlock = false;
92389
+ if (html.includes('"is-block')) {
92390
+ isBlock = true;
92391
+ bSnippet = false;
92392
+ }
92393
+
92394
+ // Convert snippet into your defined 12 columns grid
92395
+ let rowClass = this.opts.row; //row
92396
+ let colClass = this.opts.cols; //['col s1', 'col s2', 'col s3', 'col s4', 'col s5', 'col s6', 'col s7', 'col s8', 'col s9', 'col s10', 'col s11', 'col s12']
92397
+ if (rowClass !== '' && colClass.length === 12) {
92398
+ // html = html.replace(new RegExp('row clearfix', 'g'), rowClass);
92399
+ html = html.replace(new RegExp('row clearfix', 'g'), 'row'); // backward
92400
+ html = html.replace(new RegExp('"row', 'g'), '"' + rowClass);
92401
+ html = html.replace(new RegExp('column full', 'g'), colClass[11]);
92402
+ html = html.replace(new RegExp('column half', 'g'), colClass[5]);
92403
+ html = html.replace(new RegExp('column third', 'g'), colClass[3]);
92404
+ html = html.replace(new RegExp('column fourth', 'g'), colClass[2]);
92405
+ html = html.replace(new RegExp('column fifth', 'g'), colClass[1]);
92406
+ html = html.replace(new RegExp('column sixth', 'g'), colClass[1]);
92407
+ html = html.replace(new RegExp('column two-third', 'g'), colClass[7]);
92408
+ html = html.replace(new RegExp('column two-fourth', 'g'), colClass[8]);
92409
+ html = html.replace(new RegExp('column two-fifth', 'g'), colClass[9]);
92410
+ html = html.replace(new RegExp('column two-sixth', 'g'), colClass[9]);
92411
+ }
92412
+ html = html.replace(/{id}/g, this.util.makeId());
92413
+ if (this.opts.onAdd) {
92414
+ html = this.opts.onAdd(html);
92415
+ }
92416
+ if (this.opts.snippetPathReplace.length > 0) {
92417
+ // try {
92418
+ if (this.opts.snippetPathReplace[0] !== '') {
92419
+ let regex = new RegExp(this.opts.snippetPathReplace[0], 'g');
92420
+ html = html.replace(regex, this.opts.snippetPathReplace[1]);
92421
+ let string1 = this.opts.snippetPathReplace[0].replace(/\//g, '%2F');
92422
+ let string2 = this.opts.snippetPathReplace[1].replace(/\//g, '%2F');
92423
+ let regex2 = new RegExp(string1, 'g');
92424
+ html = html.replace(regex2, string2);
92425
+ }
92426
+ // } catch (e) { 1; }
92427
+ }
92428
+
92429
+ // this.addSnippet(html, bSnippet, noedit);
92430
+
92431
+ if (bSnippet) {
92432
+ // Just snippet (without row/column grid), ex. buttons, line, social, video, map.
92433
+ // Can be inserted after current row, column (cell), element, or last row.
92434
+
92435
+ html = `<div class="${this.opts.row}"><div class="${this.opts.cols[this.opts.cols.length - 1]}"${noedit ? ' data-noedit' : ''}>${html}</div></div>`;
92436
+ } else if (isBlock) ; else {
92437
+ // Snippet is wrapped in row/colum (may contain custom code or has [data-html] attribute)
92438
+ // Can only be inserted after current row or last row (not on column or element).
92439
+
92440
+ let snippet = this.dom.createElement('div');
92441
+ snippet.innerHTML = html;
92442
+ let blocks = snippet.querySelectorAll('[data-html]');
92443
+ Array.prototype.forEach.call(blocks, block => {
92444
+ // Render custom code block
92445
+ html = decodeURIComponent(block.getAttribute('data-html'));
92446
+ html = html.replace(/{id}/g, this.util.makeId());
92447
+ html = html.replace(/<script>/g, `${this.nonce ? `<script nonce="${this.nonce}">` : '<script>'}`);
92448
+ for (var i = 1; i <= 20; i++) {
92449
+ html = html.replace('[%HTML' + i + '%]', block.getAttribute('data-html-' + i) === undefined ? '' : decodeURIComponent(block.getAttribute('data-html-' + i))); //render editable area
92450
+ }
92451
+
92452
+ block.innerHTML = html;
92453
+ });
92454
+ html = snippet.innerHTML;
92455
+ }
92456
+ const activeBox = this.doc.querySelector('.box-select');
92457
+ const activeRow = this.doc.querySelector('.row-active');
92458
+ if (activeRow) {
92459
+ this.addRow(html, activeBox);
92460
+ } else {
92461
+ if (activeBox) {
92462
+ if (activeBox.classList.contains('box-canvas')) {
92463
+ // Canvas Mode
92464
+
92465
+ let snippet = this.dom.createElement('div');
92466
+ snippet.innerHTML = html;
92467
+ let blocks = snippet.querySelectorAll('[data-html]');
92468
+ Array.prototype.forEach.call(blocks, block => {
92469
+ // Render custom code block
92470
+ html = decodeURIComponent(block.getAttribute('data-html'));
92471
+ html = html.replace(/{id}/g, this.util.makeId());
92472
+ html = html.replace(/<script>/g, `${this.nonce ? `<script nonce="${this.nonce}">` : '<script>'}`);
92473
+ for (var i = 1; i <= 20; i++) {
92474
+ html = html.replace('[%HTML' + i + '%]', block.getAttribute('data-html-' + i) === undefined ? '' : decodeURIComponent(block.getAttribute('data-html-' + i))); //render editable area
92475
+ }
92476
+
92477
+ block.innerHTML = html;
92478
+ });
92479
+ html = snippet.innerHTML;
92480
+ const blockTemplate = `
92481
+ <div class="is-block block-steady height-auto" data-new-dummy="1" style="top: 20%; left: 20%; width: 760px;">
92482
+ <div class="is-container container-new size-18 leading-14">
92483
+ [%CONTENT%]
92484
+ </div>
92485
+ </div>
92486
+ `; // data-new-dummy will be used by onSort to apply top/left position (snippetpanel.js)
92487
+ // html = blockTemplate.replace('[%CONTENT%]', html);
92488
+
92489
+ this.uo.saveForUndo();
92490
+ this.eb.addBlock(blockTemplate, activeBox);
92491
+ const builders = activeBox.querySelectorAll('.is-container.container-new');
92492
+ builders.forEach(builder => {
92493
+ // After snippet has been added, re-apply behavior on builder areas
92494
+
92495
+ var range = document.createRange();
92496
+ range.setStart(builder, 0);
92497
+ builder.appendChild(range.createContextualFragment(html));
92498
+ this.applyBehaviorOn(builder);
92499
+ builder.classList.remove('container-new');
92500
+ });
92501
+ this.opts.onChange();
92502
+ this.opts.onRender();
92503
+ if (this.opts.onBlockCanvasAdd) this.opts.onBlockCanvasAdd();
92504
+ } else {
92505
+ let container = activeBox.querySelector('.builder-active');
92506
+ if (container) {
92507
+ this.addRow(html, activeBox);
92508
+ } else {
92509
+ container = activeBox.querySelector('.is-container');
92510
+ if (container) {
92511
+ this.addRow(html, activeBox);
92512
+ }
92513
+ }
92514
+ }
92515
+ }
92516
+ }
92517
+ this.activeCol = null;
92518
+ }
92519
+ addRow(html, box) {
92520
+ this.uo.saveForUndo();
92521
+ let rowElement;
92522
+ let bAddLast = false;
92523
+
92524
+ // Add after selected row
92525
+ let cell = this.activeCol;
92526
+ let row;
92527
+ if (cell) {
92528
+ row = cell.parentNode; // in email mode, cell active is also under row active (incorrect, but cell active is not needed in email mode. So this line works!)
92529
+ } else {
92530
+ // If no active cell, check if it is from .row-add-initial (empty info)
92531
+ row = this.doc.querySelector('.row-active');
92532
+ if (!row) {
92533
+ bAddLast = true;
92534
+ }
92535
+ }
92536
+ // Add after last row
92537
+ if (bAddLast) {
92538
+ const container = box.querySelector('.is-builder');
92539
+ const rows = this.dom.elementChildren(container);
92540
+ const lastrow = rows[rows.length - 1];
92541
+ row = lastrow;
92542
+ }
92543
+
92544
+ // Use createContextualFragment() to make embedded script executable
92545
+ let range = document.createRange();
92546
+ row.parentNode.insertBefore(range.createContextualFragment(html), row.nextSibling);
92547
+ // rowElement = snippet.childNodes[0];
92548
+
92549
+ rowElement = row.nextElementSibling; // a must. Must be before applyBehavior to prevent element delete during fixLayout
92550
+
92551
+ // checkEmpty & onRender called here
92552
+ let builderActive = box.querySelector('.builder-active');
92553
+ if (builderActive) this.applyBehaviorOn(builderActive);else {
92554
+ builderActive = box.querySelector('.is-builder');
92555
+ this.applyBehaviorOn(builderActive);
92556
+ }
92557
+ let cellElement = rowElement.querySelector('div');
92558
+ if (cellElement) cellElement.click(); //change active block to the newly created
92559
+
92560
+ // Change to row selection
92561
+ rowElement.className = rowElement.className.replace('row-outline', '');
92562
+
92563
+ //Hide Column tool (new!)
92564
+ this.util.hideColumnTool();
92565
+ this.opts.onChange();
92566
+ this.opts.onRender();
92567
+ }
92319
92568
  sectionDropSetup() {
92320
92569
  if (this.blockContainer) {
92321
92570
  this.sortableOnCanvas = [];