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