PluginProbe
Smart Grid-Layout Design for Contact Form 7 / 3.3.3
Smart Grid-Layout Design for Contact Form 7 v3.3.3
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-layout-admin.js

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

1,205 lines 46.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2 /**
3 Javascript to handle grid editor
4 Event 'cf7sg-form-change' fired on #contact-form-editor element when codemirror changes occur
5 */
6 (function( $ ) {
7 var offsets = ['offset-one','offset-two', 'offset-three', 'offset-four', 'offset-five', 'offset-six', 'offset-seven', 'offset-eight', 'offset-nine', 'offset-ten', 'offset-eleven'];
8 var columnsizes = ['one', 'two', 'one-fourth', 'one-third', 'five', 'one-half', 'seven', 'two-thirds', 'nine', 'ten', 'eleven', 'full'];
9 var $wpcf7Editor,$grid,$rowControl = null;
10 //graphics UI template pattern
11 var $pattern = $('<div>').html(cf7grid.preHTML+'\\s*(\\[.*\\s*\\].*\\s*){0,}\\s*'+cf7grid.postHTML);
12 var required = cf7grid.requiredHTML.replace('*', '\\*');
13 // console.log('r:'+required);
14 $pattern.find('label').html('((\\s*.*)('+required+'){1}|(\\s*.*))');
15 $pattern.find('.info-tip').text('(.*\\s*)');
16 //console.log('p:'+$pattern.html());
17 var templateRegex = new RegExp($pattern.html(), 'ig');
18 var seekTemplate = false;
19 var cssTemplate = 'div.field';
20 var $template = $('<div id="cf7sg-dummy">').append(cf7grid.preHTML+cf7grid.postHTML);
21 $template = $template.children();
22 if(1==$template.length && $template.find('label').length>0){
23 seekTemplate = true;
24 cssTemplate = $template.prop('nodeName');
25 if($template.prop('class').length>0){
26 cssTemplate += '.'+$template.attr('class').split(' ').join('.');
27 }
28 }
29 var wpcf7Value = '';
30
31 $(document).ready( function(){
32 //change the form id to mimic cf7 plugin custom admin page.
33 /** @since 2.11.0 full screen button*/
34 var $editor = $('#cf7sg-editor'),
35 bodyWidth = $('#wpbody').width();
36 $editor.css('background-color',$('body').css('background-color'));
37 $('#full-screen-cf7').on('click', function(){
38 $editor.toggleClass('full-screen');
39 $(this).toggleClass('full-screen');
40 if($editor.is('.full-screen')) $editor.width(bodyWidth);
41 else $editor.width('auto');
42 // $oldParent = $editor.parent();
43 // $editor.detach();
44 // $nextParent.prepend($editor);
45 // $nextParent = $oldParent;
46 });
47 $wpcf7Editor = $('textarea#wpcf7-form-hidden');
48 $grid = $('#grid-form');
49 $rowControl = $('#top-grid-controls');
50
51 /*
52 Build grid from existing form------------------------- BUILD UI FORM
53 */
54 function buildGridForm(){
55 var formhtml = $wpcf7Editor.text();
56 if(0===formhtml.length){
57 formhtml = '<div class="container"><div class="row"><div class="columns full"></div></div></div>';
58 }
59 var $form = $('<div>').append( formhtml );
60 var isGrid = true; //return value.
61 $grid.html(''); //empty the grid.
62 if(0===$form.children('.container').length){
63 isGrid = false;
64 }
65 //remove the external forms
66 $('.cf7sg-external-form .cf7sg-external-form-content', $form).remove();
67 //replace columns content with textareas
68 /*--------------------------------------------------- convert columns */
69 $('div.columns', $form).each(function(){
70 var $this = $(this);
71 var $area = $($('#grid-col').html());
72 if($this.children().is('.container')){
73 $('textarea.grid-input', $area).remove();
74 $('div.cf7-field-inner', $area).remove();
75 }else{
76 if(cf7grid.ui) $('textarea.grid-input', $area).html($this.html().trim());
77 else $('textarea.grid-input', $area).val($this.html().trim());
78
79 $this.children().remove();
80 $this.text('');
81 }
82 $this.prepend($area);
83 });
84 $('div.row', $form).each(function(){
85 $(this).append( $('#grid-row .row-controls').clone() );
86 });
87 /*--------------------------------------------------- convert collapsible sections */
88 $('div.container.cf7sg-collapsible', $form).each(function(){
89 var $this = $(this);
90 var id = $this.attr('id');
91 if(typeof id == 'undefined'){
92 id = randString(6);
93 $this.attr('id', id); //assign a random id
94 }
95 var text = $this.children('.cf7sg-collapsible-title span.cf7sg-title').text();
96 if(0==text.length){ //pre v1.8 title?.
97 text = $this.children('.cf7sg-collapsible-title').text();
98 }
99 var $toggle = $('.toggle', $this.children('.cf7sg-collapsible-title'));
100 if($toggle.length>0){
101 $toggle = $toggle.clone();
102 }
103
104 $this.children('.cf7sg-collapsible-title').remove();
105 $this.prepend( $('#grid-collapsible').html());
106 $('input', $this.children('.cf7sg-collapsible-title')).not('[type="checkbox"]').val(text);
107 if($toggle.length>0){
108 $this.children('.cf7sg-collapsible-title').append($toggle);
109 $('input[type="checkbox"]', $this.children('.cf7sg-collapsible-title') ).prop('checked', true);
110 }
111 var $ctrl = $this.children('.row').children('.row-controls').find('.collapsible-row-label');
112 $('input', $ctrl).prop('checked', true);
113 //toggle disable the sibling input
114 $('input', $ctrl.siblings('.unique-mod')).prop('disabled', function(i,v){return !v;});
115 });
116 /*--------------------------------------------------- convert tables */
117 $('div.container.cf7-sg-table', $form).each(function(){
118 var $this = $(this);
119 var id = $this.attr('id');
120 if(typeof id == 'undefined'){
121 id = 'cf7-sg-table-'+(new Date).getTime();
122 $this.attr('id', id);
123 }
124 var $ctrl = $this.find('.row.cf7-sg-table > .row-controls' ).first().find('.table-row-label');
125 $('input', $ctrl).prop('checked', true);
126 //set button label
127 var text = $this.data('button');
128 if(typeof text == 'undefined'){
129 text = 'Add Row';
130 $this.attr('data-button',text);
131 }
132 $ctrl.next('.table-row-button').children('input').val(text);
133 //toggle disable the sibling input
134 $('input', $ctrl.siblings('.unique-mod')).prop('disabled', function(i,v){return !v;});
135 //toggle footer row
136 var $footer = $this.next();
137 if($footer.is('.cf7-sg-table-footer')){
138 $ctrl = $footer.children('.row').first().find('.row-controls .footer-row-label');
139 $('input.footer-row', $ctrl).prop('checked', true);
140 $('input', $ctrl.siblings('.unique-mod')).prop('disabled', function(i,v){return !v;});
141 }
142 });
143 //tabs
144 /*--------------------------------------------------- convert tabs */
145 $('ul.cf7-sg-tabs-list li', $form).each(function(){
146 var $this = $(this);
147 var text = $this.children('a').text();
148 $this.append($('#grid-tabs ul li label').clone());
149 $('label input', $this).val(text);
150 //setup checkbox
151 var $ctrl = $this.parent().siblings('.cf7-sg-tabs-panel');
152 $ctrl = $ctrl.children('.row').find('.row-controls' ).first().find('.tabs-row-label');
153 $('input.tabs-row', $ctrl).prop('checked', true);
154 $('input', $ctrl.siblings('.unique-mod')).prop('disabled', function(i,v){return !v;});
155 });
156 //reinsert the external forms
157 $('.cf7sg-external-form', $form).each(function(){
158 var $extform = $(this);
159 $extform.append($( $('#grid-cf7-forms .cf7sg-external-form').html() ) );
160 var id = $extform.data('form');
161 if($('#grid-cf7-forms .form-select option[value="'+id+'"]' ).length > 0 ){
162 //add controls
163 //$extform.append($('#grid-cf7-forms .form-controls').clone());
164 $('.form-controls .form-select', $extform).val(id);
165 //check for form update.
166 var data = {
167 'action' : 'get_cf7_content',
168 'id': $('#post_ID').val(),
169 'nonce' : $('#_wpcf7nonce').val(),
170 'cf7_key' : id,
171 'update' :true
172 };
173 $.post(ajaxurl, data, function(response) {
174 if(response.length > 0){
175 $('.cf7sg-external-form-content', $extform).attr('id','cf7sg-form-'+id).append( response );
176 }//TODO if error insert a msg.
177 });
178
179 }
180 });
181 //add the form to the grid
182 if($form.children('.container').length >0 || $form.children('.cf7sg-external-form').length>0){
183 $grid.append($form.children());
184 }else{ //this is not a cf7sg form.
185 $grid.html($form.html());
186 }
187 //set the value of each textarea as inner text
188 $('textarea', $grid).each(function(){
189 var $this = $(this);
190 $this.html($this.val());
191 });
192 /*--------------------------------------------------- if ui mode, then convert to gui template */
193 var $textareaSelected='';
194 if(cf7grid.ui){
195 $('div.columns', $grid).each(function(){
196 var $this = $(this);
197 if($this.children().is('.container')) return true;
198 $this.html2gui();
199 });
200 }else{
201 //set the first textarea as our default tag consumer
202 $('textarea#wpcf7-form').attr('id','');
203 $textareaSelected = $('textarea', $grid).first();
204 $textareaSelected.attr('id', 'wpcf7-form');
205 }
206 //change this to whichever is live
207 $('textarea', $grid).on('focus', function(){
208 var $this = $(this);
209 if($textareaSelected.length>0 && $textareaSelected.is('#wpcf7-form')){
210 $textareaSelected.attr('id','');
211 $textareaSelected.html($textareaSelected.val()); //set its inner html
212 }
213 if($this.is('.grid-input')){
214 $('textarea#wpcf7-form').attr('id','');
215 $textareaSelected = $this.attr('id','wpcf7-form');
216 }
217 });
218 return isGrid;
219 } //end buildGridForm()
220
221 //offset/size change using event delegation
222 /*---------------------------------------------------------------------------- ui menus */
223 $grid.on('change', function(event){
224 var $target = $(event.target);
225 if($target.is('.column-setting')){ //----------- column size/offset settings
226 var validation = ['dummy'];
227 if( $target.is('.column-offset') ){
228 validation = offsets;
229 }else if( $target.is('.column-size') ){
230 validation = columnsizes;
231 }else{
232 return false;
233 }
234 var $column = $target.closest('.columns');
235 var classList = $column.attr('class').split(/\s+/);
236 var idx;
237 for(idx=0; idx<classList.length; idx++){
238 if($.inArray(classList[idx], validation) > -1){
239 $column.removeClass(classList[idx]);
240 }
241 }
242 $column.addClass($target.val());
243 //filter the options
244 $target.closest('.grid-controls').filterColumnControls();
245 }else if($target.is('.form-select')){ //-------------- external form selection
246 var $container = $target.closest('.cf7sg-external-form');
247 $container.attr('data-form', $target.val());
248 var data = {
249 'action' : 'get_cf7_content',
250 'id': $('#post_ID').val(),
251 'nonce' : $('#_wpcf7nonce').val(),
252 'cf7_key' : $target.val()
253 };
254 $.post(ajaxurl, data, function(response) {
255 $('.cf7sg-external-form-content', $container).attr('id','cf7sg-form-'+$target.val()).html(response);
256 });
257 }
258 /*
259 Collapsible / tabs rows input changes
260 */
261 if($target.is('.cf7sg-collapsible-title label input[type="text"]')){ //------- Collapsible title
262 $target.siblings('input[type="hidden"]').val($target.val());
263 }else if( $target.is('.cf7sg-collapsible-title label input[type="checkbox"]')){ //------- Collapsible title toggled
264 var $title = $target.closest('.cf7sg-collapsible-title');
265 if($target.is(':checked')){
266 $title.append($('#grid-collapsible-with-toggle').html());
267 $title.closest('.container.cf7sg-collapsible').addClass('with-toggle').attr('data-group','');
268 }else{
269 $('.toggle', $title).remove();
270 $title.closest('.container.cf7sg-collapsible').removeClass('with-toggle').removeAttr( 'data-group');
271 }
272 }else if($target.is('ul.cf7-sg-tabs-list li label input[type="text"]')){ //------- Tabs title
273 $target.parent().siblings('a').text($target.val());
274 }else if($target.is('label.table-row-button input')){
275 $target.closest('.container.cf7-sg-table').attr('data-button',$target.val());
276 }
277 if(cf7grid.ui){
278 if($target.is('.cf7-field-inner textarea')){
279 var label = $target.scanCF7Tag();
280 $target.siblings('p.content').html(label);//.show();
281 $target.parent().siblings('textarea.grid-input').updateGridForm();
282 }else if($target.is('.cf7-field-inner input')){
283 $target.siblings('p.content').html($target.val());
284 $target.parent().siblings('textarea.grid-input').updateGridForm();
285 }
286 }
287 });
288
289 //grid click event delegation
290 $grid.on('click', function(event){
291 var $target = $(event.target);
292 switch(true){
293 case $target.is('input[type="text"]:visible'):
294 case $target.is('textarea:visible'): //click on a field.
295 case $target.is('select:visible'):
296 return true;
297 break;
298 }
299 //close any open row/column controls
300 closeAllControls();
301
302 /*
303 Row controls
304 ----------------------------------------------------------ROW CONTRLS
305 */
306 var $parentRow;
307 if($target.is('.dashicons-trash.form-control')){ //--------TRASH included form
308 $target.closest('.cf7sg-external-form').remove();
309 }else if($target.is('.dashicons-plus.form-control') ){ //---ADD external form
310 $target.closest('.cf7sg-external-form').insertNewRow();
311 }else if($target.is('.dashicons-trash.row-control')){ //--------TRASH
312 var $parentContainer = $target.closest('.container');
313 var $parent = $parentContainer.parent();
314 $parentContainer.remove();
315 if( $parent.is('.columns') ) { //verify is this is the last row being deleted
316 if( 0 == $parent.children('.container').length ){
317 //add a text area to the column
318 $parent.children('.grid-column').append('<textarea class="grid-input"></textarea>');
319 }
320 }
321 }else if($target.is('.dashicons-plus.row-control')){ //-----------ADD Row
322 $target.closest('.container').insertNewRow();
323 }else if($target.is('.dashicons-edit.row-control')){ //-----------Show controls
324 //hide any other controls that might be open
325 //taken care by closeAllControls
326 // $('.grid-controls', $grid).hide();
327 // $('.dashicons-no-alt', $grid).hide();
328 // $('.dashicons-edit', $grid).show();
329 //now show this control
330 $target.siblings('.grid-controls').show();
331 $target.hide();
332 $target.siblings('.dashicons-no-alt').show();
333 /*
334 TODO: use $('.grid-controls')filterColumnControls() to make sure columns sizes/offsets are correct.
335 possibly introduce a boolean to check if filter has been run already on this row
336 */
337 }else if( $target.is('.dashicons-no-alt.row-control') ) { //----------------hide controls
338 //take care by closeAllControls
339 // $target.siblings('.grid-controls').hide();
340 // $target.hide();
341 // $target.siblings('.dashicons-edit').show();
342 }else if($target.is('input.collapsible-row')){ //-------------checkbox collapsible row
343 var $container = $target.closest('.container');
344 if($target.is(':checked')){
345 $container.addClass('cf7sg-collapsible');
346 var id = $container.attr('id');
347 if(typeof id == 'undefined'){
348 id = randString(6);
349 $container.attr('id', id); //assign a random id
350 }
351 $container.prepend($('#grid-collapsible').html());
352 }else{
353 $container.removeClass('cf7sg-collapsible');
354 $container.children('.cf7sg-collapsible-title').remove();
355 }
356 //toggle disable the sibling input
357 $target.parent().siblings('label.unique-mod').children('input').prop('disabled', function(i,v){return !v;});
358 }else if($target.is('input.table-row')){ //-------------checkbox table row
359 if($target.is(':checked')){
360 var id = 'cf7-sg-table-'+(new Date).getTime();
361 $target.closest('.row').addClass('cf7-sg-table');
362 $target.closest('.container').addClass('cf7-sg-table').attr('id',id);
363 }else{
364 $target.closest('.row').removeClass('cf7-sg-table');
365 $target.closest('.container').removeClass('cf7-sg-table').removeAttr('id');
366 }
367 //toggle disable the sibling input
368 $target.parent().siblings('label.unique-mod').children('input').prop('disabled', function(i,v){return !v;});
369 }else if($target.is('input.tabs-row')){ //-------------checkbox tabbed row
370 var $panel = $target.closest('.container');
371 if($target.is(':checked')){
372 var id = 'cf7-sg-tab-'+(new Date).getTime();
373 $panel.addClass('cf7-sg-tabs-panel').attr('id',id);
374 $panel.before($('#grid-tabs').html());
375 $panel.closest('.columns').addClass('cf7-sg-tabs');
376 $('li a', $panel.siblings('ul.cf7-sg-tabs-list')).attr('href','#'+id);
377 }else{
378 $panel.removeClass('cf7-sg-tabs-panel');
379 $panel.closest('.columns').removeClass('cf7-sg-tabs');
380 $panel.siblings('ul.cf7-sg-tabs-list').remove();
381 }
382 //toggle disable the sibling input
383 $target.parent().siblings('label.unique-mod').children('input').prop('disabled', function(i,v){return !v;});
384 }else if($target.is('input.footer-row')){ //-------------checkbox footer row
385 var $table = $target.closest('.container').prev();
386 if($table.is('.container.cf7-sg-table')){
387 if($target.is(':checked')){
388 $target.closest('.container').addClass('cf7-sg-table-footer');
389 }else{
390 $target.closest('.container').removeClass('cf7-sg-table-footer');
391 }
392 //toggle disable the sibling input
393 $target.parent().siblings('label.unique-mod').children('input').prop('disabled', function(i,v){return !v;});
394 }
395 }
396
397
398 /*
399 Column controls, show/hide contols, add column, and refresh select dropdowns for colum size on addition
400 */
401
402 var $parentColumn ;
403 if( $target.is('.columns')){
404 $parentColumn = $target;
405 }else{
406 $parentColumn = $target.closest('.columns');
407 }
408 //let's close any column controls : taken care by closeAllControls
409 // if(0===$target.closest('.grid-controls').length ){
410 // $('.grid-column .grid-controls', $grid).hide();
411 // $('.grid-column .column-control.dashicons-no-alt', $grid).hide();
412 // $('.grid-column .column-control.dashicons-edit', $grid).show();
413 // //close any ui fields if any
414 // if(cf7grid.ui && !$target.is('.cf7-field-inner *')){
415 // $('.grid-column .cf7-field-inner span.dashicons').trigger('click');
416 // }
417 // }
418 //verify which target was clicked
419 if( $target.is('.dashicons-edit.column-control') ){ //------------------show controls
420 //now show this control
421 $target.siblings('.grid-controls').show().filterColumnControls();
422 $target.hide();
423 $target.siblings('.dashicons-no-alt').show();
424 }else if( $target.is('.php-icon.column-control') ) { //--------show hooks
425 let $helper =$('<div class="helper-popup">').html( $('#grid-helper').html());
426 $target.after($helper);
427 let $copy = $('.copy-helper', $helper);
428 let field = $target.data('field');
429 let tag = $target.data('tag');
430 let search = $target.data('search');
431 let $hooks = $(search, '#fieldhelperdiv').clone();
432 $('.cf7sg-helper-list', $helper).append($hooks);
433 $('a.helper', $helper).each(function(){
434 new Clipboard($(this)[0], {
435 text: function(trigger) {
436 let $target = $(trigger);
437 let text = $target.data('cf72post');
438 //get post slug
439 let key = $('#post_name').val();
440 text = text.replace(/\{\$form_key\}/gi, key);
441 text = text.replace(/\{\$field_name\}/gi, field);
442 text = text.replace(/\{\$field_name_slug\}/gi, field.replace('-','_'));
443 text = text.replace(/\{\$field_type\}/gi, tag);
444 text = text.replace(/\[dqt\]/gi, '"');
445 return text;
446 }
447 });
448 $(this).append($copy.clone());
449 });
450 $helper.click('a.helper, .dashicons-no-alt', function(e){
451 $(this).remove();
452 });
453 }else if( $target.is('.js-icon.column-control') ) { //--------show js hooks
454 let $helper =$('<div class="helper-popup">').html( $('#grid-js-helper').html());
455 $target.after($helper);
456 let $copy = $('.copy-helper', $helper);
457 let field = $target.data('field');
458 let tag = $target.data('tag');
459 let search = $target.data('search');
460 let $hooks = $(search, '#fieldhelperjs').clone();
461 $('.cf7sg-helper-list', $helper).append($hooks);
462 $('a.helper', $helper).each(function(){
463 new Clipboard($(this)[0], {
464 text: function(trigger) {
465 let $target = $(trigger);
466 let text = $target.data('cf72post');
467 //get post slug
468 let key = $('#post_name').val();
469 text = text.replace(/\{\$form_key\}/gi, key);
470 text = text.replace(/\{\$field_name\}/gi, field);
471 text = text.replace(/\{\$field_name_slug\}/gi, field.replace('-','_'));
472 text = text.replace(/\{\$field_type\}/gi, tag);
473 text = text.replace(/\[dqt\]/gi, '"');
474 return text;
475 }
476 });
477 $(this).append($copy.clone());
478 });
479 $helper.click('a.helper, .dashicons-no-alt', function(e){
480 $(this).remove();
481 });
482 }else if($target.is('.icon-code.column-control') ){
483 var $focus = $target.closest('.columns');
484 //toggle cf7sgfocus class on inner field to focus on.
485 if($focus.is('.cf7sgfocus')){
486 $focus.removeClass('cf7sgfocus');
487 }else{
488 $('.cf7sgfocus', $grid).removeClass('cf7sgfocus');
489 $focus.addClass('cf7sgfocus');
490 }
491 //move to text editor.
492 $('#form-editor-tabs').tabs('option',{ active:1});
493 }else if($target.is('.dashicons-trash.column-control') ){ //-------------------delete column
494
495 $parentColumn.remove();
496 //refilter
497 //$target.siblings('.grid-controls').filterColumnControls();
498 }else if( $target.is('.make-grid') ){ //--------------------add row/convert column to grid
499
500 //close the grid-control box
501 $target.parent().hide();
502 $target.parent().siblings('.dashicons-no-alt').hide();
503 $target.parent().siblings('.dashicons-edit').show();
504 //convert to grid
505 var $parentColumn = $target.closest('.columns');
506 if($parentColumn.length > 0){
507 //column already has a row?
508 var text = '';
509 if(0 == $parentColumn.children('.container').length){
510 //keep the textarea and remove from the column
511 if(cf7grid.ui){
512 text = $('textarea.grid-input', $parentColumn).remove().text();
513 $('div.cf7-field-inner', $parentColumn).remove();
514 }else{
515 text = $('textarea.grid-input', $parentColumn).remove().val();
516 }
517 }else{
518 text = $parentColumn.children('.container').remove();
519 }
520 $parentColumn.insertNewRow(text);
521 }else{ //add to the main container
522 $grid.insertNewRow();
523 }
524 }else if($target.is('.external-form')){ //---------------- insert cf7 form
525 //close the grid-control box
526 $target.parent().hide();
527 $target.parent().siblings('.dashicons-no-alt').hide();
528 $target.parent().siblings('.dashicons-edit').show();
529 //replace container with form selector
530 $target.closest('.container').after($('#grid-cf7-forms').html());
531 $target.closest('.container').remove();
532 }else if( $target.is('.dashicons-plus.column-control') ){ //--------------------add column
533 var $parentColumn = $target.closest('.columns');
534 var $parentRow = $parentColumn.closest('.row');
535 var classList, idx , columns, row;
536 var newSize=0;
537 var sizes = [];
538 var $newColumn = $('<div class="columns"></div>');
539 var createColumn = true;
540 //is the row filled up?
541 var total = 0;
542 //first check if the current column fills the entire row
543 if( $parentColumn.is('.full') ){
544 total = 12;
545 columns = 1;
546 sizes[0] = columnsizes.length - 1; //ie 11, the last value
547 }else{
548 row = $parentRow.getRowSize();
549 total = row.length;
550 sizes = row.cols;
551 columns = sizes.length;
552 }
553 if(12 == total) {
554 newSize = Math.floor( total/(columns + 1) ) - 1 ;
555 if(newSize < 0 ){ //max columns reached
556 /*TODO: display an error message and return */
557 return false;
558 }
559 createColumn = false;
560 var newClass = columnsizes[newSize];
561 $parentRow.children('.columns').each(function(index){
562 //make sure the column is not size 1
563 if( sizes[index] > 0 ){
564 $(this).changeColumnSize(columnsizes[sizes[index]], newClass );
565 createColumn = true;
566 }
567 });
568 if(!createColumn){ //not enough space to create extra column
569 /*TODO: display an error message and return */
570 return false;
571 }
572 }else if(total < 12){ //just add the new column as size 1
573 if( (12 - total) < total){
574 newSize = 12 - total - 1;
575 }else{
576 newSize = total - 1;
577 }
578 }else{ //should never reach here
579 return false;
580 }
581 //add the new column
582 $newColumn.append( $( $('#grid-col').html() ) );
583 if(cf7grid.ui){
584 $('textarea.grid-input' ,$newColumn).hide();
585 }else{
586 $('div.cf7-field-inner', $newColumn).hide();
587 }
588 $newColumn.changeColumnSize('',columnsizes[newSize]);
589 $parentColumn.after($newColumn);
590 }
591 /*
592 Column UI fields
593 */
594 if(cf7grid.ui){
595 //close any open ui fields
596 closeAlluiFields();
597 if($target.is('.cf7-field-inner p.content')){
598 $target.parent().showUIfield();
599 }else if($target.is('.cf7-field-inner span.dashicons')){
600 //field will be closed by closeAlluiFields
601 }else if('none'!==$('#wpcf7-form').css('display') && !$target.is('#wpcf7-form')){
602 changeTextarea();
603 }
604 }
605 });
606
607 // capture tab and move to the next field
608 if(cf7grid.ui){
609 $grid.keydown('div.cf7-field-inner', function(event){
610 if(9 !== event.which ){//tab
611 return;
612 }
613 $target = $(event.target);
614 if($target.is('div.cf7-field-inner :input')){
615 $target.closeUIfield();
616 var $next = $target.parent().next('div.cf7-field-inner');
617 if($next.length>0){
618 $next.showUIfield();
619 event.preventDefault(); //stop tab focus on this element.
620 }
621 }
622 });
623
624 }
625 //general inputs into the textareas will trigger form change event
626 $grid.on('input selectionchange propertychange', 'textarea', function(event){
627 var $target = $(event.target);
628 if($target.is('.cf7-field-type textarea')){
629 return false; //form change trigger will happen after this
630 }else if($target.is('textarea')){
631 $target.html($target.val()+'\n'); //ensure changes are capture in the codemirror editor
632 $('#contact-form-editor').trigger('cf7sg-form-change');
633 }
634 });
635 //before grid editor is closed, update the form with the last textarea
636 //event 'cf7grid-tab-finalise' is fired in cf7-grid-codemirror.js file
637 $grid.on('cf7grid-form-finalise', function(){
638 if(cf7grid.ui){
639 $('#wpcf7-form').parent().siblings('textarea.grid-input').on('change', function(){
640 $grid.trigger('cf7grid-form-ready'); //codemirror initialisation
641 });
642 $('textarea.grid-input#wpcf7-form').on('change', function(){ //special case for custom code in cf7ui mode
643 $grid.trigger('cf7grid-form-ready'); //codemirror initialisation
644 });
645 changeTextarea(true);
646 }else{
647 var $txta = $('textarea#wpcf7-form');
648 $txta.html($txta.val()+'\n');
649 $grid.trigger('cf7grid-form-ready'); //codemirror initialisation
650 }
651 });
652
653 //initial construction of grid form
654 buildGridForm();
655 $grid.on('build-grid', function(){
656 if( !buildGridForm() ){
657 $('#form-editor-tabs').tabs('option',{ active:1, disabled:true});
658 }else{
659 var $focus = $('.cf7sgfocus', $grid);
660 if($focus.length>0){
661 var scrollPos = $focus.offset().top - $(window).height()/2 + $focus.height()/2;
662 //console.log(scrollPos);
663 $(window).scrollTop(scrollPos);
664 $focus.removeClass('cf7sgfocus');
665 }
666 }
667
668 });
669 //make columns sortable
670 sortableRows();
671 //make rows sortable
672 $('.columns, #grid-form').sortable({
673 //placeholder: "ui-state-highlight",
674 handle:'.row-controls > .dashicons-move',
675 axis: 'y',
676 //containment:'parent',
677 items: '> .container, > .cf7sg-external-form',
678 helper:'clone'
679 });
680 //grid is ready
681 $wpcf7Editor.trigger('grid-ready');
682 }); //end document ready
683
684 //random id function.
685 function randString(n){
686 if(!n){
687 n = 5;
688 }
689 var text = '';
690 var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
691 for(var i=0; i < n; i++){
692 text += possible.charAt(Math.floor(Math.random() * possible.length));
693 }
694 return text;
695 }
696 //function to close any ui fields
697 function closeAlluiFields(){
698 $('.cf7-field-inner :input:visible').each(function(){
699 $(this).closeUIfield();
700 });
701 }
702 //close controls row/column
703 function closeAllControls(){
704 $('.grid-controls:visible', $grid).each(function(){
705 var $this = $(this);
706 $this.hide();
707 $this.siblings('.dashicons-no-alt').hide();
708 $this.siblings('.dashicons-edit').show();
709 });
710 //close helper popups.
711 $('.column-control+.helper-popup').remove();
712 }
713 //trigger a change on the textarea#wpcf7-form field if its value has changed
714 function changeTextarea(finalise = false){
715 if(cf7grid.ui){
716 var $lastTA = $('#wpcf7-form');
717 if($lastTA.length >0 && wpcf7Value !== $lastTA.val()){
718 wpcf7Value = '';
719 $lastTA.trigger('change');
720 }else if(finalise){
721 $grid.trigger('cf7grid-form-ready'); //codemirror initialisation
722 }
723 }
724 }
725 function sortableRows( $newRow='' ){
726 if($newRow.lenght>0){
727 $('.row', $grid).not($newRow).sortable('destroy');
728 }
729 $('.row', $grid).sortable({
730 //placeholder: "ui-state-highlight",
731 //containment:'.row',
732 handle:'.grid-column > .dashicons-move',
733 connectWith: '.row',
734 helper:'clone',
735 items: '> .columns',
736 receive: function( event, ui ) {
737 //ui.item validate column size relative to new row columns.
738 //ui.sender original row. cnacel if column does not fit in new row.
739 //$targetRow has more than 12 columns then cancel.
740 var $targetRow = $(event.target);//receiving row
741 var row = $targetRow.getRowSize();
742 //var total = row.length;
743 //var sizes = row.cols;
744 //var columns = sizes.length;
745 if( row.length > 12){
746 ui.sender.sortable('cancel');
747 var $warning = $('<span class="cf7sg-warning">Not enough space in this row to add column</span>');
748 $targetRow.after($warning);
749 $warning.delay(2000).fadeOut('slow', function(){
750 $(this).remove();
751 });
752 }else{
753 //make sure the row controls is at the end.
754 var control = $targetRow.children('.row-controls').remove();
755 $targetRow.append(control);
756 }
757 }
758 });
759 }
760 /* some function definitions...*/
761 $.fn.closeUIfield = function(){
762 var $this = $(this);
763 if(!$this.is('.cf7-field-inner :input:visible')){
764 return $this;
765 }
766 if($this.parent().is('.cf7-field-type')) changeTextarea();
767 $this.hide().attr('id', '');
768 $this.siblings('.dashicons-no-alt').hide();
769 $this.siblings('.content').show();
770 return $this;
771 }
772 $.fn.showUIfield = function(){
773 var $this = $(this);
774 if(!$this.is('.cf7-field-inner')){
775 return $this;
776 }
777 $this.find('p.content').hide();
778 $this.find('span.dashicons').show();
779 var $input = $(':input', $this).show();
780 $input.focus();
781 if($input.is('textarea')){
782 $('textarea#wpcf7-form').attr('id','');
783 $input.attr('id', 'wpcf7-form');
784 wpcf7Value = $input.val();
785 }else{
786 changeTextarea();
787 }
788 return $this;
789 }
790 $.fn.html2gui = function(html){
791 var $this = $(this);
792 if(typeof html === 'undefined') html ='';
793 if(html.length === 0){
794 //get the fields from the textarea
795 html = $('textarea.grid-input', $this).text();
796 if(html.length === 0){
797 $('textarea.grid-input', $this).hide();
798 return $this;
799 }
800 }
801 var singleField = true;
802 var search = $('<div>').append(html);
803
804 if(seekTemplate && 1==search.children(cssTemplate).length){
805 var lines = html.split(/\r\n|\r|\n/g);
806 search = '';
807 for(var i=0; i<lines.length; i++){
808 search += lines[i].trim();
809 }
810 var match = templateRegex.exec(search);
811 if(null !== match){
812 //populate the fields
813 var $field = $('div.cf7-field-label', $this);
814 $('input', $field).val(match[1]);
815 $('p.content', $field).html(match[1]);
816 $field = $('div.cf7-field-type', $this);
817 var tag = $('textarea', $field).val(match[5]).scanCF7Tag();
818 $('p.content', $field).html(tag);
819 $field = $('div.cf7-field-tip', $this);
820 $('input', $field).val(match[6]);
821 $('p.content', $field).html(match[6]);
822 //hide the textarea
823 $('textarea.grid-input', $this).hide();
824 //reset global regex
825 templateRegex.lastIndex = 0;
826 }else{ //this html does not match our templates
827 $('div.cf7-field-inner', $this).remove();
828 }
829 }else{//this html does not match our templates
830 $('div.cf7-field-inner', $this).remove();
831 }
832 }
833
834 $.fn.scanCF7Tag = function(){
835 let $this = $(this);
836 if(!$this.is('textarea')){
837 return '';
838 }
839 let $parent = $this.parent(), //.cf7-field-type.
840 $helper = $parent.siblings('.php-icon'),
841 $jshelper = $parent.siblings('.js-icon');
842 $helper.each(function(index){
843 if(index>0){
844 $(this).remove();
845 return;
846 }
847 $(this).removeAttr('data-field').removeAttr('data-tag').removeAttr('data-search');
848 });
849 //reset helper.
850 $helper = $parent.siblings('.php-icon');
851 //handle jshleper
852 $jshelper.each(function(index){
853 if(index>0){
854 $(this).remove();
855 return;
856 }
857 $(this).removeAttr('data-field').removeAttr('data-tag').removeAttr('data-search');
858 });
859 //reset jshelper.
860 $jshelper = $parent.siblings('.js-icon');
861
862 let cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s\]]*)[\s\[]*(.[^\[]*\"source:([^\s]*)\"[\s^\[]*|[.^\[]*(?!\"source:)[^\[]*)\]/img,
863 search = $this.val(),
864 match = cf7TagRegexp.exec(search),
865 label='',
866 isRequired = false,
867 type = [],
868 fields = [],
869 hooks = [],jshooks=[],
870 tag='',
871 isSubmit = false, hasHidden = false,
872 count =0, counth = 0,
873 field = '',
874 stopSearch = false;
875
876 while (match != null && !stopSearch) {
877 count++;
878 label+='['+match[1]+' '+match[2]+']';
879 tag = match[1].replace('*','');
880 field = match[2];
881 let helpers = ['cf7sg-tag-all'], jsHelpers = ['cf7sg-tag-all'];
882 helpers[helpers.length] = 'cf7sg-tag-'+tag;
883 jsHelpers[jsHelpers.length] = 'cf7sg-tag-'+tag;
884 switch(tag){
885 case 'submit':
886 case 'save':
887 tag +='-button';
888 isSubmit = true;
889 break;
890 case 'textarea':
891 if( match[0].search(/\s[0-9]{0,3}x[0-9]{1,3}\s?/ig) <0){
892 var cf7sc = match[0].replace(']',' x5]');
893 cf7sc = search.replace(match[0], cf7sc);
894 $this.val(cf7sc);
895 }
896 break;
897 case 'acceptance': //special case with closing tag.
898 stopSearch = true;
899 break;
900 case 'recaptch':
901 case 'recaptcha': //special case with closing tag.
902 label='[recaptcha]';
903 stopSearch = true;
904 break;
905 case 'dynamic_select':
906 var source ='';
907 switch(true){
908 case ( match.length > 4 && 'undefined' !== typeof match[4] ) : //match[4] exists.
909 source = match[4].split(':');
910 source = source[0];
911 helpers[helpers.length] = 'cf7sg-tag-dynamic_select-'+source;
912 case match.length > 3: //lets deal with match[3]
913 if(0=== source.length && match[3].indexOf('slug:'>-1)){
914 source = 'taxonomy';
915 helpers[helpers.length] = 'cf7sg-tag-dynamic_select-'+source;
916 }
917 if(match[3].indexOf('class:tags')>-1){
918 helpers[helpers.length] = 'cf7sg-tag-dynamic_select-tags';
919 if(source.length>0){
920 helpers[helpers.length] = 'cf7sg-tag-dynamic_select-'+source+'-tags';
921 }
922 }
923 break;
924 }
925 break;
926 case 'hidden': /** @since 3.2.1 fix hidden field class */
927 tag+='-input';
928 counth++;
929 break;
930 }
931 /** @since 3.3.0 add extension classes */
932 if(undefined !== cf7sgCustomHelperModule[tag]){
933 let ch = cf7sgCustomHelperModule[tag](search);
934 if(Array.isArray(ch.php) && ch.php.length>0) helpers = helpers.concat(ch.php);
935 if(Array.isArray(ch.js) && ch.js.length>0) jsHelpers = jsHelpers.concat(ch.js);
936 }
937 type[type.length] = tag;
938 fields[fields.length] = field;
939 hooks[hooks.length] = helpers;
940 jshooks[jshooks.length] = jsHelpers;
941 if('*' === match[1][match[1].length -1]){
942 isRequired = true;
943 }
944 if(!stopSearch) match = cf7TagRegexp.exec(search); //get the next match.
945 }
946 var classes = $('#grid-col div.cf7-field-type').attr('class');
947 classes += " "+ type.join(' ');
948 field = fields.join(' ');
949 // $parent.removeClass('required');
950 if(isRequired) classes += ' required';//$parent.addClass('required').
951 var $parentColumn = $parent.closest('.columns');
952 if($parentColumn.is('[class*="cf7-tags-"]')){
953 $parentColumn.removeClass(function (index, className) {
954 return (className.match (/(^|\s)cf7-tags-\S+/g) || []).join(' ');
955 });
956 }
957 if( (count-counth) >1 ){ /** @since 3.2.1 don't count hidden fields */
958 classes += ' cf7-tags-'+count;
959 $parentColumn.addClass('cf7-tags-'+count);
960 }
961 $parent.attr('class',classes);
962 /**@since 2.0.0
963 * setup fields for tag specific filters/actions.
964 */
965 //for each tag get corresponding set of filters.
966 let helperUsed = false, jsHelperUsed=false,len = type.length;
967 search = '';
968 for (let i = 0; i < len; i++) {
969 for (let j=0, jlen = hooks[i].length; j<jlen; j++){
970 search += 'li.'+hooks[i][j]+',';
971 }
972 search = search.slice(0,-1); //remove last ','
973 if($( search ,$('#fieldhelperdiv')).length>0){
974 //this tag has some filters.
975 if(helperUsed){
976 var $clone = $helper.clone();
977 $helper.after($clone);
978 $helper = $clone;
979 }
980 helperUsed = true;
981 $helper.attr('data-field', fields[i]);
982 $helper.attr('data-tag', type[i]);
983 $helper.attr('data-search', search);
984 $helper.show();
985 }
986 //js helpers.
987 search='', jlen = jshooks[i].length;
988 for (let j=0; j<jlen; j++){
989 search += 'li.'+jshooks[i][j]+',';
990 }
991 search = search.slice(0,-1); //remove last ','
992 if($( search ,$('#fieldhelperjs')).length>0){
993 //this tag has some filters.
994 if(jsHelperUsed){
995 var $clone = $jshelper.clone();
996 $jshelper.after($clone);
997 $jshelper = $clone;
998 }
999 jsHelperUsed = true;
1000 $jshelper.attr('data-field', fields[i]);
1001 $jshelper.attr('data-tag', type[i]);
1002 $jshelper.attr('data-search', search);
1003 $jshelper.show();
1004 }
1005 }
1006 if(isSubmit){
1007 $parent.parent().addClass('submit-field');
1008 }
1009
1010 return label;
1011 }
1012 $.fn.updateGridForm = function(){
1013 var $this = $(this);
1014 if(!$this.is('textarea.grid-input')){
1015 return $this;
1016 }
1017 //label
1018 var $label = $this.siblings('div.cf7-field-label').find(':input');
1019 var label = $label.val();
1020 //field
1021 var field = $this.siblings('div.cf7-field-type').find('textarea').val();
1022 var idx = 0;
1023 if(cf7grid.requiredHTML.length>0) idx=label.indexOf(cf7grid.requiredHTML)
1024 if($this.siblings('div.cf7-field-type').is('.required')){
1025 /** @since 2.10.4 fix for custom manual labels, allow replacement with empty span*/
1026 if(idx<0) idx = label.search(/<span>[\w\W]*<\/span>/g);
1027 if(idx<0){
1028 label += cf7grid.requiredHTML;
1029 $label.val(label);//input field.
1030 $label.siblings('p.content').html(label);
1031 }
1032 }else{
1033 if(idx>=0){
1034 label = label.replace(cf7grid.requiredHTML, '');
1035 $label.val(label);//input field.
1036 $label.siblings('p.content').html(label);
1037 }
1038 }
1039 //tip
1040 var tip = $this.siblings('div.cf7-field-tip').find(':input').val();
1041 var $cell = $('<div>').append( cf7grid.preHTML + field + cf7grid.postHTML );
1042 $('label', $cell).html(label);
1043 $('.info-tip', $cell).html(tip);
1044 //update grid input and trigger change to udpate form
1045 if(cf7grid.ui) $this.html($cell.html()+'\n').trigger('change');
1046 else $this.val($cell.html()).trigger('change');
1047 return $this;
1048 };
1049
1050 $.fn.toggleSiblingUIFields = function(){
1051 var $this = $(this);
1052 if(!$this.is('div.cf7-field-inner')){
1053 return $this;
1054 }
1055 $this.siblings('div.cf7-field-inner').each(function(){
1056 var $this = $(this);
1057 $('p.content', $this).show();
1058 $(':input', $this).hide().attr('id','');
1059 $('span.dashicons', $this).hide();
1060 });
1061 }
1062 $.fn.getRowSize = function(){
1063 var size, off, idx, foundSize, classList;
1064 var total = 0;
1065 var sizes = [0];
1066 $(this).children('.columns').each(function(index){
1067 classList = $(this).attr('class').split(/\s+/);
1068 foundSize = false;
1069 for(idx=0;idx<classList.length; idx++){
1070 size = $.inArray(classList[idx], columnsizes);
1071 off = $.inArray(classList[idx], offsets);
1072 if(size > -1){
1073 foundSize = true;
1074 sizes[index] = size;
1075 total += size + 1;
1076 }
1077 if(off > -1) total+= off+1;
1078 }
1079 if(!foundSize){ //by default a colum which is not set set is treated as 1
1080 sizes[index] = 0;
1081 total += 1;
1082 }
1083 });
1084 return {'length':total, 'cols':sizes};
1085 }
1086 $.fn.getColumnTotalSize = function(){
1087 var $this = $(this);
1088 if(! $this.is('.columns')){
1089 return 0;
1090 }
1091 var off, foundSize, size = 0;
1092 var total = 0;
1093 var classList = $this.attr('class').split(/\s+/);
1094 var $sizes = $this.find('.grid-column select.column-size');
1095 var $offsets = $this.find('.grid-column select.column-offset');
1096 $offsets.val('');
1097 $sizes.val('one');
1098 foundSize = false;
1099 for(var idx=0;idx<classList.length; idx++){
1100 if(!foundSize){
1101 size = $.inArray(classList[idx], columnsizes);
1102 if(size > -1){
1103 foundSize = true;
1104 total += size + 1;
1105 //reset select
1106 $sizes.val(classList[idx]);
1107 }
1108 }
1109 off = $.inArray(classList[idx], offsets);
1110 if(off > -1){
1111 total += off+1;
1112 $offsets.val(classList[idx]);
1113 }
1114 }
1115 if(!foundSize){ //by default a colum which is not set set is treated as 1
1116 size = 0; //by default a colum which is not set set is treated as 1
1117 total += 1;
1118 }
1119 return {'length':total, 'size':size};
1120 }
1121 //add new rows
1122 $.fn.insertNewRow = function(areaCode){
1123 var $this = $(this);
1124 if(typeof areaCode === 'undefined') areaCode ='';
1125 var append=true;
1126 switch(true){
1127 case ( $this.is('.columns') || $this.is($grid)):
1128 case $this.is('.container.cf7-sg-tabs-panel'): /* fixes rows added in panels*/
1129 append=true;
1130 break;
1131 case ($this.is('.container') || $this.is('.cf7sg-external-form') ):
1132 append=false;
1133 break;
1134 default: //unknown element, maybe an error.
1135 return $this;
1136 }
1137
1138 var $newRow = $( $('#grid-row').html() );
1139 //append the column controls and textarea
1140 $('.columns', $newRow).append( $($('#grid-col').html()) );
1141 //append the new row to the column or container
1142 if(append) $this.append($newRow);
1143 else $this.after($newRow);
1144 //is areaCode text or jQuery object?
1145 if(areaCode instanceof jQuery){
1146 $('.cf7-field-inner', $newRow).remove();
1147 $('textarea.grid-input',$newRow).remove();
1148 $('.columns', $newRow).append(areaCode);
1149 }else{
1150 //add the code to the textarea
1151 if(cf7grid.ui){
1152 $('textarea.grid-input',$newRow).html(areaCode).hide();//.trigger('change');
1153 $newRow.html2gui(areaCode);
1154 }else{
1155 $('textarea.grid-input',$newRow).val(areaCode);//.trigger('change');
1156 $('div.cf7-field-inner', $newRow).hide();
1157 }
1158 }
1159 //make new row's columns sortable.
1160 sortableRows($newRow);
1161 return $this;
1162 }
1163 //refresh controls select
1164 $.fn.changeColumnSize = function(oldSize, newSize){
1165 var $this = $(this);
1166 if(oldSize.length > 0) $this.removeClass(oldSize);
1167 $this.addClass(newSize);
1168 $('.column-size option[value="'+newSize+'"]', $this ).prop('selected', true);
1169 }
1170 //$target.closest('.grid-controls').filterColumnControls();
1171 $.fn.filterColumnControls = function(){
1172 var $this = $(this);
1173 if(!$this.is('.grid-controls')){
1174 return $this;
1175 }
1176 //enable all options
1177 $('.column-size option', $this ).prop('disabled', false);
1178 $('.column-offset option', $this ).prop('disabled', false);
1179 var $parentRow = $this.closest('.row');
1180 var $parentColumn = $this.closest('.columns');
1181 var row = $parentRow.getRowSize();
1182 var col = $parentColumn.getColumnTotalSize();
1183 var idx, start, free = 0;
1184 if(row.length < 12) free = (12 - row.length);
1185 for(idx = start = col.size+1; idx < columnsizes.length; idx++){
1186 if( idx > (free + start - 1) ){
1187 $('.column-size option[value="'+columnsizes[idx]+'"]', $this ).prop('disabled', true);
1188 }
1189 }
1190 for(idx = start = col.length - col.size - 1 ;idx< offsets.length; idx++){
1191 if( idx > (free + start - 1) ){
1192 $('.column-offset option[value="'+offsets[idx]+'"]', $this ).prop('disabled', true);
1193 }
1194 }
1195 return $this;
1196 }
1197
1198 })( jQuery );
1199
1200 /** @since 3.3.0 custom helpers */
1201 var cf7sgCustomHelperModule = (function (cch) {
1202
1203 return cch;
1204 }(cf7sgCustomHelperModule || {}));
1205