| 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 |
|