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

540 lines 14.9 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.appendChild( contentWrapper );
72
73 const customTemplatesNav = getCustomTemplatesNav();
74
75 contentWrapper.appendChild( 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.appendChild( getTemplatesNav() );
142 contentWrapper.appendChild( elements.templatesGrid );
143 }
144
145 function addTemplatesToGrid( templates ) {
146 templates.forEach(
147 application => elements.templatesGrid.appendChild( 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.appendChild( 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.appendChild( 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 const wrappedInput = newSearchInput( id, placeholder, targetClassName, args );
245 return wrappedInput;
246 }
247
248 function handleTemplateSearch({ foundSomething, notEmptySearchText }) {
249 if ( false === elements.noTemplateSearchResultsPlaceholder ) {
250 elements.noTemplateSearchResultsPlaceholder = getNoResultsPlaceholder();
251 elements.templatesGrid.appendChild( elements.noTemplateSearchResultsPlaceholder );
252 }
253 elements.noTemplateSearchResultsPlaceholder.classList.toggle( 'frm_hidden', ! notEmptySearchText || foundSomething );
254 }
255
256 function getNoResultsPlaceholder() {
257 return div( __( 'No application templates match your search query.', 'formidable' ) );
258 }
259
260 function handleCardHook( _, args ) {
261 return createApplicationCard( args.data );
262 }
263
264 function createApplicationCard( data ) {
265 const isTemplate = ! data.termId;
266 const card = div({
267 className: 'frm-application-card frm-card-item',
268 children: [
269 getCardHeader(),
270 div({ className: 'frm-flex' })
271 ]
272 });
273
274 if ( isTemplate ) {
275 card.classList.add( 'frm-application-template-card' );
276 card.classList.add( 'frm-locked-application-template' );
277 card.appendChild( tag( 'hr' ) );
278 card.appendChild( getCardContent() );
279
280 card.addEventListener(
281 'click',
282 event => 'a' !== event.target.nodeName.toLowerCase() && card.querySelector( 'a' ).click()
283 );
284 }
285
286 const hookName = 'frm_application_index_card';
287 const args = { data };
288 wp.hooks.doAction( hookName, card, args );
289
290 function getCardHeader() {
291 const titleWrapper = tag(
292 'h3',
293 {
294 children: [
295 svg({ href: '#frm_lock_icon' }),
296 span({ className: 'frm-inner-text', text: data.name })
297 ]
298 }
299 );
300 const header = div({
301 children: [
302 titleWrapper
303 ]
304 });
305
306 const templateControl = getUseThisTemplateControl( data );
307 if ( templateControl.classList.contains('frm-delete-application-trigger' ) ) {
308 header.appendChild( templateControl );
309 } else {
310 titleWrapper.appendChild( templateControl );
311 }
312 if ( data.isNew ) {
313 titleWrapper.appendChild( span({ className: 'frm-new-pill frm-meta-tag frm-fadein', text: __( 'NEW', 'formidable' ) }) );
314 }
315
316 const counter = getItemCounter();
317 if ( false !== counter ) {
318 header.appendChild( counter );
319 }
320
321 return header;
322 }
323
324 function getItemCounter() {
325 const hookName = 'frm_application_card_item_count';
326 const args = { data };
327 return wp.hooks.applyFilters( hookName, false, args );
328 }
329
330 function getCardContent() {
331 const thumbnailFolderUrl = getUrlToApplicationsImages() + 'thumbnails/';
332 const filenameToUse = data.hasLiteThumbnail ? data.key + ( data.isWebp ? '.webp' : '.png' ) : 'placeholder.svg';
333 return div({
334 className: 'frm-application-card-image-wrapper',
335 child: img({ src: thumbnailFolderUrl + filenameToUse })
336 });
337 }
338
339 return card;
340 }
341
342 function filterItemCount( counter, { data }) {
343 const hasForms = 'undefined' !== typeof data.formCount && '0' !== data.formCount;
344 const hasViews = 'undefined' !== typeof data.viewCount && '0' !== data.viewCount;
345 const hasPages = 'undefined' !== typeof data.pageCount && '0' !== data.pageCount;
346
347 if ( ! hasForms && ! hasViews && ! hasPages ) {
348 return counter;
349 }
350
351 counter = div({ className: 'frm-application-item-count' });
352
353 if ( hasForms ) {
354 addCount( data.formCount, __( 'Forms', 'formidable' ), __( 'Form', 'formidable' ) );
355 }
356
357 if ( hasViews ) {
358 addCount( data.viewCount, __( 'Views', 'formidable' ), __( 'View', 'formidable' ) );
359 }
360
361 if ( hasPages ) {
362 addCount( data.pageCount, __( 'Pages', 'formidable' ), __( 'Page', 'formidable' ) );
363 }
364
365 function addCount( countValue, pluralDescriptor, singularDescriptor ) {
366 if ( counter.children.length ) {
367 counter.appendChild( document.createTextNode( ' | ' ) );
368 }
369
370 const descriptor = '1' === countValue ? singularDescriptor : pluralDescriptor;
371 counter.appendChild(
372 span( countValue + ' ' + descriptor )
373 );
374 }
375
376 return counter;
377 }
378
379 function getUseThisTemplateControl( data ) {
380 let control = a({
381 className: 'button frm-button-secondary frm-button-sm frm-fadein',
382 text: __( 'Learn More', 'formidable' )
383 });
384 control.setAttribute( 'role', 'button' );
385 /* translators: %s: Application Template Name */
386 const ariaDescription = sprintf( __( '%s Template', 'formidable' ), data.name );
387 control.setAttribute( 'aria-description', ariaDescription );
388 control.addEventListener(
389 'click',
390 event => {
391 if ( '#' === control.getAttribute( 'href' ) ) {
392 event.preventDefault();
393 openViewApplicationModal( data );
394 }
395 }
396 );
397
398 const hookName = 'frm_application_card_control';
399 const args = { data };
400 control = wp.hooks.applyFilters( hookName, control, args );
401
402 return control;
403 }
404
405 function getUpgradeNowText() {
406 return __( 'Upgrade Now', 'formidable' );
407 }
408
409 function openViewApplicationModal( data ) {
410 elements.viewApplicationModal = maybeCreateModal(
411 'frm_view_application_modal',
412 {
413 content: getViewApplicationModalContent( data ),
414 footer: getViewApplicationModalFooter( data )
415 }
416 );
417 elements.viewApplicationModal.querySelector( '.frm-modal-title' ).textContent = data.name;
418 elements.viewApplicationModal.classList.add( 'frm_common_modal' );
419 }
420
421 function getViewApplicationModalContent( data ) {
422 const children = [];
423
424 if ( data.upgradeUrl ) {
425 children.push(
426 div({
427 className: 'frm_note_style2',
428 children: [
429 span(
430 /* translators: %s: The required license type (ie. Plus, Business, or Elite) */
431 sprintf( __( 'Access to this application requires the %s plan.', 'formidable' ), data.requires )
432 ),
433 a({
434 className: 'frm-gradient frm-button-primary frm-button-sm',
435 text: getUpgradeNowText(),
436 href: data.upgradeUrl
437 })
438 ]
439 })
440 );
441 }
442
443 const placeholderImage = img({ src: getUrlToApplicationsImages() + 'placeholder.png' });
444 if ( placeholderImage.complete ) {
445 setTimeout( maybeCenterViewApplicationModal, 0 );
446 } else {
447 placeholderImage.addEventListener( 'load', maybeCenterViewApplicationModal );
448 }
449
450 const detailsChildren = [
451 div({
452 className: 'frm-application-modal-label',
453 text: __( 'Description', 'formidable' )
454 }),
455 div( data.description )
456 ];
457
458 if ( data.usedAddons && data.usedAddons.length ) {
459 detailsChildren.push(
460 div({
461 className: 'frm-application-modal-label',
462 text: __( 'Required Add-ons', 'formidable-pro' )
463 }),
464 getBasicList( data.usedAddons )
465 );
466 }
467
468 children.push(
469 div({
470 className: 'frm-application-image-wrapper',
471 child: placeholderImage
472 }),
473 div({
474 className: 'frm-application-modal-details',
475 children: detailsChildren
476 })
477 );
478
479 const output = div({ children });
480
481 const hookName = 'frm_view_application_modal_content';
482 const args = { data };
483 wp.hooks.doAction( hookName, output, args );
484
485 return output;
486 }
487
488 function getBasicList( data ) {
489 return tag(
490 'ul',
491 {
492 className: 'frm-application-item-list',
493 children: data.map( text => tag( 'li', text ) )
494 }
495 );
496 }
497
498 function maybeCenterViewApplicationModal() {
499 if ( false !== elements.viewApplicationModal ) {
500 centerModal( elements.viewApplicationModal );
501 }
502 }
503
504 function centerModal( modal ) {
505 const position = {
506 my: 'center',
507 at: 'center',
508 of: window
509 };
510 jQuery( modal ).dialog({ position });
511 }
512
513 function getViewApplicationModalFooter( data ) {
514 const viewDemoSiteButton = footerButton({
515 text: __( 'Learn More', 'formidable' ),
516 buttonType: 'secondary'
517 });
518 viewDemoSiteButton.href = data.link;
519 viewDemoSiteButton.target = '_blank';
520
521 let primaryActionButton = footerButton({
522 text: getUpgradeNowText(),
523 buttonType: 'primary'
524 });
525
526 if ( data.upgradeUrl ) {
527 primaryActionButton.classList.remove( 'dismiss' );
528 primaryActionButton.setAttribute( 'href', data.upgradeUrl );
529 }
530
531 const hookName = 'frm_view_application_modal_primary_action_button';
532 const args = { data };
533 primaryActionButton = wp.hooks.applyFilters( hookName, primaryActionButton, args );
534
535 return div({
536 children: [ viewDemoSiteButton, primaryActionButton ]
537 });
538 }
539 }() );
540