PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.1
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 6.1, at js/admin/embed.js

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