PluginProbe
Smart Grid-Layout Design for Contact Form 7 / 4.14
Smart Grid-Layout Design for Contact Form 7 v4.14
4.18.0 4.17.0 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 4.0.0 4.0.1 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10 4.11 4.12 4.13 4.14 All 119 releases
cf7-grid-layout / admin / js / cf7-grid-codemirror.js

cf7-grid-codemirror.js in Smart Grid-Layout Design for Contact Form 7 4.14, at admin/js/cf7-grid-codemirror.js

793 lines 34.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function( $, cme, jscme, csscme ) {
2 'use strict';
3 /** @since 4.2.0 manage editor loading errors */
4 const $codemirror = $('#cf7-codemirror'),
5 $formEditor = $('#contact-form-editor'),
6 $wpcf7Editor = $('textarea#wpcf7-form-hidden'),
7 $editorTabs = $('#form-editor-tabs'),
8 $optionals= $('#optional-editors'),
9 $topTags =$('#top-tags').addClass('click-disabled'),
10 $bottomTags =$('#bottom-tags').addClass('click-disabled');
11
12 let eh = $formEditor.height(), ew =$formEditor.width();
13 $('.loading-screen',$formEditor).css('padding','250px 5px '+(eh-100)+'px 5px');
14 $('#publish').prop('disabled', true);
15
16 window.onerror= function(msg, source, lineno, colno, error){
17 let $loadScreen = $('.loading-screen',$formEditor);
18 if($loadScreen.is(':visible')){
19 $loadScreen.html('<div class="js-error"><p><span class="message">'+cf7sgeditor.jserror+'</span><br/><span class="error">'+msg+'</span><br/>(file: '+source+', line: <strong>'+lineno+'</strong><br/>'+error+')</p></div>');
20
21 if($codemirror.children('.CodeMirror')){
22 let load = window.confirm(cf7sgeditor.fixhtmlform);
23 if(load){
24 $loadScreen.hide();
25 $editorTabs.children('ul').hide();
26 $optionals.hide();
27 $topTags.hide();
28 $bottomTags.hide();
29 $('#publish').prop('disabled', false);
30 }
31 }
32 }
33 }
34
35 CodeMirror.defineMode("shortcode", function(config, parserConfig) {
36 let scOverlay = {
37 token: function(stream, state) {
38 let ch;
39 if (stream.match(/^\[([a-zA-Z0-9_]+)\*?\s?/)) {
40 while ((ch = stream.next()) != null){
41 if (ch == "]" ) {
42 //stream.eat("]");
43 return "shortcode";
44 }
45 }
46 }else if(stream.match(/data-([a-z0-9_]+)/)){
47 while ((ch = stream.next()) != null){
48 if (ch == '=' ) {
49 //stream.eat("]");
50 return "cf7sg-attr";
51 }
52 }
53 }
54 while (stream.next() != null && !stream.match(/^\[([a-zA-Z0-9_]+)\*?\s?/, false) && !stream.match(/data-([a-z0-9_]+)/,false) ) {}
55 return null;
56 }
57 };
58 return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "htmlmixed"), scOverlay);
59 });
60
61 if(cf7sgeditor.mode.length>0) cme.setOption('mode',cf7sgeditor.mode);
62 if(cf7sgeditor.theme.user.length>0) cme.setOption('theme',cf7sgeditor.theme.user);
63 /** @since 4.4.4 enable tag matching in text editor, set after addon script is loaded */
64 cme.setOption('matchTags', {bothTags: true});
65 /** @since 4.0.0 manage user theme pref */
66 let $themeRadio = $('.codemirror-theme', $codemirror);
67 if($(':input:checked', $themeRadio).length>0){
68 $themeRadio.on('change',':input',function(e){
69 cme.setOption("theme",cf7sgeditor.theme[e.target.value]);
70 });
71 }else $(':input',$themeRadio).prop('disabled',true);
72
73 $(document).ready( function(){
74 let codemirrorUpdated = false, formFields={},
75 $grid = $('#grid-form'),
76 $jsTags = $('#js-tags'),
77 gridTab = '#cf7-editor-grid', //default at load time.
78 $jsCodemirror = $('#cf7-js-codemirror'),
79 $cssCodemirror = $('#cf7-css-codemirror'),
80 $jstext = $('textarea#cf7-form-js'),
81 $csstext = $('textarea#cf7-form-css'),
82 jscmUpdated = false, jsInsertAtLine = false,
83 csscmUpdated = false, cssInsertAtLine = false,
84 $jsThemeRadio = $('.codemirror-theme', $jsCodemirror),
85 $cssThemeRadio = $('.codemirror-theme', $cssCodemirror),
86 $cf7Tabs = $('#contact-form-editor-tabs');
87 /** @since 4.11 update the hidden cf7 textarea when the cf7 tabs are achanged.*/
88 $cf7Tabs.on('mousedown', 'li',function(e){
89 let active = e.delegateTarget.querySelector('.ui-state-active');
90 if( active && 'form-panel-tab' == active.id){
91 $wpcf7Editor.text(cme.getValue());
92 }
93 })
94
95 const $cf7key = $('#post_name');
96 $.fn.beautify = function(cursor){
97 let cm = cme, $this=$(this);
98 if($this.is('#cf7-js-codemirror')) cm = jscme;
99 else if($this.is('#cf7-css-codemirror')) cm = csscme;
100 cm.setSelection({
101 'line':cm.firstLine(),
102 'ch':0,
103 'sticky':null
104 },{
105 'line':cm.lastLine(),
106 'ch':0,
107 'sticky':null
108 },
109 {scroll: false});
110 cm.indentSelection("smart");
111 cm.setCursor(cm.firstLine(),0,{scroll: false});
112 if('undefined' != typeof cursor && cursor.find(false)){
113 const from = cursor.from(), to = cursor.to();
114 cm.setSelection(CodeMirror.Pos(from.line, 0), to);
115 cm.scrollIntoView({from: from, to: CodeMirror.Pos(to.line + 10, 0)});
116 }
117 }
118 //set codemirror editor value;
119 cme.setValue($wpcf7Editor.text());
120 cme.setSize("100%");
121 // cme.setOption('viewportMargin',Infinity);
122
123
124 $wpcf7Editor.on('grid-ready', function(){ //------ setup the codemirror editor
125 //codemirror editor
126 $codemirror.beautify();
127
128 cme.on('changes', function(){
129 codemirrorUpdated = true;
130 const disabled = $editorTabs.tabs('option','disabled');
131
132 if(true===disabled){
133 const changes = $('<div>').append(cme.getValue());
134 if(0===changes.children().length || changes.children('.container').length>0){
135 $editorTabs.tabs('option',{disabled:false});
136 /**
137 * @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms.
138 */
139 $('#is-cf7sg-form').val('true');
140 }
141 }
142
143 $formEditor.trigger('cf7sg-form-change');
144 });
145 //toogle body scroll off/on
146 function disableBodyScroll(){
147 let $post = $('form#post');
148 $post.css('width',$post.width()+'px');
149 $('body').addClass('disable-scroll')
150 }
151 function enableBodyScroll(){
152 $('form#post').css('width','auto');
153 $('body').removeClass('disable-scroll')
154 }
155 //create tabs
156 $editorTabs.tabs({
157 beforeActivate: function (event, ui){
158 enableBodyScroll();
159 //update the codemirror panel
160 let panel = '';
161 if(ui.newPanel instanceof jQuery) panel = '#'+ui.newPanel.attr('id');
162 else if('undefined' != typeof ui.newPanel.selector) panel = ui.newPanel.selector;
163 else console.log('CF7 GRID EDITOR: unknown tab ui panel object');
164
165 if('#cf7-codemirror' == panel){
166 //finalise any changes in the grid form editor
167 $grid.on('cf7grid-form-ready', function(){
168 let code = $grid.CF7FormHTML();
169 if($grid.children('.container').length > 0){ //beautify.
170 code = html_beautify(code ,
171 {
172 'indent_size': 2,
173 'wrap_line_length': 0
174 });
175 }
176 cme.setValue(code);
177 // cme.refresh();
178 //reset the codemirror change flag
179 codemirrorUpdated = false;
180 //remove id from textarea
181 $('textarea#wpcf7-form').attr('id', '');
182 //change the wpcf7 textarea
183 $('textarea.codemirror-cf7-update', $codemirror).attr('id', 'wpcf7-form');
184 //setup the form code in the hidden textarea
185 $wpcf7Editor.html(code);
186 });
187 /** @since 2.8.3 clear the codemirror textarea##wpcf7-form */
188 $('textarea.codemirror-cf7-update', $codemirror).val('');
189 $grid.trigger('cf7grid-form-finalise');
190 }else{
191 //remove id from textarea
192 $('textarea#wpcf7-form').attr('id', '');
193 }
194 },
195 activate: function( event, ui ) {
196 if(ui.newPanel instanceof jQuery) gridTab = '#'+ui.newPanel.attr('id');
197 else if('undefined' != typeof ui.newPanel.selector) gridTab = ui.newPanel.selector;
198 else console.log('CF7 GRID EDITOR: unknown tab ui panel object');
199
200 $topTags.show().addClass('click-disabled');
201 $bottomTags.show().addClass('click-disabled');
202 $jsTags.hide();
203 $optionals.hide();
204 $(window).scrollTop($('#form-panel').offset().top);
205
206 switch(gridTab){
207 case '#cf7-editor-grid': //grid editor.
208 $optionals.show();
209 if(codemirrorUpdated){
210 //update the hidden textarea
211 $wpcf7Editor.text(cme.getValue());
212 //trigger rebuild grid event
213 $grid.trigger('build-grid');
214 }else{ //try to set the focus on the 'cf7sgfocus element'
215 const $focus = $('.cf7sgfocus', $grid);
216 if($focus.length>0){
217 const scrollPos = $focus.offset().top - $(window).height()/2 + $focus.height()/2;
218 //console.log(scrollPos);
219 $(window).scrollTop(scrollPos);
220 $focus.removeClass('cf7sgfocus');
221 }
222 }
223 break;
224 case '#cf7-codemirror': //HTML editor.
225 const cursor = cme.getSearchCursor('cf7sgfocus', CodeMirror.Pos(cme.firstLine(), 0), {caseFold: true, multiline: true});
226 $topTags.removeClass('click-disabled');
227 $bottomTags.removeClass('click-disabled');
228 $codemirror.beautify(cursor);
229 cme.refresh();
230 break;
231 case '#cf7-js-codemirror': //js editor.
232 $topTags.hide();
233 $bottomTags.hide();
234 $jsTags.show();
235 let $form = $('<div>').html($grid.CF7FormHTML());
236 $('.display-none', $jsTags).removeClass('show-events');
237 if( $('div.container.cf7-sg-table', $form).length > 0){
238 $('#table-events', $jsTags).addClass('show-events');
239 }
240 if( $('.container.cf7sg-collapsible', $form).not('.cf7sg-slider-section > .cf7sg-collapsible').length > 0){
241 $('#collapsible-events', $jsTags).addClass('show-events');
242 }
243 if( $('div.container.cf7-sg-tabs-panel', $form).length > 0){
244 $('#tab-events', $jsTags).addClass('show-events');
245 }
246 if( $('.cf7sg-slider-section', $form).length > 0){
247 $('#slider-events', $jsTags).addClass('show-events');
248 }
249
250 //scan all fields.
251 let cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s\]]*)[\s\[]*(.[^\[]*\"source:([^\s]*)\"[\s^\[]*|[.^\[]*(?!\"source:)[^\[]*)\]/img;
252 //reset form fields.
253 formFields={};
254 $('.field', $form).each(function(){
255 let $field = $(this), search = $field.text(), match = cf7TagRegexp.exec(search);
256 while(null != match && match.length>2){
257 switch(match[1].replace('*','')){
258 case 'recaptcha':
259 case 'recaptch':
260 case 'acceptance':
261 case 'submit':
262 case 'save':
263 match=null;
264 break;//tags with no fields of interest.
265 default:
266 formFields[match[2]] = [match[2],match[0]];
267 if($field.is('.cf7-sg-tabs .field')) formFields[match[2]][0] += '_tab';
268 if($field.is('.container.cf7-sg-table .field')) formFields[match[2]][0] += '_row';
269 if(match[0].indexOf('class:select2')>0){
270 $('#field-events li.select2', $jsTags).addClass('show-events');
271 }
272 if(match[1]==='dynamic_checkbox'){
273 $('#field-events li.hybriddd', $jsTags).addClass('show-events');
274 }
275 match = cf7TagRegexp.exec(search); //search next.
276 break;
277 }
278 }
279 });
280 break;
281 case '#cf7-css-codemirror': //css editor.
282 $topTags.hide();
283 $bottomTags.hide();
284 break;
285 }
286 },
287 create: function(e){
288 //console.info("Created tab");
289 $('.ui-tabs-panel:not(#cf7-editor-grid)',$editorTabs).hover(disableBodyScroll, enableBodyScroll);
290 $('.loading-screen',$formEditor).hide();
291 $('#publish').prop('disabled', false);
292 }
293 });
294 /** @since 4.0 js cm editor */
295 $.fn.createNewCMEditor = function(activate=false){
296 let $this = $(this);
297 $this.each(function(activate){
298 let $this = $(this), ref, $theme, $text, cm, theme, $cm, mode,regex;
299 if(!$this.is('a.button.cf7sg-cmtab')) return $this;
300
301 ref = '#'+$this.next('div.display-none').attr('id');
302 $this.attr('href',ref);
303 switch(ref){
304 case '#cf7-js-codemirror':
305 $theme = $jsThemeRadio;
306 $text = $jstext;
307 cm = jscme;
308 theme = cf7sgeditor.jstheme;
309 $cm = $jsCodemirror;
310 mode={name: "javascript", json: true};
311 $this.text('JS');
312 //enable helper popups.
313 // regex = /(?<=\/\*)(.*?)(?=\s?\*\/)/im;
314 regex = /^.*(\/\*)(.*?)(?=\s?\*\/)/im;
315 $('a.helper', $jsTags).each(function(){
316 let $helper = $(this), text = regex.exec($helper.data('cf72post'));
317 if('undefined' != typeof text[2]){
318 $helper.parent().append('<span style="position:relative"><span class="display-none">'+text[2]+'</span></span>');
319 }
320 });
321 break;
322 case '#cf7-css-codemirror':
323 $theme = $cssThemeRadio;
324 $text = $csstext;
325 cm = csscme;
326 theme = cf7sgeditor.csstheme;
327 $cm = $cssCodemirror;
328 mode = "css";
329 $this.text('CSS');
330 break;
331 }
332 $theme.append('<span class="file">'+$text.data('file')+'</span>');
333 //convert to css editor.
334 $editorTabs.append($this.next('div.display-none').remove());
335 $text.data('form', $cf7key.val());
336 $this = $this.remove().wrap('<li></li>');
337 $editorTabs.children('ul').append($this.closest('li'));
338 //set codemirror editor value;
339 cm.setValue($text.text());
340 cm.setOption("mode", mode);
341 cm.setSize("100%");
342 $cm.on('cf7sg-screen-resize',function(){cm.refresh()});
343 $cm.hover(disableBodyScroll, enableBodyScroll);
344 $cm.removeClass('display-none').beautify();
345
346 if(theme.user.length>0) cm.setOption('theme',theme.user);
347 if($(':input:checked', $theme).length>0){
348 $theme.on('change',':input',function(e){
349 cm.setOption("theme",theme[e.target.value]);
350 });
351 }else $(':input',$theme).prop('disabled',true);
352 $editorTabs.tabs('refresh');
353 // if(activate) $this.delay(300).click();
354 });
355 return $this;
356 }
357 //add requried cm editors.
358 $('a.cf7sg-cmtab.required', $optionals).createNewCMEditor();
359 //else listen for user requirement.
360 $optionals.click('a.cf7sg-cmtab',function(e){ $(e.target).createNewCMEditor(true) });
361
362 jscme.on('changes', function(e, changes){
363 jsInsertAtLine = false;
364 let last = jscme.getLine(changes[changes.length-1].to.line);
365 if(isEmpty(last) || ""==last.trim()) jsInsertAtLine = true;
366
367 jscmUpdated = false;
368 if(jscme.getValue().length>0){
369 jscmUpdated = true;
370 $jstext.attr('data-form', $cf7key.val()); //set current form slug.
371 }
372 });
373 csscme.on('changes', function(e, changes){
374 cssInsertAtLine = false;
375 let last = csscme.getLine(changes[changes.length-1].to.line);
376 if(isEmpty(last) || ""==last.trim()) cssInsertAtLine = true;
377
378 csscmUpdated = false;
379 if(csscme.getValue().length>0){
380 csscmUpdated = true;
381 $csstext.attr('data-form', $cf7key.val()); //set current form slug.
382 }
383 });
384 $cf7key.on('change',function(e){
385 let oldkey = $jstext.data('form'),
386 newkey = $(this).val(), filepath;
387 if( oldkey.length > 0 && oldkey != $cf7key.val() ){
388 /* update editor js */
389 jscme.setValue( jscme.getValue().replace('#'+oldkey, '#'+$cf7key.val()) );
390 $jstext.attr('data-form', newkey);
391 jscmUpdated = true;
392 }
393 //update the file name.
394 oldkey = $('.file',$jsThemeRadio).text();
395 oldkey = oldkey.substring(oldkey.indexOf('js/')+3, oldkey.indexOf('.js'));
396 $('.file',$jsThemeRadio).text( $jstext.data('file').replace(oldkey, newkey) );
397 filepath = $jstext.data('file');
398 filepath = filepath.substring(filepath.indexOf('>>')+3);
399 $('.prev-file:input', $jsThemeRadio).val(filepath);
400 //css cm update.
401 $('.file',$cssThemeRadio).text( $csstext.data('file').replace(oldkey, newkey) );
402 filepath = $csstext.data('file');
403 filepath = filepath.substring(filepath.indexOf('>>')+3);
404 $('.prev-file:input', $cssThemeRadio).val(filepath);
405
406 oldkey = $csstext.data('form');
407 if( oldkey.length > 0 && oldkey != $cf7key.val() ){
408 /* update editor js */
409 csscme.setValue( csscme.getValue().replace('#'+oldkey, '#'+$cf7key.val()) );
410 $csstext.data('form', newkey);
411 csscmUpdated = true;
412 }
413 });
414 $('#js-tags').on('click','a.helper',function(e){
415 let $this = $(this),
416 helper = $this.data('cf72post').replace('{$cf7_key}', $cf7key.val() ),
417 enableArrayFields = false,
418 line = jscme.getCursor().line;
419
420 if(!$('#cf7sg-jstags-comments').is(':checked')){
421 helper = helper.replace(/^\n?\s*?\/\/.*\n?/gmi,'');
422 }
423 if($this.is('.all-fields')){
424 let fieldsText = '\n';
425 $.each(formFields, function(field, ftag){
426 fieldsText += " case '"+field+"': //"+field+" updated";
427 switch(ftag[0]){
428 case field+'_tab':
429 fieldsText +=", tIdx is tab index.\n";
430 enableArrayFields=true;
431 break;
432 case field+'_row':
433 fieldsText +=", rIdx is row index.\n";
434 enableArrayFields=true;
435 break;
436 case field+'_tab_row':
437 fieldsText +=", tIdx is tab index / rIdx is row index.\n";
438 enableArrayFields=true;
439 break;
440 default:
441 fieldsText +=".\n //do something\n";
442 break;
443 }
444 fieldsText +=" break;\n"
445 });
446 let arrayFields = '';
447 if(enableArrayFields){
448 arrayFields = "//-----code to extract field name and tab/row index -----------\n";
449 arrayFields += "let search='', tIdx=0, rIdx=0;\n";
450 arrayFields += "if( $field.is('.cf7sgtab-field') || $field.is('.cf7sgrow-field') ){\n";
451 arrayFields += " $.each($field.attr('class').split(/\\s+/), function(idx, clss){\n";
452 arrayFields += " if(0==clss.indexOf('cf7sg-')){\n";
453 arrayFields += " clss = clss.replace('cf7sg-','');\n";
454 arrayFields += " search = new RegExp( '(?<='+clss+')(_tab-(\\\\d))?(_row-(\\\\d))?','gi' ).exec(fieldName);\n";
455 arrayFields += " switch(true){\n";
456 arrayFields += " case /\\d+/.test(search[2]*search[4]): //table within a tab.\n";
457 arrayFields += " tIdx = parseInt(search[2]);\n";
458 arrayFields += " rIdx = parseInt(search[4]);\n";
459 arrayFields += " break;\n";
460 arrayFields += " case /\\d+/.test(search[2]): //tab.\n";
461 arrayFields += " tIdx = parseInt(search[2]);\n";
462 arrayFields += " break;\n";
463 arrayFields += " case /\\d+/.test(search[4]): //row.\n";
464 arrayFields += " rIdx = parseInt(search[4]);\n";
465 arrayFields += " break;\n";
466 arrayFields += " }\n";
467 arrayFields += " fieldName = clss;\n";
468 arrayFields += " return false; //break out of each loop.\n";
469 arrayFields += " }\n });\n";
470 arrayFields += "}\n//------ end of code for field extraction ---------\n";
471 }
472 helper = helper.replace('{$array_field_extraction}', arrayFields);
473 helper = helper.replace('{$list_of_fields}', fieldsText);
474 }else if($this.is('.select2 > a')){
475 let s='',g='';
476 $.each(formFields, function(field, ftag){
477 if(ftag[1].indexOf('class:select2') > 0){
478 s+= `${g}ccs2t['${ftag[0]}']={\n'placeholder':'',\n'dir':'ltr'\n};`
479 g='\n';
480 }
481 });
482 helper = helper.replace('{$select2_fields}', s);
483 }else if($this.is('.hybriddd > a')){
484 let s='',g='';
485 $.each(formFields, function(field, ftag){
486 if(ftag[1].indexOf('dynamic_checkbox') > 0){
487 s+= `${g}cchddt['${ftag[0]}']={\n'placeholder':'',\n'checkboxes':true\n};`
488 g='\n';
489 }
490 });
491 helper = helper.replace('{$hybriddd_fields}', s);
492 }
493 if($this.data('insert')=='end'){
494 line = jscme.lastLine();
495 jscme.setCursor({'line':line,'ch':line.length});
496 }
497 switch(line){
498 case 0:
499 let il = jscme.lastLine();
500 if(il>1 && jscme.getLine(il).indexOf('(jQuery)')<0){
501 if(jscme.getLine(il-1).indexOf('(jQuery)')>0) il-=1;
502 }
503 jscme.setCursor({'line':il,'ch':0});
504 helper += "\n";
505 break;
506 default:
507 helper += "\n";
508 break;
509 }
510 jscme.replaceSelection(helper);
511 line = jscme.getCursor().line;
512 $jsCodemirror.beautify();
513 jscme.setCursor({'line':line,'ch':line.length});
514 });
515 /*@since 1.1.1 disable grid editor for existing cf7 forms*/
516 if(0==$grid.children('.container').length){
517 $editorTabs.tabs('option',{ active:1, disabled:[0]});
518 $topTags.removeClass('click-disabled');
519 $bottomTags.removeClass('click-disabled');
520 /** @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms.*/
521 $('#is-cf7sg-form').val('false');
522 }
523 //update the codemirror when tags are inserted
524 $('form.tag-generator-panel .button.insert-tag').on('click', function(){
525 const $textarea = $('textarea#wpcf7-form');
526 if($textarea.is('.codemirror-cf7-update')){
527 const tag = $textarea.delay(100).val();
528 cme.replaceSelection(tag);
529 $textarea.val(''); //clear.
530 }
531 });
532 /** @since 4.8.1 ensure latest HTML form is available to other tabs */
533 $grid.on('cf7sg-cf7tag-update', function(e){
534 //update HTML code to ensure latest UI form changes available to other tabs.
535 $wpcf7Editor.html($grid.CF7FormHTML());
536 })
537 }); //-----------end codemirror editor setup
538 $('form#post').submit(function(event) {
539 const $this = $(this);
540 $( window ).off( 'beforeunload' ); //remove event to stop cf7 script from warning on save
541 event.preventDefault(); //this will prevent the default submit
542 //close any open UI fields.
543 $('.cf7-field-inner :input:visible').closeUIfield();
544 let $embdedForms = '',$formNoEmbeds = '', codeMirror ='';
545 if('#cf7-editor-grid' == gridTab){ //set up the code in the cf7 textarea
546 const $txta = $('textarea#wpcf7-form');
547 $txta.html($txta.val()+'\n');
548 codeMirror = html_beautify(
549 $grid.CF7FormHTML(),
550 {
551 'indent_size': 2,
552 'wrap_line_length': 0
553 }
554 );
555 $('textarea#wpcf7-form-hidden').html(codeMirror);
556 $formNoEmbeds = $('<div>').append(codeMirror);
557 }else{//we are in text mode.
558 codeMirror = cme.getValue();
559 $('textarea#wpcf7-form-hidden').html(codeMirror).val(codeMirror);
560 $formNoEmbeds = $('<div>').append(codeMirror);
561 }
562 //since 1.8 remove cf7sgfocus class if present.
563 $('.cf7sgfocus', $formNoEmbeds).removeClass('cf7sgfocus');
564 $embdedForms = $formNoEmbeds.find('.cf7sg-external-form').remove();
565 //setup sub-forms hidden field.
566 const embeds = [];
567 let hasTables = false, hasTabs = false, hasToggles=false;
568 if($embdedForms.length>0){
569 $embdedForms.each(function(){
570 embeds[embeds.length] = $(this).data('form');
571 });
572 }
573 $('#cf7sg-embeded-forms').val(JSON.stringify(embeds));
574 const cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s]*)(|\s*(.[^\[]*))\]/img;
575 /** @since 3.0.0 determine scripts required */
576 let scriptClass ="";
577 if(codeMirror.indexOf("class:sgv-")>0) scriptClass += "has-validation,";
578 if(codeMirror.indexOf("class:select2")>0) scriptClass += "has-select2,";
579 if(codeMirror.indexOf("class:nice-select")>0) scriptClass += "has-nice-select,";
580 /** @since 4.0 enable grouping of collapsible sections as slider */
581 if($('.cf7sg-collapsible', $formNoEmbeds).not('.cf7sg-slider-section > .cf7sg-collapsible').length>0){
582 scriptClass += "has-accordion,";
583 }
584 if($('.cf7sg-slider-section', $formNoEmbeds).length>0) scriptClass += "has-slider,";
585 if(codeMirror.indexOf("[benchmark")>0) scriptClass += "has-benchmark,";
586 if(codeMirror.indexOf("[date")>0 || 0<codeMirror.search(/\[text([^\]]+?)class:datepicker/ig)) scriptClass += "has-date,";
587
588 //scan and submit tabs & tables fields.
589 const tableFields = [];
590 $('.row.cf7-sg-table', $formNoEmbeds).each(function(){
591 /**@since 2.4.2 track each tables with unique ids and their fields*/
592 const unique = $(this).closest('.container.cf7-sg-table').attr('id'),
593 fields = {}, search = $(this).html();
594 fields[unique]=[];
595 let match = cf7TagRegexp.exec(search);
596 //console.log('search:'+search);
597 while (match != null) {
598 //ttFields[ match[2] ] = match[1];
599 fields[unique][fields[unique].length] = match[2];
600 //console.log('match'+match[2]);
601 match = cf7TagRegexp.exec(search); //get the next match.
602 }
603 tableFields[tableFields.length] = fields;
604 hasTables = true;
605 });
606
607 const tabFields = [];
608 $('.container.cf7-sg-tabs-panel', $formNoEmbeds).each(function(){
609 /**@since 2.4.2 track each tables with unique ids and their fields*/
610 const unique = $(this).attr('id'),fields = {}, search = $(this).html();
611 fields[unique]=[];
612
613 let match = cf7TagRegexp.exec(search);
614 while (match != null) {
615 //if( -1 === tableFields.indexOf(match[2]) ) /*removed as now want to idenify fields which are both tabs and table fields*/
616 fields[unique][fields[unique].length] = match[2];
617 //ttFields[match[2]] = match[1];
618 match = cf7TagRegexp.exec(search); //get the next match.
619 }
620 tabFields[tabFields.length] = fields;
621 hasTabs = true;
622 });
623 /**
624 * Track toggled fields to see if they are submitted or not.
625 * @since 2.5 */
626
627 const toggledFields = [], tabbedToggles=[], groupedToggles={};
628 $('.container.cf7sg-collapsible.with-toggle', $formNoEmbeds).each(function(){
629 /**@since 2.4.2 track each tables with unique ids and their fields*/
630 const $toggle = $(this), unique = $toggle.attr('id'), group = $toggle.data('group'),
631 fields = {}, search = $toggle.html();
632 fields[unique]=[];
633 if(group.length>0){
634 if('undefined' == typeof groupedToggles[group] ) groupedToggles[group] = [];
635 groupedToggles[group].push(unique);
636 }
637 let match = cf7TagRegexp.exec(search);
638 while (match != null) {
639 fields[unique].push(match[2]);
640 match = cf7TagRegexp.exec(search); //get the next match.
641 }
642 toggledFields.push(fields);
643 /** @since 4.0.0 differentiate toggles in tabed sections.*/
644 if($toggle.is('.cf7-sg-tabs .cf7sg-collapsible')) tabbedToggles.push(unique);
645 hasToggles = true;
646 });
647 //append hidden fields
648 $this.append('<input type="hidden" name="cf7sg-has-tabs" value="'+hasTabs+'" /> ');
649 $this.append('<input type="hidden" name="cf7sg-has-tables" value="'+hasTables+'" /> ');
650 $this.append('<input type="hidden" name="cf7sg-has-toggles" value="'+hasToggles+'" /> ');
651 const disabled = $editorTabs.tabs('option','disabled');
652 $this.append('<input type="hidden" name="cf7sg-has-grid" value="'+disabled+'" /> ');
653 //update script classes since v3.
654 if(hasTabs) scriptClass+="has-tabs,";
655 if(hasTables) scriptClass+="has-table,";
656 if(hasToggles) scriptClass+="has-toggles,";
657 if(!disabled) scriptClass+="has-grid,";
658 $this.append('<input type="hidden" name="cf7sg-script-classes" value="'+scriptClass+'" />');
659
660 $('#cf7sg-tabs-fields').val(JSON.stringify(tabFields));
661 $('#cf7sg-table-fields').val(JSON.stringify(tableFields));
662 $('#cf7sg-toggle-fields').val(JSON.stringify(toggledFields));
663 $('#cf7sg-tabbed-toggles').val(JSON.stringify(tabbedToggles));
664 $('#cf7sg-grouped-toggles').val(JSON.stringify(groupedToggles));
665 /** @since 4.0 enable js/css */
666 $jstext.text('');//empty.
667 codeMirror = jscme.getValue();
668 if(jscmUpdated){
669 if(codeMirror.length>2) $jstext.html(codeMirror);
670 }else if(codeMirror.length>1) $jstext.prop('disabled',true);
671 $csstext.text('');//empty.
672 codeMirror = csscme.getValue();
673 if(csscmUpdated){
674 if(codeMirror.length>2) $csstext.html(csscme.getValue());
675 }else if(codeMirror.length>1) $csstext.prop('disabled',true);
676 // continue the submit unbind preventDefault.
677 /** @since 4.4.3 conditional cf7 plugin hack.*/
678 if ('undefined' != typeof wpcf7cf && wpcf7cf.currentMode == 'normal' && wpcf7cf.getnumberOfFieldEntries() > 0) {
679 wpcf7cf.copyFieldsToText();
680 }
681 $this.unbind('submit').submit();
682 });
683 /*
684 Function to convert the UI form into its html final form for editing in the codemirror and/or saving to the CF7 plugin.
685 */
686 $.fn.CF7FormHTML = function(){
687 const $this = $(this);
688 if( !$this.is('#grid-form') ){
689 return '';
690 }
691 const $form = $('<div>').append( $this.html() );
692 let text='';
693 //remove the external forms
694 const external = {};
695 $('.cf7sg-external-form', $form).each(function(){
696 const $exform = $(this), id = $exform.data('form');
697 external[id] = $exform.children('.cf7sg-external-form-content').remove();
698 $exform.children('.form-controls').remove();
699 });
700 //remove the row controls
701 $('.row', $form).removeClass('ui-sortable').children('.row-controls').remove();
702
703 //remove the collapsible input
704 $('.container.cf7sg-collapsible', $form).each(function(){
705 const $this = $(this),cid = $this.attr('id'), $title = $this.children('.cf7sg-collapsible-title');
706 let text = $title.children('label').children('input[type="hidden"]').val();
707 $title.children('label').remove();
708 let toggle = '';
709 if($this.is('.with-toggle')){
710 toggle=' toggled';
711 }
712 text = '<span class="cf7sg-title'+toggle+'">'+text+'</span>';
713 $title.html(text + $title.html());
714 });
715 //remove tabs inputs
716 $('ul.cf7-sg-tabs-list li label', $form).remove();
717
718 const cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s]*)(|\s*(.[^\[]*))\]/img,
719 cf7sgToggleRegex = /class:cf7sg-toggle-(.[^\s]+)/i;
720 //remove textarea and embed its content
721 $('.columns', $form).each(function(){
722 const $this = $(this), $gridCol = $this.children('.grid-column'),
723 $text = $('textarea.grid-input', $gridCol);
724 $this.removeClass('ui-sortable');
725 if($text.length>0){
726 let text = $text.text();
727 //verify if this column is within a toggled section.
728 const $toggle = $this.closest('.container.cf7sg-collapsible.with-toggle');
729 if($toggle.length>0){
730 const cid = $toggle.attr('id');
731 /**
732 * track toggled checkbox/radio fields, because they are not submitted when not filled.
733 *@since 2.1.5
734 */
735 const $field = $text.siblings('div.cf7-field-type');
736 let isToggled = false;
737
738 if($field.length>0){
739 if($field.is('.checkbox.required') || $field.is('.radio') || $field.is('.file.required')) isToggled = true;
740 }else isToggled = true; //custom column, needs checking.
741
742 if(isToggled){
743 let search = text, match = cf7TagRegexp.exec(search);
744 while (match != null) {
745 switch(match[1]){
746 case 'checkbox*':
747 case 'radio':
748 case 'file*':
749 let options = '';
750 if(match.length>4){
751 const tglmatch = cf7sgToggleRegex.exec(match[4]);
752 if(tglmatch != null){
753 if(tglmatch[1] == cid) break;
754 options =match[4].replace(tglmatch[0],'class:cf7sg-toggle-'+cid);
755 }else{
756 options = 'class:cf7sg-toggle-'+cid+' '+match[4];
757 }
758 }else{
759 options = 'class:cf7sg-toggle-'+cid;
760 }
761 text = text.replace(match[0], '['+match[1]+' '+match[2]+' '+options+']');
762 //hasRadios = true;
763 break;
764 }
765 //console.log('match'+match[2]);
766 match = cf7TagRegexp.exec(search); //get the next match.
767 }
768 }//end isToggled.
769 }
770 $this.html('\n'+text);
771 }//else this column is a grid.
772 $gridCol.remove();
773 });
774 //reinsert the external forms
775 $('.cf7sg-external-form', $form).each(function(){
776 const $this = $(this);
777 let id = $this.data('form');
778 $this.append( external[id] );
779 });
780 text = $form.html();
781 if($grid.children('.container').length > 0){ //strip tabs/newlines
782 text = text.replace(/^(?:[\t ]*(?:\r?\n|\r))+/gm, "");
783 }
784 return text;
785 }
786 });//document ready end
787 //empty checks for undefined, null, false, NaN, ''
788 function isEmpty(v){
789 if('undefined' === typeof v || null===v) return true;
790 return typeof v === 'number' ? isNaN(v) : !Boolean(v);
791 }
792 })( jQuery, codeMirror_5_32, jsCodeMirror_5_32, cssCodeMirror_5_32);
793