PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.4
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.4
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / js / admin / embed.js

embed.js in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 5.4, at js/admin/embed.js

505 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function() {
2 /** globals ajaxurl, wp, frmDom */
3
4 if ( 'undefined' === typeof ajaxurl || 'undefined' === typeof wp || 'undefined' === typeof frmDom ) {
5 return;
6 }
7
8 const __ = wp.i18n.__;
9 const { div, tag, svg } = frmDom;
10 const { maybeCreateModal, footerButton } = frmDom.modal;
11
12 let autoId = 0;
13 let modal;
14
15 const state = {
16 type: 'form',
17 objectId: 0,
18 objectKey: ''
19 };
20
21 initEmbedModal();
22
23 function initEmbedModal() {
24 document.addEventListener( 'click', listenForEmbedClick );
25 }
26
27 function listenForEmbedClick( event ) {
28 let clicked = false;
29
30 const element = event.target;
31 const tag = element.tagName.toLowerCase();
32
33 switch ( tag ) {
34 case 'a':
35 clicked = 'frm-embed-action' === element.id || element.classList.contains( 'frm-embed-form' );
36
37 if ( clicked ) {
38 state.type = 'form';
39 } else {
40 clicked = element.classList.contains( 'frm-embed-view' );
41 if ( clicked ) {
42 state.type = 'view';
43 }
44 }
45 break;
46
47 case 'svg':
48 clicked = element.parentNode.classList.contains( 'frm-embed-form' );
49 if ( clicked ) {
50 state.type = 'form';
51 }
52 break;
53 }
54
55 if ( clicked ) {
56 event.preventDefault();
57
58 const hookName = 'frm_before_embed_modal';
59 const initialIds = [ 0, 0 ];
60 const hookArgs = {
61 element,
62 type: state.type
63 };
64 const [ objectId, objectKey ] = wp.hooks.applyFilters( hookName, initialIds, hookArgs );
65
66 if ( objectId && objectKey ) {
67 state.objectId = objectId;
68 state.objectKey = objectKey;
69 openEmbedModal();
70 }
71 }
72 }
73
74 function openEmbedModal() {
75 /* translators: %s type: ie form, view. */
76 const title = __( 'Embed %s', 'formidable' ).replace( '%s', getTypeDescription() );
77
78 modal = maybeCreateModal(
79 'frm_embed_modal',
80 {
81 title,
82 content: getModalOptions(),
83 footer: getModalFooter()
84 }
85 );
86 modal.classList.add( 'frm_common_modal' );
87 modal.classList.remove( 'frm-on-page-2' );
88
89 const $modal = jQuery( modal );
90 offsetModalY( $modal, '50px' );
91 $modal.parent().addClass( 'frm-embed-modal-wrapper' );
92 }
93
94 function getModalFooter() {
95 const doneButton = footerButton({
96 text: __( 'Done', 'formidable' ),
97 buttonType: 'primary'
98 });
99
100 const cancelButton = footerButton({
101 text: __( 'Back', 'formidable' ),
102 buttonType: 'cancel'
103 });
104 cancelButton.addEventListener(
105 'click',
106 function( event ) {
107 event.preventDefault();
108 openEmbedModal();
109 }
110 );
111
112 return div({
113 children: [ doneButton, cancelButton ]
114 });
115 }
116
117 function offsetModalY( $modal, amount ) {
118 const position = {
119 my: 'top',
120 at: 'top+' + amount,
121 of: window
122 };
123 $modal.dialog( 'option', 'position', position );
124 }
125
126 function getModalOptions() {
127 const content = div({ className: 'frm-embed-modal-content frm_wrap' });
128 const typeDescription = getTypeDescription();
129
130 /* translators: %s type: ie form, view. */
131 const existingPageDescription = __( 'Embed your %s into an existing page.', 'formidable' ).replace( '%s', typeDescription );
132
133 /* translators: %s type: ie form, view. */
134 const newPageDescription = __( 'Put your %s on a newly created page.', 'formidable' ).replace( '%s', typeDescription );
135
136 /* translators: %s type: ie form, view. */
137 const insertManuallyDescription = __( 'Use WP shortcodes or PHP code to put the %s in any place.', 'formidable' ).replace( '%s', typeDescription );
138
139 const options = [
140 {
141 icon: '#frm_file_icon',
142 label: __( 'Select existing page', 'formidable' ),
143 description: existingPageDescription,
144 callback: () => {
145 content.innerHTML = '';
146
147 const spinner = tag( 'span' );
148 spinner.className = 'frm-wait frm_spinner';
149 spinner.style.visibility = 'visible';
150 content.appendChild( spinner );
151
152 const gap = div();
153 gap.style.height = '20px';
154 content.appendChild( gap );
155
156 content.classList.add( 'frm-loading-page-options' );
157
158 jQuery.ajax({
159 type: 'POST',
160 url: ajaxurl,
161 data: {
162 action: 'get_page_dropdown',
163 nonce: frmGlobal.nonce
164 },
165 dataType: 'json',
166 success: addExistingPageDropdown
167 });
168
169 function addExistingPageDropdown( response ) {
170 if ( 'object' !== typeof response || 'string' !== typeof response.html ) {
171 return;
172 }
173
174 content.classList.remove( 'frm-loading-page-options' );
175 content.innerHTML = '';
176
177 const typeDescription = getTypeDescription();
178
179 /* translators: %s: type (ie. view, form). */
180 const titleText = __( 'Select the page you want to embed your %s into.', 'formidable' ).replace( '%s', typeDescription );
181
182 const title = getLabel( titleText );
183 title.setAttribute( 'for', 'frm_page_dropdown' );
184 content.appendChild( title );
185
186 let editPageUrl;
187
188 doneButton = modal.querySelector( '.frm_modal_footer .button-primary' );
189 doneButton.classList.remove( 'dismiss' );
190 /* translators: %s: type (ie. view, form). */
191 doneButton.textContent = __( 'Insert %s', 'formidable' ).replace( '%s', typeDescription );
192 doneButton.addEventListener( 'click', redirectToExistingPageWithInjectedShortcode );
193
194 const dropdownWrapper = div();
195 dropdownWrapper.innerHTML = response.html;
196 content.appendChild( dropdownWrapper );
197
198 if ( 'form' === state.type ) {
199 editPageUrl = response.edit_page_url + '&frmForm=' + state.objectId;
200 } else {
201 const hookName = 'frm_embed_edit_page_url';
202 const hookArgs = {
203 objectId: state.objectId,
204 type: state.type
205 };
206 editPageUrl = wp.hooks.applyFilters( hookName, response.edit_page_url, hookArgs );
207 }
208
209 frmDom.autocomplete.initAutocomplete( 'page', dropdownWrapper );
210
211 function redirectToExistingPageWithInjectedShortcode( event ) {
212 event.preventDefault();
213
214 const pageDropdown = modal.querySelector( '[name="frm_page_dropdown"]' );
215 modal.querySelectorAll( '.frm_error_style' ).forEach( error => error.remove() );
216
217 const pageId = pageDropdown.value;
218
219 if ( '0' === pageId || '' === pageId ) {
220 const error = div({ className: 'frm_error_style' });
221 error.setAttribute( 'role', 'alert' );
222 error.textContent = __( 'Please select a page', 'formidable' );
223 content.insertBefore( error, title.nextElementSibling );
224 return;
225 }
226
227 window.location.href = editPageUrl.replace( 'post=0', 'post=' + pageId );
228 }
229 }
230 }
231 },
232 {
233 icon: '#frm_plus_icon',
234 label: __( 'Create new page', 'formidable' ),
235 description: newPageDescription,
236 callback: () => {
237 content.innerHTML = '';
238
239 const wrapper = div({ className: 'field-group' });
240 const form = tag( 'form' );
241
242 const createPageWithShortcode = () => {
243 const hookName = 'frm_create_page_with_shortcode_data';
244 const data = wp.hooks.applyFilters(
245 hookName,
246 {
247 action: 'frm_create_page_with_shortcode',
248 object_id: state.objectId,
249 type: state.type,
250 name: input.value,
251 nonce: frmGlobal.nonce
252 }
253 );
254 jQuery.ajax({
255 type: 'POST',
256 url: ajaxurl,
257 data,
258 dataType: 'json',
259 success: function( response ) {
260 if ( 'object' === typeof response && 'string' === typeof response.redirect ) {
261 window.location.href = response.redirect;
262 }
263 }
264 });
265 };
266
267 form.addEventListener(
268 'submit',
269 function( event ) {
270 event.preventDefault();
271 createPageWithShortcode();
272 return false;
273 },
274 true
275 );
276
277 const title = getLabel( __( 'What will you call the new page?', 'formidable' ) );
278 title.setAttribute( 'for', 'frm_name_your_page' );
279 form.appendChild( title );
280
281 const input = tag( 'input' );
282 input.id = 'frm_name_your_page';
283 input.placeholder = __( 'Name your page', 'formidable' );
284 form.appendChild( input );
285
286 wrapper.appendChild( form );
287 content.appendChild( wrapper );
288
289 input.type = 'text';
290 input.focus();
291
292 doneButton = modal.querySelector( '.frm_modal_footer .button-primary' );
293 doneButton.textContent = __( 'Create page', 'formidable' );
294 doneButton.addEventListener(
295 'click',
296 function( event ) {
297 event.preventDefault();
298 createPageWithShortcode();
299 }
300 );
301 }
302 },
303 {
304 icon: '#frm_code_icon',
305 label: __( 'Insert manually', 'formidable' ),
306 description: insertManuallyDescription,
307 callback: () => {
308 content.innerHTML = '';
309
310 if ( 'form' === state.type ) {
311 getEmbedFormManualExamples().forEach( example => content.appendChild( getEmbedExample( example ) ) );
312 } else {
313 const hookName = 'frm_embed_examples';
314 const hookArgs = {
315 type: state.type,
316 objectId: state.objectId,
317 objectKey: state.objectKey
318 };
319 wp.hooks.applyFilters( hookName, [], hookArgs ).forEach(
320 example => content.appendChild( getEmbedExample( example ) )
321 );
322 }
323 }
324 }
325 ];
326
327 options.forEach(
328 option => content.appendChild( getModalOption( option ) )
329 );
330
331 return content;
332 }
333
334 function getTypeDescription() {
335 switch ( state.type ) {
336 case 'view':
337 return __( 'view', 'formidable' );
338 case 'form':
339 default:
340 return __( 'form', 'formidable' );
341 }
342 }
343
344 function getModalOption({ icon, label, description, callback }) {
345 const output = div();
346 output.appendChild( wrapModalOptionIcon( icon ) );
347 output.className = 'frm-embed-modal-option';
348 output.setAttribute( 'tabindex', 0 );
349 output.setAttribute( 'role', 'button' );
350
351 const textWrapper = div();
352 textWrapper.appendChild( getLabel( label ) );
353 textWrapper.appendChild( div( description ) );
354 output.appendChild( textWrapper );
355
356 output.addEventListener(
357 'click',
358 function() {
359 modal.classList.add( 'frm-on-page-2' );
360 callback();
361 }
362 );
363 return output;
364 }
365
366 function wrapModalOptionIcon( iconHref ) {
367 return div({
368 className: 'frm-embed-modal-icon-wrapper',
369 child: svg({ href: iconHref })
370 });
371 }
372
373 function getEmbedFormManualExamples() {
374 const formId = state.objectId;
375 const formKey = state.objectKey;
376
377 let examples = [
378 {
379 label: __( 'WordPress shortcode', 'formidable' ),
380 example: '[formidable id=' + formId + ' title=true description=true]',
381 link: 'https://formidableforms.com/knowledgebase/publish-a-form/#kb-insert-the-shortcode-manually',
382 linkLabel: __( 'How to use shortcodes in WordPress', 'formidable' )
383 },
384 {
385 label: __( 'Use PHP code', 'formidable' ),
386 example: '<?php echo FrmFormsController::get_form_shortcode( array( \'id\' => ' + formId + ', \'title\' => true, \'description\' => true ) ); ?>'
387 }
388 ];
389
390 const filterArgs = { formId, formKey };
391 examples = wp.hooks.applyFilters( 'frmEmbedFormExamples', examples, filterArgs );
392
393 return examples;
394 }
395
396 function getEmbedExample({ label, example, link, linkLabel }) {
397 let unique, element, labelElement, exampleElement, linkElement;
398
399 unique = getAutoId();
400 element = div();
401
402 labelElement = getLabel( label );
403 labelElement.id = 'frm_embed_example_label_' + unique;
404 element.appendChild( labelElement );
405
406 if ( example.length > 80 ) {
407 exampleElement = tag( 'textarea' );
408 } else {
409 exampleElement = tag( 'input' );
410 exampleElement.type = 'text';
411 }
412
413 exampleElement.id = 'frm_embed_example_' + unique;
414 exampleElement.className = 'frm_embed_example';
415 exampleElement.value = example;
416 exampleElement.readOnly = true;
417 exampleElement.setAttribute( 'tabindex', -1 );
418
419 if ( 'undefined' !== typeof link && 'undefined' !== typeof linkLabel ) {
420 linkElement = tag( 'a' );
421 linkElement.href = link;
422 linkElement.textContent = linkLabel;
423 linkElement.setAttribute( 'target', '_blank' );
424 element.appendChild( linkElement );
425 }
426
427 element.appendChild( exampleElement );
428 element.appendChild( getCopyIcon( label ) );
429
430 return element;
431 }
432
433 function getLabel( text ) {
434 const args = { text };
435 return tag( 'label', args );
436 }
437
438 function getCopyIcon( label ) {
439 const icon = svg({ href: '#frm_clone_icon' });
440 icon.id = 'frm_copy_embed_' + getAutoId();
441 icon.setAttribute( 'tabindex', 0 );
442 icon.setAttribute( 'role', 'button' );
443 /* translators: %s: Example type (ie. WordPress shortcode, API Form script) */
444 icon.setAttribute( 'aria-label', __( 'Copy %s', 'formidable' ).replace( '%s', label ) );
445 icon.addEventListener(
446 'click',
447 () => copyExampleToClipboard( icon.parentNode.querySelector( '.frm_embed_example' ) )
448 );
449 return icon;
450 }
451
452 function copyExampleToClipboard( example ) {
453 if ( navigator.clipboard ) {
454 navigator.clipboard.writeText( example.value ).then( handleCopySuccess );
455 return;
456 }
457
458 let copySuccess;
459
460 example.focus();
461 example.select();
462 example.setSelectionRange( 0, 99999 );
463
464 try {
465 copySuccess = document.execCommand( 'copy' );
466 } catch ( error ) {
467 copySuccess = false;
468 }
469
470 if ( copySuccess ) {
471 handleCopySuccess();
472 }
473
474 return copySuccess;
475 }
476
477 function handleCopySuccess() {
478 speak( __( 'Successfully copied embed example', 'formidable' ) );
479 }
480
481 function speak( message ) {
482 const element = document.createElement( 'div' );
483 const id = 'speak-' + getAutoId();
484
485 element.setAttribute( 'aria-live', 'assertive' );
486 element.setAttribute( 'id', id );
487 element.className = 'frm_screen_reader frm_hidden';
488 element.textContent = message;
489 document.body.appendChild( element );
490
491 setTimeout( () => document.body.removeChild( element ), 1000 );
492 }
493
494 /**
495 * Get a unique id that automatically increments with every function call.
496 * Can be used for any UI that requires a unique id.
497 * Not to be used in data.
498 *
499 * @returns {integer}
500 */
501 function getAutoId() {
502 return ++autoId;
503 }
504 }() );
505