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

509 lines 14.1 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 frm-mb-md'
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', className: 'subsubsub' });
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( 'current' );
191 elements.activeCategoryAnchor = anchor;
192 }
193 onClickPreventDefault(
194 anchor,
195 () => {
196 if ( false !== elements.activeCategoryAnchor ) {
197 elements.activeCategoryAnchor.classList.remove( 'current' );
198 }
199
200 handleCategorySelect( category );
201 anchor.classList.add( 'current' );
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 frm-card-item',
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 = tag(
288 'h3',
289 {
290 children: [
291 svg({ href: '#frm_lock_icon' }),
292 span({ className: 'frm-inner-text', text: data.name })
293 ]
294 }
295 );
296 const header = div({
297 children: [
298 titleWrapper,
299 getUseThisTemplateControl( data )
300 ]
301 });
302
303 if ( data.isNew ) {
304 titleWrapper.appendChild( span({ className: 'frm-new-pill frm-meta-tag', text: __( 'NEW', 'formidable' ) }) );
305 }
306
307 const counter = getItemCounter();
308 if ( false !== counter ) {
309 header.appendChild( counter );
310 }
311
312 return header;
313 }
314
315 function getItemCounter() {
316 const hookName = 'frm_application_card_item_count';
317 const args = { data };
318 return wp.hooks.applyFilters( hookName, false, args );
319 }
320
321 function getCardContent() {
322 const thumbnailFolderUrl = getUrlToApplicationsImages() + 'thumbnails/';
323 const filenameToUse = data.hasLiteThumbnail ? data.key + '.png' : 'placeholder.svg';
324 return div({
325 className: 'frm-application-card-image-wrapper',
326 child: img({ src: thumbnailFolderUrl + filenameToUse })
327 });
328 }
329
330 return card;
331 }
332
333 function filterItemCount( counter, { data }) {
334 const hasForms = 'undefined' !== typeof data.formCount && '0' !== data.formCount;
335 const hasViews = 'undefined' !== typeof data.viewCount && '0' !== data.viewCount;
336 const hasPages = 'undefined' !== typeof data.pageCount && '0' !== data.pageCount;
337
338 if ( ! hasForms && ! hasViews && ! hasPages ) {
339 return counter;
340 }
341
342 counter = div({ className: 'frm-application-item-count' });
343
344 if ( hasForms ) {
345 addCount( data.formCount, __( 'Forms', 'formidable' ), __( 'Form', 'formidable' ) );
346 }
347
348 if ( hasViews ) {
349 addCount( data.viewCount, __( 'Views', 'formidable' ), __( 'View', 'formidable' ) );
350 }
351
352 if ( hasPages ) {
353 addCount( data.pageCount, __( 'Pages', 'formidable' ), __( 'Page', 'formidable' ) );
354 }
355
356 function addCount( countValue, pluralDescriptor, singularDescriptor ) {
357 if ( counter.children.length ) {
358 counter.appendChild( document.createTextNode( ' | ' ) );
359 }
360
361 const descriptor = '1' === countValue ? singularDescriptor : pluralDescriptor;
362 counter.appendChild(
363 span( countValue + ' ' + descriptor )
364 );
365 }
366
367 return counter;
368 }
369
370 function getUseThisTemplateControl( data ) {
371 let control = a({
372 className: 'button frm-button-secondary frm-button-sm',
373 text: __( 'Learn More', 'formidable' )
374 });
375 control.setAttribute( 'role', 'button' );
376 /* translators: %s: Application Template Name */
377 const ariaDescription = __( '%s Template' ).replace( '%s', data.name );
378 control.setAttribute( 'aria-description', ariaDescription );
379 control.addEventListener(
380 'click',
381 event => {
382 if ( '#' === control.getAttribute( 'href' ) ) {
383 event.preventDefault();
384 openViewApplicationModal( data );
385 }
386 }
387 );
388
389 const hookName = 'frm_application_card_control';
390 const args = { data };
391 control = wp.hooks.applyFilters( hookName, control, args );
392
393 return control;
394 }
395
396 function getUpgradeNowText() {
397 return __( 'Upgrade Now', 'formidable' );
398 }
399
400 function openViewApplicationModal( data ) {
401 elements.viewApplicationModal = maybeCreateModal(
402 'frm_view_application_modal',
403 {
404 content: getViewApplicationModalContent( data ),
405 footer: getViewApplicationModalFooter( data )
406 }
407 );
408 elements.viewApplicationModal.querySelector( '.frm-modal-title' ).textContent = data.name;
409 elements.viewApplicationModal.classList.add( 'frm_common_modal' );
410 }
411
412 function getViewApplicationModalContent( data ) {
413 const children = [];
414
415 if ( data.upgradeUrl ) {
416 children.push(
417 div({
418 className: 'frm_warning_style',
419 children: [
420 span(
421 /* translators: %s: The required license type (ie. Plus, Business, or Elite) */
422 __( 'Access to this application requires the %s plan.', 'formidable' )
423 .replace( '%s', data.requires )
424 ),
425 a({
426 text: getUpgradeNowText(),
427 href: data.upgradeUrl
428 })
429 ]
430 })
431 );
432 }
433
434 const placeholderImage = img({ src: getUrlToApplicationsImages() + 'placeholder.png' });
435 if ( placeholderImage.complete ) {
436 setTimeout( maybeCenterViewApplicationModal, 0 );
437 } else {
438 placeholderImage.addEventListener( 'load', maybeCenterViewApplicationModal );
439 }
440
441 children.push(
442 div({
443 className: 'frm-application-image-wrapper',
444 child: placeholderImage
445 }),
446 div({
447 className: 'frm-application-modal-details',
448 children: [
449 div({
450 className: 'frm-application-modal-label',
451 text: __( 'Description', 'formidable' )
452 }),
453 div( data.description )
454 ]
455 })
456 );
457
458 const output = div({ children });
459
460 const hookName = 'frm_view_application_modal_content';
461 const args = { data };
462 wp.hooks.doAction( hookName, output, args );
463
464 return output;
465 }
466
467 function maybeCenterViewApplicationModal() {
468 if ( false !== elements.viewApplicationModal ) {
469 centerModal( elements.viewApplicationModal );
470 }
471 }
472
473 function centerModal( modal ) {
474 const position = {
475 my: 'center',
476 at: 'center',
477 of: window
478 };
479 jQuery( modal ).dialog({ position });
480 }
481
482 function getViewApplicationModalFooter( data ) {
483 const viewDemoSiteButton = footerButton({
484 text: __( 'Learn More', 'formidable' ),
485 buttonType: 'secondary'
486 });
487 viewDemoSiteButton.href = data.link;
488 viewDemoSiteButton.target = '_blank';
489
490 let primaryActionButton = footerButton({
491 text: getUpgradeNowText(),
492 buttonType: 'primary'
493 });
494
495 if ( data.upgradeUrl ) {
496 primaryActionButton.classList.remove( 'dismiss' );
497 primaryActionButton.setAttribute( 'href', data.upgradeUrl );
498 }
499
500 const hookName = 'frm_view_application_modal_primary_action_button';
501 const args = { data };
502 primaryActionButton = wp.hooks.applyFilters( hookName, primaryActionButton, args );
503
504 return div({
505 children: [ viewDemoSiteButton, primaryActionButton ]
506 });
507 }
508 }() );
509