PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.28
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.28
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 / applications.js

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

539 lines 14.8 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, frmApplicationsVars */
3
4 if ( 'undefined' === typeof ajaxurl || 'undefined' === typeof wp || 'undefined' === typeof frmDom ) {
5 return;
6 }
7
8 const { __, sprintf } = wp.i18n;
9 const { tag, div, span, a, svg, img } = frmDom;
10 const { maybeCreateModal, footerButton } = frmDom.modal;
11 const { newSearchInput } = frmDom.search;
12 const { doJsonFetch } = frmDom.ajax;
13 const { onClickPreventDefault } = frmDom.util;
14
15 const container = document.getElementById( 'frm_applications_container' );
16 if ( ! container ) {
17 return;
18 }
19
20 const state = {
21 categories: false,
22 templates: false,
23 filteredCategory: false
24 };
25 const elements = {
26 noTemplateSearchResultsPlaceholder: false,
27 templatesGrid: false,
28 activeCategoryAnchor: false,
29 viewApplicationModal: false
30 };
31
32 wp.hooks.addFilter( 'frm_application_card', 'formidable', handleCardHook );
33 wp.hooks.addFilter( 'frm_application_card_item_count', 'formidable', filterItemCount );
34
35 initialize();
36
37 function initialize() {
38 wp.hooks.addAction( 'frm_application_render_templates', 'formidable', getUrlParamsAndMaybeOpenTemplateModal );
39 getApplicationDataAndLoadPage();
40 }
41
42 function getUrlParamsAndMaybeOpenTemplateModal( _, { data } = {} ) {
43 const url = new URL( window.location.href );
44 const urlParams = url.searchParams;
45 if ( ! urlParams.get( 'triggerViewApplicationModal' ) ) {
46 return;
47 }
48
49 const templateKey = urlParams.get( 'template' );
50 if ( ! templateKey ) {
51 return;
52 }
53
54 const template = data.templates.find( template => templateKey === template.key );
55 if ( template ) {
56 openViewApplicationModal( template );
57 }
58 }
59
60 function getApplicationDataAndLoadPage() {
61 doJsonFetch( 'get_applications_data' ).then( handleApplicationsDataResponse );
62 }
63
64 function handleApplicationsDataResponse( data ) {
65 state.categories = data.categories;
66 state.templates = data.templates;
67
68 container.innerHTML = '';
69
70 const contentWrapper = div( { className: 'frm-applications-index-content' } );
71 container.append( contentWrapper );
72
73 const customTemplatesNav = getCustomTemplatesNav();
74
75 contentWrapper.append( customTemplatesNav );
76 renderFormidableTemplates( contentWrapper, data.templates );
77
78 const hookName = 'frm_application_render_templates';
79 const args = { data, customTemplatesNav };
80 wp.hooks.doAction( hookName, contentWrapper, args );
81
82 if ( ! contentWrapper.querySelector( '#frm_custom_applications_grid' ) && ! contentWrapper.querySelector( '#frm_no_applications_placeholder' ) ) {
83 addCustomApplicationsPlaceholder( customTemplatesNav );
84 }
85 }
86
87 function getCustomTemplatesNav() {
88 return div( {
89 className: 'frm-application-templates-nav wrap',
90 children: [
91 tag(
92 'h2',
93 {
94 child: span( __( 'My Applications', 'formidable' ) ),
95 className: 'frm-h2'
96 }
97 )
98 ]
99 } );
100 }
101
102 function addCustomApplicationsPlaceholder( customTemplatesNav ) {
103 const placeholder = div( {
104 id: 'frm_custom_applications_placeholder',
105 className: 'frm_placeholder_block',
106 child: div( {
107 className: 'frm_grid_container',
108 children: [
109 div( {
110 className: 'frm10 frm_clearfix',
111 children: [
112 img( { src: getUrlToApplicationsImages() + 'folder.svg' } ),
113 tag( 'h3', __( 'Improve your workflow with applications', 'formidable' ) ),
114 div( __( 'Applications help to organize your workspace by combining forms, Views, and pages into a full solution.', 'formidable' ) ),
115 ]
116 } ),
117 div( {
118 className: 'frm2',
119 child: a( {
120 className: 'frm-button-primary frm-gradient',
121 text: __( 'Upgrade to Pro', 'formidable' ),
122 href: frmApplicationsVars.proUpgradeUrl
123 } )
124 } )
125 ]
126 } )
127 } );
128 customTemplatesNav.parentNode.insertBefore( placeholder, customTemplatesNav.nextElementSibling );
129 }
130
131 function getUrlToApplicationsImages() {
132 return frmGlobal.url + '/images/applications/';
133 }
134
135 function renderFormidableTemplates( contentWrapper, templates ) {
136 elements.templatesGrid = div( {
137 id: 'frm_application_templates_grid',
138 className: 'frm_grid_container frm-application-cards-grid'
139 } );
140 addTemplatesToGrid( templates );
141 contentWrapper.append( getTemplatesNav() );
142 contentWrapper.append( elements.templatesGrid );
143 }
144
145 function addTemplatesToGrid( templates ) {
146 templates.forEach(
147 application => elements.templatesGrid.append( createApplicationCard( application ) )
148 );
149 maybeTriggerSearch();
150 }
151
152 function maybeTriggerSearch() {
153 const searchInput = document.getElementById( 'frm-application-search' );
154 if ( searchInput && '' !== searchInput.value ) {
155 const eventArgs = { bubbles: true, cancelable: true };
156 const event = new Event( 'input', eventArgs );
157 searchInput.dispatchEvent( event );
158 }
159 }
160
161 function getTemplatesNav() {
162 return div( {
163 className: 'frm-application-templates-nav wrap',
164 children: [
165 tag(
166 'h2',
167 {
168 text: __( 'Application Templates', 'formidable' ),
169 className: 'frm-h2 frm-mb-md'
170 }
171 ),
172 getCategoryOptions(),
173 getTemplateSearch()
174 ]
175 } );
176 }
177
178 function getCategoryOptions() {
179 if ( state.templates.length < 8 ) {
180 // Do not show categories filters until there are at least 8 templates.
181 return document.createTextNode( '' );
182 }
183
184 const categories = [ getAllItemsCategory() ].concat( state.categories );
185 const wrapper = div( { id: 'frm_application_category_filter', className: 'subsubsub' } );
186
187 categories.forEach( addCategoryToWrapper );
188 function addCategoryToWrapper( category, index ) {
189 if ( 0 !== index ) {
190 wrapper.append( document.createTextNode( '|' ) );
191 }
192 const anchor = a( category );
193 if ( 0 === index ) {
194 anchor.classList.add( 'current' );
195 elements.activeCategoryAnchor = anchor;
196 }
197 onClickPreventDefault(
198 anchor,
199 () => {
200 if ( false !== elements.activeCategoryAnchor ) {
201 elements.activeCategoryAnchor.classList.remove( 'current' );
202 }
203
204 handleCategorySelect( category );
205 anchor.classList.add( 'current' );
206 elements.activeCategoryAnchor = anchor;
207 }
208 );
209 wrapper.append( anchor );
210 }
211
212 return wrapper;
213 }
214
215 function getAllItemsCategory() {
216 /* translators: %d: Number of application templates. */
217 return sprintf( __( 'All Items (%d)', 'formidable' ), state.templates.length );
218 }
219
220 function handleCategorySelect( category ) {
221 state.filteredCategory = category;
222
223 Array.from( elements.templatesGrid.children ).forEach(
224 child => child.classList.contains( 'frm-application-card' ) && child.remove()
225 );
226
227 if ( getAllItemsCategory() === category ) {
228 addTemplatesToGrid( state.templates );
229 return;
230 }
231
232 addTemplatesToGrid(
233 state.templates.filter(
234 template => -1 !== template.categories.indexOf( category )
235 )
236 );
237 }
238
239 function getTemplateSearch() {
240 const id = 'frm-application-search';
241 const placeholder = __( 'Search Templates', 'formidable' );
242 const targetClassName = 'frm-application-template-card';
243 const args = { handleSearchResult: handleTemplateSearch };
244 return newSearchInput( id, placeholder, targetClassName, args );
245 }
246
247 function handleTemplateSearch( { foundSomething, notEmptySearchText } ) {
248 if ( false === elements.noTemplateSearchResultsPlaceholder ) {
249 elements.noTemplateSearchResultsPlaceholder = getNoResultsPlaceholder();
250 elements.templatesGrid.append( elements.noTemplateSearchResultsPlaceholder );
251 }
252 elements.noTemplateSearchResultsPlaceholder.classList.toggle( 'frm_hidden', ! notEmptySearchText || foundSomething );
253 }
254
255 function getNoResultsPlaceholder() {
256 return div( __( 'No application templates match your search query.', 'formidable' ) );
257 }
258
259 function handleCardHook( _, args ) {
260 return createApplicationCard( args.data );
261 }
262
263 function createApplicationCard( data ) {
264 const isTemplate = ! data.termId;
265 const card = div( {
266 className: 'frm-application-card frm-card-item',
267 children: [
268 getCardHeader(),
269 div( { className: 'frm-flex' } )
270 ]
271 } );
272
273 if ( isTemplate ) {
274 card.classList.add( 'frm-application-template-card' );
275 card.classList.add( 'frm-locked-application-template' );
276 card.append( tag( 'hr' ) );
277 card.append( getCardContent() );
278
279 card.addEventListener(
280 'click',
281 event => 'a' !== event.target.nodeName.toLowerCase() && card.querySelector( 'a' ).click()
282 );
283 }
284
285 const hookName = 'frm_application_index_card';
286 const args = { data };
287 wp.hooks.doAction( hookName, card, args );
288
289 function getCardHeader() {
290 const titleWrapper = tag(
291 'h3',
292 {
293 children: [
294 svg( { href: '#frm_lock_icon' } ),
295 span( { className: 'frm-inner-text', text: data.name } )
296 ]
297 }
298 );
299 const header = div( {
300 children: [
301 titleWrapper
302 ]
303 } );
304
305 const templateControl = getUseThisTemplateControl( data );
306 if ( templateControl.classList.contains( 'frm-delete-application-trigger' ) ) {
307 header.append( templateControl );
308 } else {
309 titleWrapper.append( templateControl );
310 }
311 if ( data.isNew ) {
312 titleWrapper.append( span( { className: 'frm-new-pill frm-meta-tag frm-fadein', text: __( 'NEW', 'formidable' ) } ) );
313 }
314
315 const counter = getItemCounter();
316 if ( false !== counter ) {
317 header.append( counter );
318 }
319
320 return header;
321 }
322
323 function getItemCounter() {
324 const hookName = 'frm_application_card_item_count';
325 const args = { data };
326 return wp.hooks.applyFilters( hookName, false, args );
327 }
328
329 function getCardContent() {
330 const thumbnailFolderUrl = getUrlToApplicationsImages() + 'thumbnails/';
331 const filenameToUse = data.hasLiteThumbnail ? data.key + ( data.isWebp ? '.webp' : '.png' ) : 'placeholder.svg';
332 return div( {
333 className: 'frm-application-card-image-wrapper',
334 child: img( { src: thumbnailFolderUrl + filenameToUse } )
335 } );
336 }
337
338 return card;
339 }
340
341 function filterItemCount( counter, { data } ) {
342 const hasForms = 'undefined' !== typeof data.formCount && '0' !== data.formCount;
343 const hasViews = 'undefined' !== typeof data.viewCount && '0' !== data.viewCount;
344 const hasPages = 'undefined' !== typeof data.pageCount && '0' !== data.pageCount;
345
346 if ( ! hasForms && ! hasViews && ! hasPages ) {
347 return counter;
348 }
349
350 counter = div( { className: 'frm-application-item-count' } );
351
352 if ( hasForms ) {
353 addCount( data.formCount, __( 'Forms', 'formidable' ), __( 'Form', 'formidable' ) );
354 }
355
356 if ( hasViews ) {
357 addCount( data.viewCount, __( 'Views', 'formidable' ), __( 'View', 'formidable' ) );
358 }
359
360 if ( hasPages ) {
361 addCount( data.pageCount, __( 'Pages', 'formidable' ), __( 'Page', 'formidable' ) );
362 }
363
364 function addCount( countValue, pluralDescriptor, singularDescriptor ) {
365 if ( counter.children.length ) {
366 counter.append( document.createTextNode( ' | ' ) );
367 }
368
369 const descriptor = '1' === countValue ? singularDescriptor : pluralDescriptor;
370 counter.append(
371 span( countValue + ' ' + descriptor )
372 );
373 }
374
375 return counter;
376 }
377
378 function getUseThisTemplateControl( data ) {
379 let control = a( {
380 className: 'button frm-button-secondary frm-button-sm frm-fadein',
381 text: __( 'Learn More', 'formidable' )
382 } );
383 control.setAttribute( 'role', 'button' );
384 /* translators: %s: Application Template Name */
385 const ariaDescription = sprintf( __( '%s Template', 'formidable' ), data.name );
386 control.setAttribute( 'aria-description', ariaDescription );
387 control.addEventListener(
388 'click',
389 event => {
390 if ( '#' === control.getAttribute( 'href' ) ) {
391 event.preventDefault();
392 openViewApplicationModal( data );
393 }
394 }
395 );
396
397 const hookName = 'frm_application_card_control';
398 const args = { data };
399 control = wp.hooks.applyFilters( hookName, control, args );
400
401 return control;
402 }
403
404 function getUpgradeNowText() {
405 return __( 'Upgrade Now', 'formidable' );
406 }
407
408 function openViewApplicationModal( data ) {
409 elements.viewApplicationModal = maybeCreateModal(
410 'frm_view_application_modal',
411 {
412 content: getViewApplicationModalContent( data ),
413 footer: getViewApplicationModalFooter( data )
414 }
415 );
416 elements.viewApplicationModal.querySelector( '.frm-modal-title' ).textContent = data.name;
417 elements.viewApplicationModal.classList.add( 'frm_common_modal' );
418 }
419
420 function getViewApplicationModalContent( data ) {
421 const children = [];
422
423 if ( data.upgradeUrl ) {
424 children.push(
425 div( {
426 className: 'frm_note_style2',
427 children: [
428 span(
429 /* translators: %s: The required license type (ie. Plus, Business, or Elite) */
430 sprintf( __( 'Access to this application requires the %s plan.', 'formidable' ), data.requires )
431 ),
432 a( {
433 className: 'frm-gradient frm-button-primary frm-button-sm',
434 text: getUpgradeNowText(),
435 href: data.upgradeUrl
436 } )
437 ]
438 } )
439 );
440 }
441
442 const placeholderImage = img( { src: getUrlToApplicationsImages() + 'placeholder.png' } );
443 if ( placeholderImage.complete ) {
444 setTimeout( maybeCenterViewApplicationModal, 0 );
445 } else {
446 placeholderImage.addEventListener( 'load', maybeCenterViewApplicationModal );
447 }
448
449 const detailsChildren = [
450 div( {
451 className: 'frm-application-modal-label',
452 text: __( 'Description', 'formidable' )
453 } ),
454 div( data.description )
455 ];
456
457 if ( data.usedAddons && data.usedAddons.length ) {
458 detailsChildren.push(
459 div( {
460 className: 'frm-application-modal-label',
461 text: __( 'Required Add-ons', 'formidable-pro' )
462 } ),
463 getBasicList( data.usedAddons )
464 );
465 }
466
467 children.push(
468 div( {
469 className: 'frm-application-image-wrapper',
470 child: placeholderImage
471 } ),
472 div( {
473 className: 'frm-application-modal-details',
474 children: detailsChildren
475 } )
476 );
477
478 const output = div( { children } );
479
480 const hookName = 'frm_view_application_modal_content';
481 const args = { data };
482 wp.hooks.doAction( hookName, output, args );
483
484 return output;
485 }
486
487 function getBasicList( data ) {
488 return tag(
489 'ul',
490 {
491 className: 'frm-application-item-list',
492 children: data.map( text => tag( 'li', text ) )
493 }
494 );
495 }
496
497 function maybeCenterViewApplicationModal() {
498 if ( false !== elements.viewApplicationModal ) {
499 centerModal( elements.viewApplicationModal );
500 }
501 }
502
503 function centerModal( modal ) {
504 const position = {
505 my: 'center',
506 at: 'center',
507 of: window
508 };
509 jQuery( modal ).dialog( { position } );
510 }
511
512 function getViewApplicationModalFooter( data ) {
513 const viewDemoSiteButton = footerButton( {
514 text: __( 'Learn More', 'formidable' ),
515 buttonType: 'secondary'
516 } );
517 viewDemoSiteButton.href = data.link;
518 viewDemoSiteButton.target = '_blank';
519
520 let primaryActionButton = footerButton( {
521 text: getUpgradeNowText(),
522 buttonType: 'primary'
523 } );
524
525 if ( data.upgradeUrl ) {
526 primaryActionButton.classList.remove( 'dismiss' );
527 primaryActionButton.setAttribute( 'href', data.upgradeUrl );
528 }
529
530 const hookName = 'frm_view_application_modal_primary_action_button';
531 const args = { data };
532 primaryActionButton = wp.hooks.applyFilters( hookName, primaryActionButton, args );
533
534 return div( {
535 children: [ viewDemoSiteButton, primaryActionButton ]
536 } );
537 }
538 }() );
539