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 / applications.js

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

498 lines 13.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 __ = 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-pro' ) ),
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 child: img({ src: getUrlToApplicationsImages() + 'custom-applications.svg' })
108 })
109 });
110 placeholder.appendChild(
111 div({
112 children: [
113 img({ src: getUrlToApplicationsImages() + 'folder.svg' }),
114 tag( 'h3', __( 'Improve your workflow with applications', 'formidable' ) ),
115 div( __( 'Applications help to organize your workspace by combining forms, Views, and pages into a full solution.', 'formidable' ) ),
116 a({
117 className: 'button button-primary frm-button-primary',
118 text: __( 'Upgrade to Pro', 'formidable' ),
119 href: frmApplicationsVars.proUpgradeUrl
120 })
121 ]
122 })
123 );
124 customTemplatesNav.parentNode.insertBefore( placeholder, customTemplatesNav.nextElementSibling );
125 }
126
127 function getUrlToApplicationsImages() {
128 return frmGlobal.url + '/images/applications/';
129 }
130
131 function renderFormidableTemplates( contentWrapper, templates ) {
132 elements.templatesGrid = div({
133 id: 'frm_application_templates_grid',
134 className: 'frm_grid_container frm-application-cards-grid'
135 });
136 addTemplatesToGrid( templates );
137 contentWrapper.appendChild( getTemplatesNav() );
138 contentWrapper.appendChild( elements.templatesGrid );
139 }
140
141 function addTemplatesToGrid( templates ) {
142 templates.forEach(
143 application => elements.templatesGrid.appendChild( createApplicationCard( application ) )
144 );
145 maybeTriggerSearch();
146 }
147
148 function maybeTriggerSearch() {
149 const searchInput = document.getElementById( 'frm-application-search' );
150 if ( searchInput && '' !== searchInput.value ) {
151 const eventArgs = { bubbles: true, cancelable: true };
152 const event = new Event( 'input', eventArgs );
153 searchInput.dispatchEvent( event );
154 }
155 }
156
157 function getTemplatesNav() {
158 return div({
159 className: 'frm-application-templates-nav wrap',
160 children: [
161 tag(
162 'h2',
163 {
164 text: __( 'Application Templates', 'formidable' ),
165 className: 'frm-h2'
166 }
167 ),
168 getCategoryOptions(),
169 getTemplateSearch()
170 ]
171 });
172 }
173
174 function getCategoryOptions() {
175 if ( state.templates.length < 8 ) {
176 // Do not show categories filters until there are at least 8 templates.
177 return document.createTextNode( '' );
178 }
179
180 const categories = [ getAllItemsCategory() ].concat( state.categories );
181 const wrapper = div({ id: 'frm_application_category_filter' });
182
183 categories.forEach( addCategoryToWrapper );
184 function addCategoryToWrapper( category, index ) {
185 if ( 0 !== index ) {
186 wrapper.appendChild( document.createTextNode( '|' ) );
187 }
188 const anchor = a( category );
189 if ( 0 === index ) {
190 anchor.classList.add( 'frm-active-application-category' );
191 elements.activeCategoryAnchor = anchor;
192 }
193 onClickPreventDefault(
194 anchor,
195 () => {
196 if ( false !== elements.activeCategoryAnchor ) {
197 elements.activeCategoryAnchor.classList.remove( 'frm-active-application-category' );
198 }
199
200 handleCategorySelect( category );
201 anchor.classList.add( 'frm-active-application-category' );
202 elements.activeCategoryAnchor = anchor;
203 }
204 );
205 wrapper.appendChild( anchor );
206 }
207
208 return wrapper;
209 }
210
211 function getAllItemsCategory() {
212 /* translators: %d: Number of application templates. */
213 return __( 'All Items (%d)', 'formidable' ).replace( '%d', state.templates.length );
214 }
215
216 function handleCategorySelect( category ) {
217 state.filteredCategory = category;
218
219 Array.from( elements.templatesGrid.children ).forEach(
220 child => child.classList.contains( 'frm-application-card' ) && child.remove()
221 );
222
223 if ( getAllItemsCategory() === category ) {
224 addTemplatesToGrid( state.templates );
225 return;
226 }
227
228 addTemplatesToGrid(
229 state.templates.filter(
230 template => -1 !== template.categories.indexOf( category )
231 )
232 );
233 }
234
235 function getTemplateSearch() {
236 const id = 'frm-application-search';
237 const placeholder = __( 'Search templates', 'formidable' );
238 const targetClassName = 'frm-application-template-card';
239 const args = { handleSearchResult: handleTemplateSearch };
240 const wrappedInput = newSearchInput( id, placeholder, targetClassName, args );
241 return wrappedInput;
242 }
243
244 function handleTemplateSearch({ foundSomething, notEmptySearchText }) {
245 if ( false === elements.noTemplateSearchResultsPlaceholder ) {
246 elements.noTemplateSearchResultsPlaceholder = getNoResultsPlaceholder();
247 elements.templatesGrid.appendChild( elements.noTemplateSearchResultsPlaceholder );
248 }
249 elements.noTemplateSearchResultsPlaceholder.classList.toggle( 'frm_hidden', ! notEmptySearchText || foundSomething );
250 }
251
252 function getNoResultsPlaceholder() {
253 return div( __( 'No application templates match your search query.', 'formidable' ) );
254 }
255
256 function handleCardHook( _, args ) {
257 return createApplicationCard( args.data );
258 }
259
260 function createApplicationCard( data ) {
261 const isTemplate = ! data.termId;
262 const card = div({
263 className: 'frm-application-card',
264 children: [
265 getCardHeader(),
266 div({ className: 'frm-flex' })
267 ]
268 });
269
270 if ( isTemplate ) {
271 card.classList.add( 'frm-application-template-card' );
272 card.classList.add( 'frm-locked-application-template' );
273 card.appendChild( tag( 'hr' ) );
274 card.appendChild( getCardContent() );
275
276 card.addEventListener(
277 'click',
278 event => 'a' !== event.target.nodeName.toLowerCase() && card.querySelector( 'a' ).click()
279 );
280 }
281
282 const hookName = 'frm_application_index_card';
283 const args = { data };
284 wp.hooks.doAction( hookName, card, args );
285
286 function getCardHeader() {
287 const titleWrapper = span({
288 children: [
289 svg({ href: '#frm_lock_simple' }),
290 tag( 'h4', { text: data.name })
291 ]
292 });
293 const header = div({
294 children: [
295 titleWrapper,
296 getUseThisTemplateControl( data )
297 ]
298 });
299
300 const counter = getItemCounter();
301 if ( false !== counter ) {
302 header.appendChild( counter );
303 }
304
305 return header;
306 }
307
308 function getItemCounter() {
309 const hookName = 'frm_application_card_item_count';
310 const args = { data };
311 return wp.hooks.applyFilters( hookName, false, args );
312 }
313
314 function getCardContent() {
315 const thumbnailFolderUrl = getUrlToApplicationsImages() + 'thumbnails/';
316 const filenameToUse = data.hasLiteThumbnail ? data.key + '.png' : 'placeholder.svg';
317 return div({
318 className: 'frm-application-card-image-wrapper',
319 child: img({ src: thumbnailFolderUrl + filenameToUse })
320 });
321 }
322
323 return card;
324 }
325
326 function filterItemCount( counter, { data }) {
327 const hasForms = 'undefined' !== typeof data.formCount && '0' !== data.formCount;
328 const hasViews = 'undefined' !== typeof data.viewCount && '0' !== data.viewCount;
329 const hasPages = 'undefined' !== typeof data.pageCount && '0' !== data.pageCount;
330
331 if ( ! hasForms && ! hasViews && ! hasPages ) {
332 return counter;
333 }
334
335 counter = div({ className: 'frm-application-item-count' });
336
337 if ( hasForms ) {
338 addCount( data.formCount, __( 'Forms', 'formidable' ), __( 'Form', 'formidable' ) );
339 }
340
341 if ( hasViews ) {
342 addCount( data.viewCount, __( 'Views', 'formidable' ), __( 'View', 'formidable' ) );
343 }
344
345 if ( hasPages ) {
346 addCount( data.pageCount, __( 'Pages', 'formidable' ), __( 'Page', 'formidable' ) );
347 }
348
349 function addCount( countValue, pluralDescriptor, singularDescriptor ) {
350 if ( counter.children.length ) {
351 counter.appendChild( document.createTextNode( ' | ' ) );
352 }
353
354 const descriptor = '1' === countValue ? singularDescriptor : pluralDescriptor;
355 counter.appendChild(
356 span( countValue + ' ' + descriptor )
357 );
358 }
359
360 return counter;
361 }
362
363 function getUseThisTemplateControl( data ) {
364 let control = a({
365 className: 'button frm-button-secondary',
366 text: __( 'Learn More', 'formidable' )
367 });
368 control.setAttribute( 'role', 'button' );
369 /* translators: %s: Application Template Name */
370 const ariaDescription = __( '%s Template' ).replace( '%s', data.name );
371 control.setAttribute( 'aria-description', ariaDescription );
372 control.addEventListener(
373 'click',
374 event => {
375 if ( '#' === control.getAttribute( 'href' ) ) {
376 event.preventDefault();
377 openViewApplicationModal( data );
378 }
379 }
380 );
381
382 const hookName = 'frm_application_card_control';
383 const args = { data };
384 control = wp.hooks.applyFilters( hookName, control, args );
385
386 return control;
387 }
388
389 function getUpgradeNowText() {
390 return __( 'Upgrade Now', 'formidable' );
391 }
392
393 function openViewApplicationModal( data ) {
394 elements.viewApplicationModal = maybeCreateModal(
395 'frm_view_application_modal',
396 {
397 content: getViewApplicationModalContent( data ),
398 footer: getViewApplicationModalFooter( data )
399 }
400 );
401 elements.viewApplicationModal.querySelector( '.frm-modal-title' ).textContent = data.name;
402 elements.viewApplicationModal.classList.add( 'frm_common_modal' );
403 }
404
405 function getViewApplicationModalContent( data ) {
406 const children = [];
407
408 if ( data.upgradeUrl ) {
409 children.push(
410 div({
411 className: 'frm_warning_style',
412 children: [
413 span( __( 'Access to this application requires a license upgrade.', 'formidable' ) ),
414 a({
415 text: getUpgradeNowText(),
416 href: data.upgradeUrl
417 })
418 ]
419 })
420 );
421 }
422
423 const placeholderImage = img({ src: getUrlToApplicationsImages() + 'placeholder.png' });
424 if ( placeholderImage.complete ) {
425 setTimeout( maybeCenterViewApplicationModal, 0 );
426 } else {
427 placeholderImage.addEventListener( 'load', maybeCenterViewApplicationModal );
428 }
429
430 children.push(
431 div({
432 className: 'frm-application-image-wrapper',
433 child: placeholderImage
434 }),
435 div({
436 className: 'frm-application-modal-details',
437 children: [
438 div({
439 className: 'frm-application-modal-label',
440 text: __( 'Description', 'formidable' )
441 }),
442 div( data.description )
443 ]
444 })
445 );
446
447 const output = div({ children });
448
449 const hookName = 'frm_view_application_modal_content';
450 const args = { data };
451 wp.hooks.doAction( hookName, output, args );
452
453 return output;
454 }
455
456 function maybeCenterViewApplicationModal() {
457 if ( false !== elements.viewApplicationModal ) {
458 centerModal( elements.viewApplicationModal );
459 }
460 }
461
462 function centerModal( modal ) {
463 const position = {
464 my: 'center',
465 at: 'center',
466 of: window
467 };
468 jQuery( modal ).dialog({ position });
469 }
470
471 function getViewApplicationModalFooter( data ) {
472 const viewDemoSiteButton = footerButton({
473 text: __( 'Learn More', 'formidable' ),
474 buttonType: 'secondary'
475 });
476 viewDemoSiteButton.href = data.link;
477 viewDemoSiteButton.target = '_blank';
478
479 let primaryActionButton = footerButton({
480 text: getUpgradeNowText(),
481 buttonType: 'primary'
482 });
483
484 if ( data.upgradeUrl ) {
485 primaryActionButton.classList.remove( 'dismiss' );
486 primaryActionButton.setAttribute( 'href', data.upgradeUrl );
487 }
488
489 const hookName = 'frm_view_application_modal_primary_action_button';
490 const args = { data };
491 primaryActionButton = wp.hooks.applyFilters( hookName, primaryActionButton, args );
492
493 return div({
494 children: [ viewDemoSiteButton, primaryActionButton ]
495 });
496 }
497 }() );
498