| 1 |
/** |
| 2 |
Javascript to handle Codemirror editor |
| 3 |
Event 'cf7sg-form-change' fired on #contact-form-editor element when codemirror changes occur |
| 4 |
@since 3.1.2 introduce instaiated cm editor as attribute in anonymous function. |
| 5 |
*/ |
| 6 |
(function( $, cme ) { |
| 7 |
|
| 8 |
$(document).ready( function(){ |
| 9 |
var $codemirror = $('#cf7-codemirror'); |
| 10 |
var $wpcf7Editor = $('textarea#wpcf7-form-hidden'); |
| 11 |
var codemirrorUpdated = false; |
| 12 |
var $grid = $('#grid-form'); |
| 13 |
var gridTab = '#cf7-editor-grid'; //default at load time. |
| 14 |
//set codemirror editor value; |
| 15 |
cme.setValue($wpcf7Editor.text()); |
| 16 |
|
| 17 |
$wpcf7Editor.on('grid-ready', function(){ //------ setup the codemirror editor |
| 18 |
//codemirror editor |
| 19 |
CodeMirror.defineMode("shortcode", function(config, parserConfig) { |
| 20 |
var cf7Overlay = { |
| 21 |
token: function(stream, state) { |
| 22 |
var ch; |
| 23 |
if (stream.match(/^\[([a-zA-Z0-9_]+)\*?\s?/)) { |
| 24 |
while ((ch = stream.next()) != null) |
| 25 |
if (ch == "]" ) { |
| 26 |
//stream.eat("]"); |
| 27 |
return "shortcode"; |
| 28 |
} |
| 29 |
} |
| 30 |
while (stream.next() != null && !stream.match(/^\[([a-zA-Z0-9_]+)\*?\s?/, false)) {} |
| 31 |
return null; |
| 32 |
} |
| 33 |
}; |
| 34 |
return CodeMirror.overlayMode(CodeMirror.getMode(config, parserConfig.backdrop || "htmlmixed"), cf7Overlay); |
| 35 |
}); |
| 36 |
// var cmConfig = |
| 37 |
if(cf7sgeditor.mode.length>0) cme.setOption('mode',cf7sgeditor.mode); |
| 38 |
if(cf7sgeditor.theme.length>0) cme.setOption('theme',cf7sgeditor.theme); |
| 39 |
// cmEditor = CodeMirror( $codemirror.get(0), cmConfig); |
| 40 |
|
| 41 |
/* TODO: enable shortcode edit at a future date |
| 42 |
$('.cm-shortcode',$codemirror).each(function(){ |
| 43 |
$(this).append('<span class="dashicons dashicons-edit"></span>'); |
| 44 |
}); |
| 45 |
$('.dashicons-edit', $codemirror).on('click', function(){ |
| 46 |
alert('shortcode '+ $(this).parent().text()); |
| 47 |
}); |
| 48 |
*/ |
| 49 |
$.fn.beautify = function(cursor){ |
| 50 |
cme.setSelection({ |
| 51 |
'line':cme.firstLine(), |
| 52 |
'ch':0, |
| 53 |
'sticky':null |
| 54 |
},{ |
| 55 |
'line':cme.lastLine(), |
| 56 |
'ch':0, |
| 57 |
'sticky':null |
| 58 |
}, |
| 59 |
{scroll: false}); |
| 60 |
cme.indentSelection("smart"); |
| 61 |
cme.setCursor(cme.firstLine(),0); |
| 62 |
if('undefined' != typeof cursor && cursor.find(false)){ |
| 63 |
var from = cursor.from(); |
| 64 |
var to = cursor.to(); |
| 65 |
cme.setSelection(CodeMirror.Pos(from.line, 0), to); |
| 66 |
cme.scrollIntoView({from: from, to: CodeMirror.Pos(to.line + 10, 0)}); |
| 67 |
} |
| 68 |
} |
| 69 |
$codemirror.beautify(); |
| 70 |
|
| 71 |
//var cur = cme.getCursor(); |
| 72 |
//cme.setCursor(99, cur.ch); |
| 73 |
|
| 74 |
cme.on('changes', function(){ |
| 75 |
codemirrorUpdated = true; |
| 76 |
var disabled = $('#form-editor-tabs').tabs('option','disabled'); |
| 77 |
|
| 78 |
if(true===disabled){ |
| 79 |
var changes = $('<div>').append(cme.getValue()); |
| 80 |
if(0===changes.children().length || changes.children('.container').length>0){ |
| 81 |
$('#form-editor-tabs').tabs('option',{disabled:false}); |
| 82 |
/** |
| 83 |
* @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms. |
| 84 |
*/ |
| 85 |
$('#is-cf7sg-form').val('true'); |
| 86 |
} |
| 87 |
} |
| 88 |
|
| 89 |
$('#contact-form-editor').trigger('cf7sg-form-change'); |
| 90 |
}); |
| 91 |
|
| 92 |
//create tabs |
| 93 |
$('#form-editor-tabs').tabs({ |
| 94 |
beforeActivate: function (event, ui){ |
| 95 |
//update the codemirror panel |
| 96 |
if('#cf7-codemirror' == ui.newPanel.selector){ |
| 97 |
//finalise any changes in the grid form editor |
| 98 |
$grid.on('cf7grid-form-ready', function(){ |
| 99 |
var code = $grid.CF7FormHTML(); |
| 100 |
if($grid.children('.container').length > 0){ //beautify. |
| 101 |
code = html_beautify(code , |
| 102 |
{ |
| 103 |
'indent_size': 2, |
| 104 |
'wrap_line_length': 0 |
| 105 |
}); |
| 106 |
} |
| 107 |
cme.setValue(code); |
| 108 |
//reset the codemirror change flag |
| 109 |
codemirrorUpdated = false; |
| 110 |
//remove id from textarea |
| 111 |
$('textarea#wpcf7-form').attr('id', ''); |
| 112 |
//change the wpcf7 textarea |
| 113 |
$('textarea.codemirror-cf7-update', $codemirror).attr('id', 'wpcf7-form'); |
| 114 |
//setup the form code in the hidden textarea |
| 115 |
$wpcf7Editor.html(code); |
| 116 |
}); |
| 117 |
/** @since 2.8.3 clear the codemirror textarea##wpcf7-form */ |
| 118 |
$('textarea.codemirror-cf7-update', $codemirror).val(''); |
| 119 |
$grid.trigger('cf7grid-form-finalise'); |
| 120 |
}else{ |
| 121 |
//remove id from textarea |
| 122 |
$('textarea#wpcf7-form').attr('id', ''); |
| 123 |
} |
| 124 |
}, |
| 125 |
activate: function( event, ui ) { |
| 126 |
gridTab = ui.newPanel.selector; |
| 127 |
if('#cf7-editor-grid' == ui.newPanel.selector){ |
| 128 |
if(codemirrorUpdated){ |
| 129 |
//update the hidden textarea |
| 130 |
$wpcf7Editor.text(cme.getValue()); |
| 131 |
//trigger rebuild grid event |
| 132 |
$grid.trigger('build-grid'); |
| 133 |
}else{ //try to set the focus on the 'cf7sgfocus element' |
| 134 |
var $focus = $('.cf7sgfocus', $grid); |
| 135 |
if($focus.length>0){ |
| 136 |
var scrollPos = $focus.offset().top - $(window).height()/2 + $focus.height()/2; |
| 137 |
//console.log(scrollPos); |
| 138 |
$(window).scrollTop(scrollPos); |
| 139 |
$focus.removeClass('cf7sgfocus'); |
| 140 |
} |
| 141 |
} |
| 142 |
}else if('#cf7-codemirror' == ui.newPanel.selector){ |
| 143 |
var cursor = cme.getSearchCursor('cf7sgfocus', CodeMirror.Pos(cme.firstLine(), 0), {caseFold: true, multiline: true}); |
| 144 |
|
| 145 |
$codemirror.beautify(cursor); |
| 146 |
|
| 147 |
if($grid.children('.container').length>0){ |
| 148 |
var scrollPos = $('#form-panel').offset().top; |
| 149 |
$(window).scrollTop(scrollPos); |
| 150 |
} |
| 151 |
} |
| 152 |
} |
| 153 |
}); |
| 154 |
/*@since 1.1.1 disable grid editor for existing cf7 forms*/ |
| 155 |
if(0==$grid.children('.container').length){ |
| 156 |
$('#form-editor-tabs').tabs('option',{ active:1, disabled:true}); |
| 157 |
/** |
| 158 |
* @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms. |
| 159 |
*/ |
| 160 |
$('#is-cf7sg-form').val('false'); |
| 161 |
} |
| 162 |
|
| 163 |
//update the codemirror when tags are inserted |
| 164 |
$('form.tag-generator-panel .button.insert-tag').on('click', function(){ |
| 165 |
var $textarea = $('textarea#wpcf7-form'); |
| 166 |
if($textarea.is('.codemirror-cf7-update')){ |
| 167 |
var tag = $textarea.delay(100).val(); |
| 168 |
cme.replaceSelection(tag); |
| 169 |
//update codemirror. |
| 170 |
$textarea.val(''); //clear. |
| 171 |
} |
| 172 |
}); |
| 173 |
}); //-----------end codemirror editor setup |
| 174 |
|
| 175 |
$('form#post').submit(function(event) { |
| 176 |
var $this = $(this); |
| 177 |
$( window ).off( 'beforeunload' ); //remove event to stop cf7 script from warning on save |
| 178 |
event.preventDefault(); //this will prevent the default submit |
| 179 |
var $embdedForms = ''; |
| 180 |
var $formNoEmbeds = ''; |
| 181 |
var codeMirror =''; |
| 182 |
if('#cf7-editor-grid' == gridTab){ //set up the code in the cf7 textarea |
| 183 |
var $txta = $('textarea#wpcf7-form'); |
| 184 |
$txta.html($txta.val()+'\n'); |
| 185 |
codeMirror = html_beautify( |
| 186 |
$grid.CF7FormHTML(), |
| 187 |
{ |
| 188 |
'indent_size': 2, |
| 189 |
'wrap_line_length': 0 |
| 190 |
} |
| 191 |
); |
| 192 |
$('textarea#wpcf7-form-hidden').html(codeMirror); |
| 193 |
$formNoEmbeds = $('<div>').append(codeMirror); |
| 194 |
}else{//we are in text mode. |
| 195 |
codeMirror = cme.getValue(); |
| 196 |
$('textarea#wpcf7-form-hidden').html(codeMirror).val(codeMirror); |
| 197 |
$formNoEmbeds = $('<div>').append(codeMirror); |
| 198 |
} |
| 199 |
//since 1.8 remove cf7sgfocus class if present. |
| 200 |
$('.cf7sgfocus', $formNoEmbeds).removeClass('cf7sgfocus'); |
| 201 |
$embdedForms = $formNoEmbeds.find('.cf7sg-external-form').remove(); |
| 202 |
//setup sub-forms hidden field. |
| 203 |
var embeds = []; |
| 204 |
var hasTables = false, hasTabs = false, hasToggles=false; |
| 205 |
if($embdedForms.length>0){ |
| 206 |
$embdedForms.each(function(){ |
| 207 |
embeds[embeds.length] = $(this).data('form'); |
| 208 |
}); |
| 209 |
} |
| 210 |
$('#cf7sg-embeded-forms').val(JSON.stringify(embeds)); |
| 211 |
var cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s]*)(|\s*(.[^\[]*))\]/img; |
| 212 |
/** @since 3.0.0 determine scripts required */ |
| 213 |
var scriptClass =""; |
| 214 |
if(codeMirror.indexOf("class:sgv-")>0) scriptClass += "has-validation,"; |
| 215 |
if(codeMirror.indexOf("class:select2")>0) scriptClass += "has-select2,"; |
| 216 |
if(codeMirror.indexOf("class:nice-select")>0) scriptClass += "has-nice-select,"; |
| 217 |
if($('.cf7sg-collapsible', $formNoEmbeds).length>0) scriptClass += "has-accordion,"; |
| 218 |
if(codeMirror.indexOf("[benchmark")>0) scriptClass += "has-benchmark,"; |
| 219 |
if(codeMirror.indexOf("[date")>0 || 0<codeMirror.search(/\[text([^\]]+?)class:datepicker/ig)) scriptClass += "has-date,"; |
| 220 |
|
| 221 |
//scan and submit tabs & tables fields. |
| 222 |
var tableFields = []; |
| 223 |
$('.row.cf7-sg-table', $formNoEmbeds).each(function(){ |
| 224 |
/**@since 2.4.2 track each tables with unique ids and their fields*/ |
| 225 |
var unique = $(this).closest('.container.cf7-sg-table').attr('id'); |
| 226 |
var fields = {}; |
| 227 |
fields[unique]=[]; |
| 228 |
var search = $(this).html(); |
| 229 |
var match = cf7TagRegexp.exec(search); |
| 230 |
//console.log('search:'+search); |
| 231 |
while (match != null) { |
| 232 |
//ttFields[ match[2] ] = match[1]; |
| 233 |
fields[unique][fields[unique].length] = match[2]; |
| 234 |
//console.log('match'+match[2]); |
| 235 |
match = cf7TagRegexp.exec(search); //get the next match. |
| 236 |
} |
| 237 |
tableFields[tableFields.length] = fields; |
| 238 |
hasTables = true; |
| 239 |
}); |
| 240 |
//var cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s]*)\s*(.[^\[]*)\]/img; |
| 241 |
var tabFields = [],toggleInTabs=[]; |
| 242 |
$('.container.cf7-sg-tabs-panel', $formNoEmbeds).each(function(){ |
| 243 |
/**@since 2.4.2 track each tables with unique ids and their fields*/ |
| 244 |
$tab = $(this), unique = $tab.attr('id'),fields = {}, search = $tab.html(); |
| 245 |
fields[unique]=[]; |
| 246 |
var match = cf7TagRegexp.exec(search); |
| 247 |
while (match != null) { |
| 248 |
//if( -1 === tableFields.indexOf(match[2]) ) /*removed as now want to idenify fields which are both tabs and table fields*/ |
| 249 |
fields[unique][fields[unique].length] = match[2]; |
| 250 |
//ttFields[match[2]] = match[1]; |
| 251 |
match = cf7TagRegexp.exec(search); //get the next match. |
| 252 |
} |
| 253 |
tabFields[tabFields.length] = fields; |
| 254 |
hasTabs = true; |
| 255 |
/** @since 3.3.5 track toggled sections in tabs */ |
| 256 |
$('.container.cf7sg-collapsible.with-toggle',$tab).each(function(){ |
| 257 |
toggleInTabs[toggleInTabs.length]=$(this).attr('id'); |
| 258 |
}); |
| 259 |
}); |
| 260 |
/** |
| 261 |
* Track toggled fields to see if they are submitted or not. |
| 262 |
* @since 2.5 */ |
| 263 |
|
| 264 |
var toggledFields = []; |
| 265 |
$('.container.cf7sg-collapsible.with-toggle', $formNoEmbeds).each(function(){ |
| 266 |
/**@since 2.4.2 track each tables with unique ids and their fields*/ |
| 267 |
var unique = $(this).attr('id'); |
| 268 |
var fields = {}; |
| 269 |
fields[unique]=[]; |
| 270 |
var search = $(this).html(); |
| 271 |
var match = cf7TagRegexp.exec(search); |
| 272 |
while (match != null) { |
| 273 |
//if( -1 === tableFields.indexOf(match[2]) ) /*removed as now want to idenify fields which are both tabs and table fields*/ |
| 274 |
fields[unique][fields[unique].length] = match[2]; |
| 275 |
//ttFields[match[2]] = match[1]; |
| 276 |
match = cf7TagRegexp.exec(search); //get the next match. |
| 277 |
} |
| 278 |
toggledFields[toggledFields.length] = fields; |
| 279 |
hasToggles = true; |
| 280 |
}); |
| 281 |
//append hidden fields |
| 282 |
$this.append('<input type="hidden" name="cf7sg-has-tabs" value="'+hasTabs+'" /> '); |
| 283 |
$this.append('<input type="hidden" name="cf7sg-has-tables" value="'+hasTables+'" /> '); |
| 284 |
$this.append('<input type="hidden" name="cf7sg-has-toggles" value="'+hasToggles+'" /> '); |
| 285 |
var disabled = $('#form-editor-tabs').tabs('option','disabled'); |
| 286 |
$this.append('<input type="hidden" name="cf7sg-has-grid" value="'+disabled+'" /> '); |
| 287 |
/** @since 3.3.5 track toggles in tabs */ |
| 288 |
$this.append('<input type="hidden" name="cf7sg-toggle-in-tabs" id="cf7sg-tggl-tabs" /> '); |
| 289 |
$('#cf7sg-tggl-tabs',$this).val(JSON.stringify(toggleInTabs)); |
| 290 |
//update script classes since v3. |
| 291 |
if(hasTabs) scriptClass+="has-tabs,"; |
| 292 |
if(hasTables) scriptClass+="has-table,"; |
| 293 |
if(hasToggles) scriptClass+="has-toggles,"; |
| 294 |
if(!disabled) scriptClass+="has-grid,"; |
| 295 |
$this.append('<input type="hidden" name="cf7sg-script-classes" value="'+scriptClass+'" />'); |
| 296 |
|
| 297 |
$('#cf7sg-tabs-fields').val(JSON.stringify(tabFields)); |
| 298 |
$('#cf7sg-table-fields').val(JSON.stringify(tableFields)); |
| 299 |
$('#cf7sg-toggle-fields').val(JSON.stringify(toggledFields)); |
| 300 |
|
| 301 |
//alert(ttFields); |
| 302 |
// continue the submit unbind preventDefault. |
| 303 |
$this.unbind('submit').submit(); |
| 304 |
}); |
| 305 |
/* |
| 306 |
Function to convert the UI form into its html final form for editing in the codemirror and/or saving to the CF7 plugin. |
| 307 |
*/ |
| 308 |
$.fn.CF7FormHTML = function(){ |
| 309 |
var $this = $(this); |
| 310 |
if( !$this.is('#grid-form') ){ |
| 311 |
return ''; |
| 312 |
} |
| 313 |
var $form = $('<div>').append( $this.html() ); |
| 314 |
var text=''; |
| 315 |
//remove the external forms |
| 316 |
var external = {}; |
| 317 |
$('.cf7sg-external-form', $form).each(function(){ |
| 318 |
var $exform = $(this); |
| 319 |
var id = $exform.data('form'); |
| 320 |
external[id] = $exform.children('.cf7sg-external-form-content').remove(); |
| 321 |
$exform.children('.form-controls').remove(); |
| 322 |
}); |
| 323 |
//remove the row controls |
| 324 |
$('.row', $form).removeClass('ui-sortable').children('.row-controls').remove(); |
| 325 |
|
| 326 |
//remove the collapsible input |
| 327 |
$('.container.cf7sg-collapsible', $form).each(function(){ |
| 328 |
var $this = $(this); |
| 329 |
var cid = $this.attr('id'); |
| 330 |
var $title = $this.children('.cf7sg-collapsible-title'); |
| 331 |
var text = $title.children('label').children('input[type="hidden"]').val(); |
| 332 |
$title.children('label').remove(); |
| 333 |
var toggle = ''; |
| 334 |
if($this.is('.with-toggle')){ |
| 335 |
toggle=' toggled'; |
| 336 |
} |
| 337 |
text = '<span class="cf7sg-title'+toggle+'">'+text+'</span>'; |
| 338 |
$title.html(text + $title.html()); |
| 339 |
}); |
| 340 |
//remove tabs inputs |
| 341 |
$('ul.cf7-sg-tabs-list li label', $form).remove(); |
| 342 |
|
| 343 |
var cf7TagRegexp = /\[(.[^\s]*)\s*(.[^\s]*)(|\s*(.[^\[]*))\]/img; |
| 344 |
var cf7sgToggleRegex = /class:cf7sg-toggle-(.[^\s]+)/i; |
| 345 |
//remove textarea and embed its content |
| 346 |
$('.columns', $form).each(function(){ |
| 347 |
var $this = $(this); |
| 348 |
$this.removeClass('ui-sortable'); |
| 349 |
var $gridCol = $this.children('.grid-column'); |
| 350 |
var $text = $('textarea.grid-input', $gridCol); |
| 351 |
if($text.length>0){ |
| 352 |
text = $text.text(); |
| 353 |
//verify if this column is within a toggled section. |
| 354 |
var $toggle = $this.closest('.container.cf7sg-collapsible.with-toggle'); |
| 355 |
if($toggle.length>0){ |
| 356 |
var cid = $toggle.attr('id'); |
| 357 |
/** |
| 358 |
* track toggled checkbox/radio fields, because they are not submitted when not filled. |
| 359 |
*@since 2.1.5 |
| 360 |
*/ |
| 361 |
var $field = $text.siblings('div.cf7-field-type'); |
| 362 |
var isToggled = false; |
| 363 |
if($field.length>0){ |
| 364 |
if($field.is('.checkbox.required') || $field.is('.radio') || $field.is('.file.required')) isToggled = true; |
| 365 |
}else isToggled = true; //custom column, needs checking. |
| 366 |
if(isToggled){ |
| 367 |
var search = text; |
| 368 |
var match = cf7TagRegexp.exec(search); |
| 369 |
//var hasRadios = false; |
| 370 |
while (match != null) { |
| 371 |
switch(match[1]){ |
| 372 |
case 'checkbox*': |
| 373 |
case 'radio': |
| 374 |
case 'file*': |
| 375 |
var options = ''; |
| 376 |
if(match.length>4){ |
| 377 |
var tglmatch = cf7sgToggleRegex.exec(match[4]); |
| 378 |
if(tglmatch != null){ |
| 379 |
if(tglmatch[1] == cid) break; |
| 380 |
options =match[4].replace(tglmatch[0],'class:cf7sg-toggle-'+cid); |
| 381 |
}else{ |
| 382 |
options = 'class:cf7sg-toggle-'+cid+' '+match[4]; |
| 383 |
} |
| 384 |
}else{ |
| 385 |
options = 'class:cf7sg-toggle-'+cid; |
| 386 |
} |
| 387 |
text = text.replace(match[0], '['+match[1]+' '+match[2]+' '+options+']'); |
| 388 |
//hasRadios = true; |
| 389 |
break; |
| 390 |
} |
| 391 |
//console.log('match'+match[2]); |
| 392 |
match = cf7TagRegexp.exec(search); //get the next match. |
| 393 |
} |
| 394 |
}//end isToggled. |
| 395 |
} |
| 396 |
$this.html('\n'+text); |
| 397 |
}//else this column is a grid. |
| 398 |
$gridCol.remove(); |
| 399 |
}); |
| 400 |
//reinsert the external forms |
| 401 |
$('.cf7sg-external-form', $form).each(function(){ |
| 402 |
var $this = $(this); |
| 403 |
var id = $this.data('form'); |
| 404 |
$this.append( external[id] ); |
| 405 |
}); |
| 406 |
text = $form.html(); |
| 407 |
if($grid.children('.container').length > 0){ //strip tabs/newlines |
| 408 |
text = text.replace(/^(?:[\t ]*(?:\r?\n|\r))+/gm, ""); |
| 409 |
} |
| 410 |
return text; |
| 411 |
} |
| 412 |
});//dcoument ready end |
| 413 |
|
| 414 |
})( jQuery, codeMirror_5_32); |
| 415 |
|