PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.11
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.11
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / assets / js / campaign-builder / admin-builder.js

admin-builder.js in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.11, at assets/js/campaign-builder/admin-builder.js

9,128 lines 298.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* global charitable_builder, charitable_campaign_builder_field_conditionals, wpchar, jconfirm, charitable_panel_switch, Charitable, CharitableCampaignEmbedWizard, CharitableCampaignCongratsWizard, wpCookies, tinyMCE, CharitableUtils, CharitableOnboarding */
2
3 var CharitableCampaignBuilder = window.CharitableCampaignBuilder || ( function( document, window, $ ) {
4
5 var s,
6 $builder,
7 $builderForm,
8 elements = {};
9
10 /**
11 * Whether to show the close confirmation dialog or not.
12 *
13 * @since 1.8.0
14 *
15 * @type {boolean}
16 */
17 var closeConfirmation = true;
18
19 /**
20 * A field is adding.
21 *
22 * @since 1.8.0
23 *
24 * @type {boolean}
25 */
26 var adding = false;
27
28 var app = {
29
30 settings: {
31 spinner: '<i class="charitable-loading-spinner"></i>',
32 spinnerInline: '<i class="charitable-loading-spinner charitable-loading-inline"></i>',
33 tinymceDefaults: { tinymce: { toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,link' }, quicktags: true },
34 pagebreakTop: false,
35 pagebreakBottom: false,
36 upload_img_modal: false
37 },
38
39 /**
40 * Start the engine.
41 *
42 * @since 1.8.0
43 */
44 init: function() {
45
46 var that = this;
47
48 charitable_panel_switch = true;
49 s = this.settings;
50
51 // Document ready.
52 $( app.ready );
53
54 // Page load.
55 $( window ).on( 'load', function() {
56
57 // In the case of jQuery 3.+, we need to wait for a ready event first.
58 if ( typeof $.ready.then === 'function' ) {
59 $.ready.then( app.load );
60 } else {
61 app.load();
62 }
63 } );
64
65 $( window ).on( 'beforeunload', function() {
66 if ( ! that.formIsSaved() && closeConfirmation ) {
67 return charitable_builder.are_you_sure_to_close;
68 }
69 } );
70
71 },
72
73 /**
74 * Page load.
75 *
76 * @since 1.8.0
77 */
78 load: function() {
79
80 // Trigger initial save for new forms.
81 if ( wpchar.getQueryString( 'newcampaign' ) ) {
82 app.formSave( false );
83 }
84
85 // Allow callbacks to prevent making Campaign Builder ready...
86 const event = CharitableUtils.triggerEvent( $builder, 'CharitableCampaignBuilderReady' );
87
88 // ...by triggering `event.preventDefault()`.
89 if ( event.isDefaultPrevented() ) {
90 return false;
91 }
92
93 // Hide loading overlay and make the Form Builder ready to use.
94 app.hideLoadingOverlay(); // eslint-disable-line
95
96 // if a textarea for a coded field is present, init the code editor.
97 app.initCodeEditor();
98
99 },
100
101 /**
102 * Document ready.
103 *
104 * @since 1.8.0
105 * @version 1.8.1.12 Added tooltip info for onboarding.
106 *
107 */
108 ready: function() {
109
110 if ( app.isVisitedViaBackButton() ) {
111 location.reload();
112
113 return;
114 }
115
116 app.showLoadingOverlay();
117
118 s.version = '1.8.4.3';
119
120 // Cache builder element.
121 $builder = $( '#charitable-builder' );
122 $builderForm = $( '#charitable-builder-form' );
123
124 // Action buttons.
125 elements.$helpButton = $( '#charitable-help' );
126 elements.$previewButton = $( '#charitable-preview-btn' );
127 elements.$viewCampaignButton = $( '#charitable-view-btn' );
128 elements.$embedButton = $( '#charitable-embed' );
129 elements.$statusButton = $( '#charitable-status-button' );
130 elements.$saveButton = $( '#charitable-save' );
131 elements.$exitButton = $( '#charitable-exit' );
132 elements.$templateButton = $( '.charitable-panel-template-button' );
133 elements.$designButton = $( '.charitable-panel-design-button' );
134 elements.$settingsButton = $( '.charitable-panel-settings-button' );
135 elements.$marketingButton = $( '.charitable-panel-marketing-button' );
136 elements.$paymentButton = $( '.charitable-panel-payment-button' );
137
138 // Cache other elements.
139 elements.$noFieldsOptions = $( '.charitable-panel-fields .charitable-no-fields-holder .no-fields' );
140 elements.$noFieldsPreview = $( '.charitable-panel-fields .charitable-no-fields-holder .no-fields-preview' );
141 elements.$formPreview = $( '.charitable-panel-fields .charitable-preview-wrap' );
142 elements.$revisionPreview = $( '#charitable-panel-revisions .charitable-panel-content' );
143 elements.$focusOutTarget = null;
144
145 elements.$preview = $( '.charitable-preview' );
146 elements.$panelDesign = $( '#charitable-panel-design');
147
148 elements.$nextFieldId = $( '#charitable-field-id' );
149 elements.$fieldOptions = $( '#charitable-field-options' );
150 elements.$fieldsPreviewWrap = $( '.charitable-panel-fields .charitable-panel-content-wrap' ),
151 elements.$sortableFieldsWrap = $( '.charitable-panel-fields .charitable-field-wrap' );
152 elements.$sortableTabsWrap = $( '.charitable-panel-fields .charitable-tab-wrap' );
153 elements.$sortableTabNav = $( '.charitable-campaign-preview nav.charitable-campaign-preview-nav' );
154 elements.$sortableTabContent = $( '.charitable-campaign-preview .tab-content' );
155
156 elements.$addFieldsButtons = $( '.charitable-add-fields-button' ).not( '.not-draggable' ).not( '.warning-modal' ).not( '.charitable-not-available').not( '.charitable-not-installed' ).not( '.charitable-not-activated' ).not( '.charitable-installed-refresh' ).not( '.charitable-addon-file-missing').not( '.charitable-need-upgrade');
157
158 elements.$templatePanel = $( '#charitable-panel-template' );
159 elements.$templatePreview = $( '.charitable-template-preview' );
160
161 elements.$settingsPanel = $( '#charitable-panel-settings' );
162
163 elements.$campaignID = $( '#charitable-builder-form input[name="id"]' );
164 elements.$templateID = $( '#charitable-builder-form input[name="template_id"]' );
165 elements.$templateLabel = $( '#charitable-builder-form input[name="template_label"]' );
166 elements.$postStatus = $( '#charitable-builder-form input[name="post_status"]' );
167
168 elements.$primaryThemeColorBase = $( '#charitable-builder-form input[name="color_base_primary"]' );
169 elements.$secondaryThemeColorBase = $( '#charitable-builder-form input[name="color_base_secondary"]' );
170 elements.$tertiaryThemeColorBase = $( '#charitable-builder-form input[name="color_base_tertiary"]' );
171 elements.$buttonThemeColorBase = $( '#charitable-builder-form input[name="color_base_button"]' );
172
173 // Bind all actions.
174 app.bindUIActions();
175
176 // Setup/cache some vars not available before
177 s.formID = $builderForm.data( 'id' );
178 s.templateID = $builderForm.data( 'template-id' );
179 s.templateLabel = $builderForm.data( 'template-label' );
180
181 s.primaryThemeColorBase = s.primaryThemeColor = $( this ).closest('.charitable-template').data('template-primary');
182 s.secondaryThemeColorBase = s.secondaryThemeColor = $( this ).closest('.charitable-template').data('template-secondary');
183 s.tertiaryThemeColorBase = s.tertiaryThemeColor = $( this ).closest('.charitable-template').data('template-tertiary');
184 s.buttonThemeColorBase = s.buttonThemeColor = $( this ).closest('.charitable-template').data('template-button');
185
186 s.primaryThemeColorBase = elements.$primaryThemeColorBase.val().length > 0 ? elements.$primaryThemeColorBase.val() : '';
187 s.secondaryThemeColorBase = elements.$secondaryThemeColorBase.val().length > 0 ? elements.$secondaryThemeColorBase.val() : '';
188 s.tertiaryThemeColorBase = elements.$tertiaryThemeColorBase.val().length > 0 ? elements.$tertiaryThemeColorBase.val() : '';
189 s.buttonThemeColorBase = elements.$buttonThemeColorBase.val().length > 0 ? elements.$buttonThemeColorBase.val() : '';
190 s.primaryThemeColor = '';
191 s.secondaryThemeColor = '';
192 s.tertiaryThemeColor = '';
193 s.buttonThemeColor = '';
194
195 s.formSaved = $builderForm.find( '#charitable-form-saved' ).val();
196 s.formSavedStatus = $builderForm.find( '#charitable-form-post-status' ).val();
197 s.formSavedStatusLabel = $builderForm.find( '#charitable-form-post-status-label' ).val();
198 s.formStatus = s.formSavedStatus.length > 0 ? s.formSavedStatus : 'draft'; // this can change but draft is the default when a new campaign is created.
199 s.formStatusLabel = s.formSavedStatusLabel.length > 0 ? s.formSavedStatusLabel : 'Draft'; // this can change but draft is the default when a new campaign is created.
200
201 // Global variables for quick reference to campaign data like titles and descriptions.
202 s.campaignTitle = $( 'input#charitable_settings_title' ).val();
203 s.campaignDescription = $( 'div[data-special-type="campaign_description"] .campaign-builder-htmleditor' ).first().html();
204 s.campaignOverview = $( 'div[data-special-type="campaign_overview"] .campaign-builder-htmleditor' ).length > 0 ? $( 'div[data-special-type="campaign_overview"] .campaign-builder-htmleditor' ).first().html() : '';
205
206 // Limit the number of tabs.
207 s.maxNumberOfTabs = 4;
208
209 s.denyList = {
210 'donation-form': { 'donate-button' : 0, 'donation-form' : 0, 'donate-amount' : 0 },
211 'donate-button': { 'donation-form' : 0 },
212 'donate-amount': { 'donation-form' : 0 }
213 };
214
215 s.pagebreakTop = $( '.charitable-pagebreak-top' ).length;
216 s.pagebreakBottom = $( '.charitable-pagebreak-bottom' ).length;
217
218 s.didInitHTMLEditorFields = false;
219 s.quilled = [];
220 s.currentModal = false;
221
222 // Disable implicit submission for every form inside the builder.
223 // All form values are managed by JS and should not be submitted by pressing Enter.
224 $builder.on( 'keypress', '#charitable-builder-form :input:not(textarea)', function( e ) {
225 if ( e.keyCode === 13 ) {
226 e.preventDefault();
227 }
228 } );
229
230 // If there is a section configured, display it.
231 // Otherwise, we show the first panel by default.
232 $( '.charitable-panel' ).each( function( index, el ) { // eslint-disable-line
233 var $this = $( this ),
234 $configured = $this.find( '.charitable-panel-sidebar-section.configured' ).first();
235
236 if ( $configured.length ) {
237 var section = $configured.data( 'section' );
238 $configured.addClass( 'active' );
239 $this.find( '.charitable-panel-content-section-' + section ).show().addClass( 'active' );
240 $this.find( '.charitable-panel-content-section-default' ).hide();
241 } else {
242 $this.find( '.charitable-panel-content-section').hide().removeClass( 'active' );
243 $this.find( '.charitable-panel-content-section').first().show().addClass( 'active' );
244 $this.find( '.charitable-panel-sidebar-section:first-of-type' ).addClass( 'active' );
245 }
246
247 } );
248
249 // Secret builder hotkeys.
250 app.builderHotkeys();
251
252 if ( typeof jconfirm !== 'undefined' ) {
253
254 // jquery-confirm defaults.
255 jconfirm.defaults = {
256 closeIcon: false,
257 backgroundDismiss: false,
258 escapeKey: true,
259 animationBounce: 1,
260 useBootstrap: false,
261 theme: 'modern',
262 boxWidth: '400px',
263 animateFromElement: false,
264 draggable: false,
265 content: charitable_builder.something_went_wrong
266 };
267
268 }
269
270 // $('#charitable_settings_title').attr('disabled', true );
271
272 // Init all date pickers on the page.
273 $( '.campaign-builder-datepicker' ).each( function() {
274 app.initDatePicker( $( this ) );
275 });
276
277 // Init all unique UI tag fields on the page.
278 $( '.campaign-tag-field' ).each( function() {
279 app.initTagField( $( this ) );
280 });
281
282 // This handles the unique suggested donation area that creates dynamic rows.
283 $( '.charitable-campaign-suggested-donations' ).each( function() {
284 app.initSuggestedDonations( $( this ) );
285 });
286 $( '.charitable-campaign-suggested-donations-mini' ).each( function() {
287 app.initSuggestedDonationsMini( $( this ) );
288 app.updateSuggestdDonationsMiniRowsFromSettings( $( this ) );
289 });
290
291 app.initColorPicker();
292
293 app.initTemplatePanel();
294
295 app.initStatusButton();
296
297 // Goal and End Date checks
298 app.updateGoalRelatedItems();
299 app.updateEndDateRelatedItems();
300
301 // Refresh so that the campaign input box at the top can resize upon load of page.
302 app.resizeTopCampaignTitleInputBox();
303 if ( false !== app.hasTemplate() ) {
304 var panel = ( null !== wpCookies.get( 'charitable_panel' ) ) ? wpCookies.get( 'charitable_panel' ).trim() : false;
305
306 // if there is a cookie saved with a particular panel, go to that by default.
307 if ( typeof panel !== 'undefined' && panel.length > 0 && panel !== 'template' ) {
308 app.panelSwitch( panel );
309 } else {
310 app.panelSwitch( 'design' );
311 }
312
313 // If there is a template, onload check the fields present on preview screen and disable the ones that are not allowed.
314 app.checkFieldAllow();
315
316 // Cookies.
317 app.redirectBasedOnCookies( panel );
318
319 } else {
320
321 app.forceTemplateSelect();
322 // app.disableFormActions( false );
323
324 // 1.8.1.12
325 // temp: change mode to "new campaign mode".
326 app.setCampaignTitleNotSet();
327
328 }
329
330 // Lite Modal.
331 app.openModalButtonClick();
332
333 app.confirmCampaignDeletion();
334
335 $('.education-buttons button').click(function() {
336 window.open("https://www.wpcharitable.com/pricing/");
337 });
338
339 // Misc.
340
341 $(".campaign-builder-campaign-creator-id select").select2({
342 templateResult: app.campaignCreatorFormatOptions
343 });
344 $(".campaign-builder-campaign-creator-id-mini select").select2({
345 templateResult: app.campaignCreatorFormatOptions
346 });
347
348 },
349
350 /**
351 * Return a "row" in the select2 HTML for creator campaign dropdown.
352 *
353 * @since 1.8.0
354 *
355 * @param {array} state The name of the panel.
356 *
357 */
358 campaignCreatorFormatOptions( state ) {
359
360 if (!state.id) { return state.text; }
361 var $state = $(
362 '<span class="charitable-select2-avatar"><img width="20px" height="20px" style="display: inline-block; float: left; margin-right: 10px;" src="' + state.element.dataset.avatar + '" /> ' + state.text + '</span><span class="charitable-select2-meta"> ' + state.element.dataset.meta + '</span>'
363 );
364
365 return $state;
366 },
367
368 /**
369 * If a cookie has saved where the user was last, attempt to bring them back to that screen.
370 *
371 * @since 1.8.0
372 *
373 * @param {string} panel The name of the panel.
374 *
375 */
376 redirectBasedOnCookies( panel = false ) {
377
378 var cookieContentSection = wpCookies.get( 'charitable_panel_content_section' ),
379 cookieActiveFieldId = '' !== wpCookies.get( 'charitable_panel_active_field_id' ) ? parseInt( wpCookies.get( 'charitable_panel_active_field_id' ) ) : '',
380 cookieActiveTabId = wpCookies.get( 'charitable_panel_tab_section_tab_id' ),
381 cookieActiveLayoutOptionsGroup = wpCookies.get( 'charitable_panel_design_layout_options_group' );
382
383 if ( $('.charitable-preview').is(':visible') && '' !== cookieActiveTabId && $('nav.charitable-campaign-preview-nav li[data-tab-id="' + cookieActiveTabId + '"] a').is(':visible') ) {
384
385 $('.charitable-preview nav.charitable-campaign-preview-nav li[data-tab-id="' + cookieActiveTabId + '"] a').click();
386
387 } else if ( $('.charitable-preview').is(':visible') && $('#charitable-field-' + cookieActiveFieldId + ' a').is(':visible') ) {
388
389 app.clickFieldEdit( cookieActiveFieldId );
390
391 } else if ( $('.charitable-panel-sidebar').is(':visible') && $('a.charitable-panel-sidebar-section[data-section="' + cookieContentSection + '"').is(':visible') ) { // settings section
392
393 $('a.charitable-panel-sidebar-section[data-section="' + cookieContentSection + '"').click();
394
395 } else if ( '' !== cookieActiveFieldId && $('#charitable-field-' + cookieActiveFieldId ).is(':visible') ) {
396
397 $('#charitable-field-' + cookieActiveFieldId ).click();
398
399 } else if ( $('.charitable-panel-sidebar').is(':visible') && cookieContentSection && cookieContentSection !== 'layout-options' && $('.charitable-tabs').is(':visible') && $('.charitable-tabs li#' + cookieContentSection + ' a').is(':visible') ) { // design tabs
400
401 $('.charitable-tabs li#' + cookieContentSection + ' a').click();
402
403 } else if ( false === panel && false !== app.hasTemplate() ) {
404
405 app.panelSwitch( 'design' );
406
407 } else {
408
409 wpchar.debug('redirectBasedOnCookies nothing');
410 }
411
412 },
413
414 /**
415 * Simulates a click on an edit fields as if the user did it themselves.
416 *
417 * @since 1.8.0
418 *
419 * @param {string} fieldId Upgrade modal type (with video or not).
420 *
421 */
422 clickFieldEdit( fieldId ) {
423
424 $('#charitable-field-' + fieldId + ' a.charitable-field-edit').click();
425
426 },
427
428 /**
429 * Reassigns the .tab-content to the variable, making sure it holds the latest.
430 *
431 * @since 1.8.0
432 *
433 */
434 refreshTabFieldsSortDrag() {
435
436 elements.$sortableTabContent = $( '.charitable-campaign-preview .tab-content' );
437
438 },
439
440 /**
441 * Attempts to determine if the campaign has a template assigned to it or not.
442 *
443 * @since 1.8.0
444 *
445 */
446 hasTemplate() {
447
448 if ( typeof s.templateID !== 'undefined' && s.templateID.length > 0 ) {
449 return true;
450 }
451
452 var templateID = parseInt( elements.$templateID.val() );
453
454 if ( templateID > 0 ) {
455 return true;
456 }
457
458 return false;
459
460 },
461
462 /**
463 * Changes UI so user can select a template and can't select other UI.
464 *
465 * @since 1.8.0
466 *
467 */
468 forceTemplateSelect() {
469
470 $( '#charitable-panels-toggle' ).find( 'button' ).removeClass( 'active' ).addClass('disabled');
471 elements.$templateButton.removeClass('disabled').addClass( 'active' );
472 app.panelSwitch( 'template' );
473
474 },
475
476 /**
477 * Removes the disabled UI that foreced a template selection.
478 *
479 * @since 1.8.0
480 *
481 */
482 unforceTemplateSelect() {
483
484 $( '#charitable-panels-toggle' ).find( 'button' ).removeClass( 'disabled' );
485
486 },
487
488 /**
489 * Changes UI so user cannot visit the template tab to change a template.
490 *
491 * @since 1.8.0
492 *
493 */
494 disableTemplateSelect() {
495
496 elements.$templateButton.removeClass('active').addClass( 'disabled' );
497
498 },
499
500 /**
501 * When a template is seleced, certain UI and CSS classes need to be updated.
502 *
503 * @since 1.8.0
504 *
505 * @param {string} templateID The 'slug' of the template.
506 * @param {string} templateLabel The text/label of the template.
507 *
508 */
509 updateTemplateID( templateID = '', templateLabel = '' ) {
510
511 // update the invisible value.
512 elements.$templateID.val( templateID );
513 elements.$templateLabel.val( templateLabel );
514
515 // update the admin preview CSS stylesheet.
516 app.updateThemeCSS( null, templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, false );
517
518 // update some critical preview CSS.
519 $('.charitable-preview').removeClass( function( index, className ) {
520 return (className.match (/(^|\s)charitable-builder-template-\S+/g) || []).join(' ');
521 }).addClass( 'charitable-builder-template-' + templateID );
522
523 // update template name in preview top bar
524 if ( templateLabel !== '' ) {
525 $('.charitable-preview-top-bar .charitable-campaign-theme-label').text( templateLabel );
526 } else if ( templateID !== '' ) {
527 $('.charitable-preview-top-bar .charitable-campaign-theme-label').text( templateID );
528 }
529
530 },
531
532 /**
533 * Update Theme CSS file for preview
534 *
535 * @since 1.8.0
536 *
537 * @param {string} whatChanged 'primary', etc. helpful for updating JUST the right CSS file.
538 * @param {string} templateID The 'slug' of the template.
539 * @param {string} primaryThemeColor Full alphanumeric hex value without hashtag.
540 * @param {string} secondaryThemeColor
541 * @param {string} tertiaryThemeColor
542 * @param {string} buttonThemeColor
543 *
544 */
545
546 updateThemeCSS( whatChanged = null, templateID = '', primaryThemeColor = '', secondaryThemeColor = '', tertiaryThemeColor = '', buttonThemeColor = '', justColors = true ) {
547
548 // There needs to be a templateID or we can't link to the CSS file.
549 if ( '' === templateID && '' === s.templateID ) {
550 return;
551 }
552
553 if ( typeof s.primaryThemeColor === 'undefined' || '' === primaryThemeColor ) {
554 if ( typeof s.primaryThemeColor !== 'undefined' && '' !== s.primaryThemeColor ) {
555 primaryThemeColor = s.primaryThemeColor;
556 } else if ( typeof s.primaryThemeColorBase !== 'undefined' && '' !== s.primaryThemeColorBase ) {
557 primaryThemeColor = s.primaryThemeColorBase;
558 } else {
559 primaryThemeColor = '000000'; // shouldn't get to this point.
560 }
561 }
562
563 if ( typeof s.secondaryThemeColor === 'undefined' || '' === secondaryThemeColor ) {
564 if ( typeof s.secondaryThemeColor !== 'undefined' && '' !== s.secondaryThemeColor ) {
565 secondaryThemeColor = s.secondaryThemeColor;
566 } else if ( typeof s.secondaryThemeColorBase !== 'undefined' && '' !== s.secondaryThemeColorBase ) {
567 secondaryThemeColor = s.secondaryThemeColorBase;
568 } else {
569 secondaryThemeColor = '000000'; // shouldn't get to this point.
570 }
571 }
572
573 if ( typeof s.tertiaryThemeColor === 'undefined' || '' === tertiaryThemeColor ) {
574 if ( typeof s.tertiaryThemeColor !== 'undefined' && '' !== s.tertiaryThemeColor ) {
575 tertiaryThemeColor = s.tertiaryThemeColor;
576 } else if ( typeof s.tertiaryThemeColorBase !== 'undefined' && '' !== s.tertiaryThemeColorBase ) {
577 tertiaryThemeColor = s.tertiaryThemeColorBase;
578 } else {
579 tertiaryThemeColor = '000000'; // shouldn't get to this point.
580 }
581 }
582
583 if ( typeof s.buttonThemeColor === 'undefined' || '' === buttonThemeColor ) {
584 if ( typeof s.buttonThemeColor !== 'undefined' && '' !== s.buttonThemeColor ) {
585 buttonThemeColor = s.buttonThemeColor;
586 } else if ( typeof s.buttonThemeColorBase !== 'undefined' && '' !== s.buttonThemeColorBase ) {
587 buttonThemeColor = s.buttonThemeColorBase;
588 } else {
589 buttonThemeColor = '000000'; // shouldn't get to this point.
590 }
591 }
592
593 // ensure colors are passed raw hex without the hashtag
594 primaryThemeColor = primaryThemeColor.replace(/[^a-zA-Z 0-9]+/g, '');
595 secondaryThemeColor = secondaryThemeColor.replace(/[^a-zA-Z 0-9]+/g, '');
596 tertiaryThemeColor = tertiaryThemeColor.replace(/[^a-zA-Z 0-9]+/g, '');
597 buttonThemeColor = buttonThemeColor.replace(/[^a-zA-Z 0-9]+/g, '');
598
599 var colorQueryString = 'p=' + primaryThemeColor + '&s=' + secondaryThemeColor + '&t=' + tertiaryThemeColor + '&b=' + buttonThemeColor + '&ver=' + s.version;
600
601 // add class to entire preview area
602
603 // update the admin preview CSS stylesheet.
604 // $("head link[rel='stylesheet']").last().after('<link rel="stylesheet" id="charitable-builder-template-preview-theme-css-temp" href="' + charitable_builder.charitable_assets_dir + 'css/campaign-builder/themes/admin/' + templateID + '.php?' + colorQueryString + '" type="text/css" media="screen">');
605
606 if ( justColors === false ) {
607 $('link[id="charitable-builder-template-preview-theme-css"]').attr('href', charitable_builder.charitable_rest_url + 'campaign-css-admin/' + templateID + '?' + colorQueryString );
608 }
609
610 if ( whatChanged !== null ) {
611 app.replaceCSSFile( whatChanged, templateID, colorQueryString );
612 } else {
613 app.replaceCSSFile( 'primary', templateID, colorQueryString );
614 app.replaceCSSFile( 'secondary', templateID, colorQueryString );
615 app.replaceCSSFile( 'tertiary', templateID, colorQueryString );
616 app.replaceCSSFile( 'button', templateID, colorQueryString );
617 }
618
619 },
620
621 /**
622 * Replace Theme CSS file for preview
623 *
624 * @since 1.8.0
625 *
626 * @param {string} whatChanged 'primary', etc. helpful for updating JUST the right CSS file.
627 * @param {string} templateID The 'slug' of the template.
628 * @param {string} colorQueryString
629 *
630 */
631 replaceCSSFile: function ( whatChanged, templateID, colorQueryString ) {
632
633 $('head').find('link#charitable-builder-template-preview-theme-colors-' + whatChanged + '-css').remove();
634 $('head').append('<link rel="stylesheet" data-color-type="' + whatChanged + '" id="charitable-builder-template-preview-theme-colors-temp" href="' + charitable_builder.charitable_rest_url + 'campaign-css-admin/' + templateID + '-colors?' + colorQueryString + '" type="text/css" media="screen">');
635
636 $('#charitable-design-wrap').addClass('loading');
637
638 setTimeout(function() {
639 $('head').find('#charitable-builder-template-preview-theme-colors-temp').attr('id', 'charitable-builder-template-preview-theme-colors-' + whatChanged + '-css');
640 $('#charitable-design-wrap').removeClass('loading');
641 }, 250);
642
643 },
644
645 /**
646 * Search template callback.
647 *
648 * @since 1.8.0
649 *
650 * @param {object} e Event object.
651 */
652 searchTemplate: function( e ) {
653
654 e.preventDefault();
655
656 let $active = $('.charitable-setup-templates-categories li.active'),
657 category = $active.data( 'category' ),
658 searchQuery = $( this ).val();
659
660 app.performSearch( searchQuery, category );
661
662 },
663
664 /**
665 * Perform search value.
666 *
667 * @since 1.8.0
668 *
669 * @param {string} query Value to search.
670 * @param {string} category Another value to search.
671 */
672 performSearch( query = '', category = '' ) {
673
674 let $templateList = elements.$templatePreview.find('.charitable-template-list');
675
676 if ( query === '' && ( category === '' || category === 'all' ) ) {
677 $templateList.find('.charitable-template-list-container-item').removeClass('charitable-hidden');
678 } else {
679 $templateList.find('.charitable-template-list-container-item').addClass('charitable-hidden');
680 if ( query !== '' && category !== '' && category !== 'all' ) {
681 $templateList.find('.charitable-template[data-template-tags*="' + query + '"][data-template-categories*="' + category + '"]').parent().removeClass('charitable-hidden');
682 } else if ( query !== '' && ( category === '' || category === 'all' ) ) {
683 $templateList.find('.charitable-template[data-template-tags*="' + query + '"],.charitable-template[data-template-categories*="' + query + '"]').parent().removeClass('charitable-hidden');
684 } else {
685 $templateList.find('.charitable-template[data-template-tags*="' + category + '"],.charitable-template[data-template-categories*="' + category + '"]').parent().removeClass('charitable-hidden');
686 }
687 }
688
689 var numItems = $('.charitable-template-list-container-item').not('.hidden').length;
690
691 $( '.charitable-templates-no-results' ).toggle( ! numItems );
692
693 if ( ! elements.$templatePreview.find('#charitable-setup-template-search').val() ) {
694 elements.$templatePreview.find('.charitable-setup-templates-search-wrap .fa-close').hide();
695 } else {
696 elements.$templatePreview.find('.charitable-setup-templates-search-wrap .fa-close').show();
697 }
698
699 app.showCheckTemplateList();
700 app.showUpgradeBanner();
701
702 },
703
704 /**
705 * Actions after a user selects a category in the template screen.
706 *
707 * @since 1.8.0
708 *
709 * @param {object} e Event object.
710 */
711 selectCategory: function( e ) {
712
713 e.preventDefault();
714
715 let $item = $( this ),
716 $active = $item.closest( 'ul' ).find( '.active' ),
717 category = $item.data( 'category' ),
718 searchQuery = $( '#charitable-setup-template-search' ).val();
719
720 $active.removeClass( 'active' );
721 $item.addClass( 'active' );
722
723 app.performSearch( searchQuery, category );
724
725
726 },
727
728 /**
729 * Show upgrade banner if licence type is less than Pro.
730 *
731 * @since 1.8.0
732 */
733 showUpgradeBanner: function() {
734
735 if ( ! $( '#tmpl-charitable-templates-upgrade-banner' ).length ) {
736 return;
737 }
738
739 if ( $('#charitable-template-list .charitable-template-upgrade-banner').length > 0 ) {
740 $('#charitable-template-list .charitable-template-upgrade-banner').remove();
741 }
742
743 if ( ! app.isFunction( wp.template ) ) { // eslint-disable-line
744 wpchar.debug( 'wp.template not a function' );
745 return;
746 } else {
747 wpchar.debug( 'wp.template is a function' );
748 }
749
750 let template = wp.template( 'charitable-templates-upgrade-banner' ); // eslint-disable-line
751
752 if ( ! template ) {
753 return;
754 }
755
756 const $templates = $( '#charitable-template-list .charitable-template-list-container-item:not(.charitable-hidden)'),
757 $insertPoint = $( '#charitable-template-list .charitable-template-list-container-item');
758
759 if ( $templates.length > 5 ) {
760 $ ('#charitable-template-list .charitable-template-list-container > div:last-child').after( template() );
761 return;
762 }
763
764 $insertPoint.last().after( template() );
765
766 },
767
768 /**
769 * Called when the template list probably has changed - see if we hide/show the sections if there is/are no templates for those sections.
770 *
771 * @since 1.8.1.12
772 */
773 showCheckTemplateList: function() {
774
775 // if there are no more template items (.charitable-template-list-container-item) with the class of blank then hide the blank section (otherwise show it).
776 if ( $('#charitable-template-list .charitable-template-list-container-item.blank:not(.charitable-hidden)').length === 0 ) {
777 $('#charitable-template-list .charitable-template-list-section-blank').addClass('charitable-hidden');
778 $('#charitable-template-list .charitable-template-list-section-prebuilt').addClass('charitable-hidden');
779 } else {
780 $('#charitable-template-list .charitable-template-list-section-blank').removeClass('charitable-hidden');
781 $('#charitable-template-list .charitable-template-list-section-prebuilt').removeClass('charitable-hidden');
782 }
783
784 // if there are no more template items (.charitable-template-list-container-item) without the class of blank then hide the prebuuilt section (otherwise show it).
785 if ( $('#charitable-template-list .charitable-template-list-container-item.prebuilt:not(.charitable-hidden)').length === 0 ) {
786 $('#charitable-template-list .charitable-template-list-section-prebuilt').addClass('charitable-hidden');
787 } else {
788 $('#charitable-template-list .charitable-template-list-section-prebuilt').removeClass('charitable-hidden');
789 }
790
791 if ( $('#charitable-template-list .charitable-template-list-container-item.prebuilt:not(.charitable-hidden)').length > 0 && $('#charitable-template-list .charitable-template-list-container-item.blank:not(.charitable-hidden)').length === 0 ) {
792 $('#charitable-template-list .charitable-template-list-section-prebuilt').addClass('charitable-hidden');
793 }
794
795 },
796
797 /**
798 * Upon page load (or general resets) make sure the top button is showing the proper status and dropdown has the right available status choices.
799 *
800 * @since 1.8.0
801 */
802 initStatusButton: function( formStatus = '', formStatusLabel = '' ) {
803
804
805 var $statusDropdown = $( 'ul#charitable-status-dropdown' );
806
807 if ( formStatus == '' && s.formStatus.length > 0 ) {
808 formStatus = s.formStatus;
809 }
810 if ( formStatusLabel == '' && s.formStatusLabel.length > 0 ) {
811 formStatusLabel = s.formStatusLabel;
812 }
813
814 // make sure dropdown isn't visible
815 $statusDropdown.addClass('charitable-hidden');
816 $( "#charitable-status-button" ).removeClass('active');
817
818 // change status
819 $( '#charitable-status-button span.text').html( formStatusLabel );
820 $( '#charitable-status-button').attr('data-status', formStatus );
821
822 $statusDropdown.find('a').removeClass('charitable-hidden');
823 $statusDropdown.find('a.switch-' + formStatus).addClass('charitable-hidden');
824 $statusDropdown.find('a.' + formStatus).addClass('charitable-hidden');
825
826 if ( formStatus === 'draft' ) {
827 // $statusDropdown.find('a[class*="switch-"]').addClass('charitable-hidden');
828 } else if ( formStatus === 'publish' ) {
829 // $statusDropdown.find('a.draft').addClass('charitable-hidden');
830 // $statusDropdown.find('a.pending').addClass('charitable-hidden');
831 // $statusDropdown.find('a.review').addClass('charitable-hidden');
832 }
833
834 },
835
836 /**
837 * Init the template panel and setup the actions, etc.
838 *
839 * @since 1.8.0
840 */
841 initTemplatePanel: function() {
842
843 $( '#charitable-template-container' )
844 .on( 'keyup', '#charitable-setup-template-search', app.searchTemplate )
845 .on( 'click', '.charitable-setup-templates-categories li', app.selectCategory );
846
847 if ( ! s.didInitHTMLEditorFields ) {
848
849 $( '.campaign-builder-htmleditor' ).each( function() {
850 app.initHTMLEditorFields( $( this ) );
851 });
852 $( '.campaign-builder-htmleditor-min' ).each( function() {
853 app.initHTMLEditorFields( $( this ), true );
854 });
855
856 s.didInitHTMLEditorFields = true;
857
858 }
859
860 // feedback form.
861 elements.$templatePreview.on( 'click', '.charitable-template-lite-to-pro a.button, .charitable-setup-templates-feedback a.send-feedback', function( e ) {
862 e.preventDefault();
863
864 // Scroll to the top of the charitable-panel-content-wrap div container.
865 $('.charitable-panel-content-wrap').animate({
866 scrollTop: 0
867 }, 500);
868
869 $('.charitable-template-list-container').addClass('disabled');
870 $('.charitable-feedback-form-container').after('<div id="charitable-builder-underlay" class="charitable-builder-underlay"></div>');
871 $('.charitable-feedback-form-container').css("opacity", "100" ).css("visibility", "visible" );
872
873 $('.charitable-feedback-form-container').find('.charitable-feedback-form-interior').removeClass('charitable-hidden');
874 $('.charitable-feedback-form-container').find('textarea').val('');
875 $('.charitable-feedback-form-interior-confirmation').addClass('charitable-hidden');
876
877 });
878
879 // Close button on feedback form.
880 $builderForm.on( 'click', '#charitable-feedback-form .charitable-templates-close-icon, #charitable-feedback-form-confirmation .charitable-templates-close-icon', function( e ) {
881 e.preventDefault();
882
883 $('.charitable-template-list-container').removeClass('disabled');
884 $('.charitable-builder-underlay').remove();
885 $('#charitable-panel-template .charitable-feedback-form-container').css("opacity", "0" ).css("visibility", "hidden" );
886
887 });
888
889 // Template Preview form.
890 $builderForm.on( 'click', '.template-buttons a.preview-campaign', function( e ) {
891 e.preventDefault();
892
893 $('.charitable-template-list-container').addClass('disabled');
894 $('.charitable-feedback-form-container').after('<div id="charitable-builder-underlay" class="charitable-builder-underlay"></div>');
895
896 // Dynamically update the popup with elemnets from the selected/clicked on <li> element.
897 const previewBox = $( this ).closest('.charitable-template'),
898 templateLabel = typeof previewBox.data('template-label') !== 'undefined' ? previewBox.data('template-label') : '',
899 templateDescription = typeof previewBox.data('template-description') !== 'undefined' ? previewBox.data('template-description') : '',
900 templatePreviewURL = typeof previewBox.data('template-preview-url') !== 'undefined' ? previewBox.data('template-preview-url') : '',
901 templateCode = typeof previewBox.data('template-code') !== 'undefined' ? previewBox.data('template-code') : '';
902
903 $('#charitable-builder-modal-template-preview').find('h4').html( templateLabel );
904 $('#charitable-builder-modal-template-preview').find('.charitable-templates-preview-description').html( '<p>' + templateDescription + '</p>' );
905 $('#charitable-builder-modal-template-preview').find('img').attr('src', templatePreviewURL );
906 $('#charitable-builder-modal-template-preview').find('img').attr('alt', templateLabel );
907 $('#charitable-builder-modal-template-preview').find('a.button-link').attr('data-template-id', templateCode );
908
909 $('.charitable-builder-modal.charitable-builder-modal-template-preview').addClass('active');
910
911 });
912 $builderForm.on( 'click', '#charitable-templates-preview-form .charitable-templates-close-icon', function( e ) {
913 e.preventDefault();
914
915 $('.charitable-template-list-container').removeClass('disabled');
916 $('#charitable-builder-underlay').remove();
917 $('.charitable-builder-modal.charitable-builder-modal-template-preview').removeClass('active');
918
919 });
920
921 // Tab and filtering.
922 elements.$templatePreview.on( 'click', 'nav.charitable-template-tabs a', function( e ) {
923 e.preventDefault();
924
925 var $this = $( this ),
926 templateTabFilter = ( $this ).data('template-tab-filter') ? ( $this ).data('template-tab-filter') : false,
927 $templateList = elements.$templatePreview.find('.charitable-template-list ');
928
929 elements.$templatePreview.find('nav.charitable-template-tabs a').removeClass('active');
930 $this.addClass('active');
931
932 $templateList.find('.charitable-template').removeClass('hidden');
933
934 if ( templateTabFilter ) {
935 $templateList.find('.charitable-template[data-template-type!="' + templateTabFilter + '"]').addClass('hidden');
936 }
937
938 });
939
940 // Show default templates.
941 elements.$templatePreview.on( 'click', 'a.charitable-trigger-blank', function( e ) {
942 e.preventDefault();
943 $('#charitable-template-list .charitable-template-upgrade-banner').remove();
944 elements.$templatePreview.find('#charitable-setup-template-search').val('simple').trigger('keyup').addClass('highlighted').addClass('with-x');
945 elements.$templatePreview.find('.charitable-setup-templates-categories').find('li[data-category=all').click();
946 app.showCheckTemplateList();
947 app.showUpgradeBanner();
948 });
949
950 elements.$templatePreview.on( 'click', 'i.fa-close', function( e ) { // eslint-disable-line
951 elements.$templatePreview.find('#charitable-setup-template-search').val('').trigger('keyup').removeClass('highlighted');
952 });
953
954
955 // Create campaign.
956 elements.$templatePreview.on( 'click', '.button.create-campaign', function( e ) {
957 e.preventDefault();
958
959 if ( app.hasTemplate() ) {
960
961 // Display error in modal window.
962 $.confirm( {
963 title: charitable_builder.heads_up,
964 content: '<p>' + charitable_builder.error_already_started_campaign + '</p>',
965 icon: 'fa fa-exclamation-circle',
966 type: 'orange',
967 buttons: {
968 confirm: {
969 text: charitable_builder.ok,
970 btnClass: 'btn-confirm',
971 keys: [ 'enter' ],
972 action: function() {
973 s.templateID = $( this ).closest('.charitable-template').data('template-code');
974 s.templateLabel = $( this ).closest('.charitable-template').data('template-label');
975
976 s.primaryThemeColorBase = s.primaryThemeColor = $( this ).closest('.charitable-template').data('template-primary');
977 s.secondaryThemeColorBase = s.secondaryThemeColor = $( this ).closest('.charitable-template').data('template-secondary');
978 s.tertiaryThemeColorBase = s.tertiaryThemeColor = $( this ).closest('.charitable-template').data('template-tertiary');
979 s.buttonThemeColorBase = s.buttonThemeColor = $( this ).closest('.charitable-template').data('template-button');
980
981 elements.$primaryThemeColorBase.val( s.primaryThemeColorBase );
982 elements.$secondaryThemeColorBase.val( s.secondaryThemeColorBase );
983 elements.$tertiaryThemeColorBase.val( s.tertiaryThemeColorBase );
984 elements.$buttonThemeColorBase.val( s.buttonThemeColorBase );
985
986 $('input[name="layout__advanced__theme_color_primary"]').val( s.primaryThemeColor );
987 $('input[name="layout__advanced__theme_color_secondary"]').val( s.secondaryThemeColor );
988 $('input[name="layout__advanced__theme_color_tertiary"]').val( s.tertiaryThemeColor );
989 $('input[name="layout__advanced__theme_color_button"]').val( s.buttonThemeColor );
990
991 document.querySelector('input[name="layout__advanced__theme_color_primary"]').dispatchEvent(new Event('input', { bubbles: true }));
992 document.querySelector('input[name="layout__advanced__theme_color_secondary"]').dispatchEvent(new Event('input', { bubbles: true }));
993 document.querySelector('input[name="layout__advanced__theme_color_tertiary"]').dispatchEvent(new Event('input', { bubbles: true }));
994 document.querySelector('input[name="layout__advanced__theme_color_button"]').dispatchEvent(new Event('input', { bubbles: true }));
995
996 app.restartForm( s.templateID, null, s.templateLabel );
997 app.restartTemplateScreen( s.templateID );
998
999 }
1000 },
1001 cancel: {
1002 text: charitable_builder.cancel,
1003 keys: [ 'esc' ]
1004 }
1005 }
1006 } );
1007
1008 } else {
1009
1010 s.templateID = $( this ).closest('.charitable-template').data('template-code');
1011 s.templateLabel = $( this ).closest('.charitable-template').data('template-label');
1012
1013 // Auto-populate campaign title with template name if title is blank (for new campaigns only).
1014 var $titleInput = $( 'input#charitable_settings_title' );
1015 var currentTitle = $titleInput.val() || s.campaignTitle || '';
1016 if ( ( ! currentTitle || $.trim( currentTitle ) === '' ) && s.templateLabel ) {
1017 app.updateFormUI( 'title', s.templateLabel );
1018 }
1019
1020 s.primaryThemeColorBase = s.primaryThemeColor = $( this ).closest('.charitable-template').data('template-primary');
1021 s.secondaryThemeColorBase = s.secondaryThemeColor = $( this ).closest('.charitable-template').data('template-secondary');
1022 s.tertiaryThemeColorBase = s.tertiaryThemeColor = $( this ).closest('.charitable-template').data('template-tertiary');
1023 s.buttonThemeColorBase = s.buttonThemeColor = $( this ).closest('.charitable-template').data('template-button');
1024
1025 elements.$primaryThemeColorBase.val( s.primaryThemeColorBase );
1026 elements.$secondaryThemeColorBase.val( s.secondaryThemeColorBase );
1027 elements.$tertiaryThemeColorBase.val( s.tertiaryThemeColorBase );
1028 elements.$buttonThemeColorBase.val( s.buttonThemeColorBase );
1029
1030 $('input[name="layout__advanced__theme_color_primary"]').val( s.primaryThemeColor );
1031 $('input[name="layout__advanced__theme_color_secondary"]').val( s.secondaryThemeColor );
1032 $('input[name="layout__advanced__theme_color_tertiary"]').val( s.tertiaryThemeColor );
1033 $('input[name="layout__advanced__theme_color_button"]').val( s.buttonThemeColor );
1034
1035 document.querySelector('input[name="layout__advanced__theme_color_primary"]').dispatchEvent(new Event('input', { bubbles: true }));
1036 document.querySelector('input[name="layout__advanced__theme_color_secondary"]').dispatchEvent(new Event('input', { bubbles: true }));
1037 document.querySelector('input[name="layout__advanced__theme_color_tertiary"]').dispatchEvent(new Event('input', { bubbles: true }));
1038 document.querySelector('input[name="layout__advanced__theme_color_button"]').dispatchEvent(new Event('input', { bubbles: true }));
1039
1040 app.updateTemplateID( s.templateID, s.templateLabel );
1041 app.unforceTemplateSelect();
1042 app.enableFormActions();
1043 app.restartForm( s.templateID, 'design', s.templateLabel );
1044 app.restartTemplateScreen( s.templateID );
1045
1046 if ( ! s.didInitHTMLEditorFields ) {
1047
1048 $( '.campaign-builder-htmleditor' ).each( function() {
1049 app.initHTMLEditorFields( $( this ) );
1050 });
1051 $( '.campaign-builder-htmleditor-min' ).each( function() {
1052 app.initHTMLEditorFields( $( this ), true );
1053 });
1054
1055 s.didInitHTMLEditorFields = true;
1056
1057 }
1058 // app.disableTemplateSelect();
1059
1060 CharitableUtils.triggerEvent( $builder, 'charitableCampaignFormScreen', [ s.templateID ] );
1061
1062 // Shepherd.activeTour.next();
1063
1064 }
1065
1066 } );
1067
1068 // Create campaign via preview window. Sneaky!
1069 $builder.on( 'click', '.button-preview-create-campaign', function( e ) {
1070 e.preventDefault();
1071 // find the create button in the list and click that instead of trying to duplicate everything.
1072 var $theButton = $( this ),
1073 theTemplateID = $theButton.data('template-id');
1074
1075 if ( theTemplateID !== '' ) {
1076 $('.charitable-template-list-container').removeClass('disabled');
1077 $('#charitable-builder-underlay').remove();
1078 // $('.charitable-templates-preview-container').addClass('charitable-hidden');
1079 $('.charitable-builder-modal.charitable-builder-modal-template-preview').removeClass('active');
1080 // if the button.update-campaign exists, click that.
1081 if ( $('#charitable-template-list .charitable-template[data-template-code="' + theTemplateID + '"] .button.update-campaign').length > 0 ) {
1082 elements.$templatePreview.find('.charitable-template-list-container-item.charitable-template-' + theTemplateID + ' .button.update-campaign').click();
1083 } else {
1084 // if the button.create-campaign exists, click that.
1085 elements.$templatePreview.find('.charitable-template-list-container-item.charitable-template-' + theTemplateID + ' .button.create-campaign').click();
1086 }
1087
1088 }
1089
1090 } );
1091
1092 $builder.on( 'click', '.button-preview-update-campaign', function( e ) {
1093 e.preventDefault();
1094 // find the create button in the list and click that instead of trying to duplicate everything.
1095 var $theButton = $( this ),
1096 theTemplateID = $theButton.data('template-id');
1097
1098 if ( theTemplateID !== '' ) {
1099 $('.charitable-template-list-container').removeClass('disabled');
1100 $('#charitable-builder-underlay').remove();
1101 // $('.charitable-templates-preview-container').addClass('charitable-hidden');
1102 $('.charitable-builder-modal.charitable-builder-modal-template-preview').removeClass('active');
1103 // if the button.update-campaign exists, click that.
1104 if ( $('#charitable-template-list .charitable-template[data-template-code="' + theTemplateID + '"] .button.update-campaign').length > 0 ) {
1105 elements.$templatePreview.find('.charitable-template-list-container-item.charitable-template-' + theTemplateID + ' .button.update-campaign').click();
1106 } else {
1107 // if the button.create-campaign exists, click that.
1108 elements.$templatePreview.find('.charitable-template-list-container-item.charitable-template-' + theTemplateID + ' .button.create-campaign').click();
1109 }
1110
1111 }
1112
1113 } );
1114
1115 // Update campaign.
1116 elements.$templatePreview.on( 'click', '.button.update-campaign', function( e ) {
1117 e.preventDefault();
1118
1119 var theButton = $( this );
1120
1121 if ( app.hasTemplate() ) {
1122
1123 // Display error in modal window.
1124 $.confirm( {
1125 title: charitable_builder.heads_up,
1126 content: '<p>' + charitable_builder.error_already_started_campaign + '</p>',
1127 icon: 'fa fa-exclamation-circle',
1128 type: 'orange',
1129 buttons: {
1130 confirm: {
1131 text: charitable_builder.ok,
1132 btnClass: 'btn-confirm',
1133 keys: [ 'enter' ],
1134 action: function() {
1135
1136 s.templateID = theButton.closest('.charitable-template').data('template-code');
1137 s.templateLabel = theButton.closest('.charitable-template').data('template-label');
1138 app.updateTemplateID( s.templateID, s.templateLabel );
1139
1140 s.primaryThemeColorBase = s.primaryThemeColor = theButton.closest('.charitable-template').data('template-primary');
1141 s.secondaryThemeColorBase = s.secondaryThemeColor = theButton.closest('.charitable-template').data('template-secondary');
1142 s.tertiaryThemeColorBase = s.tertiaryThemeColor = theButton.closest('.charitable-template').data('template-tertiary');
1143 s.buttonThemeColorBase = s.buttonThemeColor = theButton.closest('.charitable-template').data('template-button');
1144
1145 elements.$primaryThemeColorBase.val( s.primaryThemeColorBase );
1146 elements.$secondaryThemeColorBase.val( s.secondaryThemeColorBase );
1147 elements.$tertiaryThemeColorBase.val( s.tertiaryThemeColorBase );
1148 elements.$buttonThemeColorBase.val( s.buttonThemeColorBase );
1149
1150 $('input[name="layout__advanced__theme_color_primary"]').val( s.primaryThemeColor );
1151 $('input[name="layout__advanced__theme_color_secondary"]').val( s.secondaryThemeColor );
1152 $('input[name="layout__advanced__theme_color_tertiary"]').val( s.tertiaryThemeColor );
1153 $('input[name="layout__advanced__theme_color_button"]').val( s.buttonThemeColor );
1154
1155 document.querySelector('input[name="layout__advanced__theme_color_primary"]').dispatchEvent(new Event('input', { bubbles: true }));
1156 document.querySelector('input[name="layout__advanced__theme_color_secondary"]').dispatchEvent(new Event('input', { bubbles: true }));
1157 document.querySelector('input[name="layout__advanced__theme_color_tertiary"]').dispatchEvent(new Event('input', { bubbles: true }));
1158 document.querySelector('input[name="layout__advanced__theme_color_button"]').dispatchEvent(new Event('input', { bubbles: true }));
1159
1160 // update the template screen UI, first clearing any "current" labels, etc.
1161 $('#charitable-template-list .charitable-template').removeClass('active');
1162 $('#charitable-template-list .charitable-banner-container').each( function() {
1163 $( this ).addClass('charitable-hidden');
1164 $( this ).parent().find('.template-buttons').removeClass('charitable-hidden');
1165 });
1166 theButton.closest('.charitable-template').addClass('active');
1167 theButton.closest('.charitable-template').find('.template-buttons').addClass('charitable-hidden');
1168 theButton.closest('.charitable-template').find('.charitable-banner-container').removeClass('charitable-hidden');
1169
1170 // Update the text.
1171 $('.charitable-setup-desc.secondary-text strong.template-name').html( s.templateLabel );
1172
1173 wpchar.debug('chose this path');
1174
1175 app.restartForm( s.templateID, 'design', s.templateLabel );
1176 app.restartTemplateScreen( s.templateID );
1177
1178 }
1179 },
1180 cancel: {
1181 text: charitable_builder.cancel,
1182 keys: [ 'esc' ]
1183 }
1184 }
1185 } );
1186
1187 }
1188
1189 } );
1190
1191 app.showUpgradeBanner();
1192
1193 },
1194
1195 /**
1196 * Restart the template page to reflect any current template changes.
1197 *
1198 * @since 1.8.0
1199 *
1200 * @param {boolean} templateID Template slug.
1201 *
1202 */
1203 restartTemplateScreen: function( templateID = 0 ) {
1204
1205 if ( templateID === '' || templateID === 0 ) {
1206 templateID = s.templateID;
1207 }
1208
1209 if ( templateID === '' || templateID === 0) {
1210 return;
1211 }
1212
1213 const templateList = $('.charitable-template-list-container');
1214
1215 // first remove any 'preview' buttons in the thumbnails.
1216 templateList.find('.template-buttons').remove('.preview-campaign');
1217 // replace the "create campaign" buttons with the "update campaign" buttons.
1218 templateList.find('.button.create-campaign').removeClass('create-campaign').addClass('update-campaign').html( charitable_builder.update_campaign );
1219 // remove any active charitable-templates so we can reset things.
1220 templateList.find('.charitable-template').removeClass('active');
1221 templateList.find('.charitable-template .charitable-banner-container').addClass('charitable-hidden');
1222 // now find the charitable-template with a data attr "template code" that matches the templateID.
1223 templateList.find('.charitable-template[data-template-code="' + templateID + '"]').addClass('active');
1224 templateList.find('.charitable-template[data-template-code="' + templateID + '"] .charitable-banner-container').removeClass('charitable-hidden');
1225
1226
1227 },
1228
1229 /**
1230 * Restart the forms, restart key values.
1231 *
1232 * @since 1.8.0
1233 *
1234 * @param {boolean} templateID Template slug.
1235 * @param {boolean} panel Panel slug.
1236 * @param {boolean} templateLabel Text/label of template.
1237 *
1238 */
1239 restartForm: function( templateID = 0, panel = 'design', templateLabel = false ) {
1240
1241 app.updateTemplateID( templateID, templateLabel );
1242
1243 app.showLoadingOverlay();
1244
1245 var data = {
1246 action : 'charitable_get_campaign_builder_template_data',
1247 id : templateID,
1248 title : s.campaignTitle,
1249 nonce : charitable_builder.nonce
1250 };
1251
1252 $.post( charitable_builder.ajax_url, data, function( response ) {
1253
1254 if ( response.success ) {
1255
1256 // Clear all hidden fields that represent placed field son preview form, if we are starting over ore resetting.
1257 $('#charitable-field-options .charitable-layout-options-group-inner').find('.charitable-panel-field').remove();
1258 $('#charitable-field-options .charitable-select-field-notice').show();
1259 s.quilled = [];
1260 elements.$nextFieldId.val( 0 );
1261
1262 app.hideLoadingOverlay();
1263
1264 var previewLayout = typeof response.data.preview !== 'undefined' ? response.data.preview : false,
1265 previewFieldOptions = typeof response.data.field_options !== 'undefined' ? response.data.field_options : false,
1266 previewTabOptions = typeof response.data.tab_options !== 'undefined' ? response.data.tab_options : false,
1267 previewAdvancedOptions = typeof response.data.advanced !== 'undefined' ? response.data.advanced : false;
1268
1269 if ( previewLayout ) {
1270 app.addLayoutToPreview( previewLayout );
1271 } else {
1272 wpchar.debug( 'previewLayout is false or not defined' );
1273 }
1274
1275 if ( previewFieldOptions ) {
1276 app.addFieldOptions( previewFieldOptions );
1277 } else {
1278 wpchar.debug( 'previewFieldOptions is false or not defined' );
1279 }
1280
1281 if ( previewTabOptions ) {
1282 app.addTabOptions( previewTabOptions );
1283 } else {
1284 wpchar.debug( 'previewTabOptions is false or not defined' );
1285 }
1286
1287 if ( previewAdvancedOptions ) {
1288 app.addAdvancedOptions( previewAdvancedOptions );
1289 } else {
1290 wpchar.debug( 'previewAdvancedOptions is false or not defined' );
1291 }
1292
1293 if ( Charitable.Admin.Builder.DragFields && typeof Charitable.Admin.Builder.DragFields.ready === 'function' ) {
1294 Charitable.Admin.Builder.DragFields.ready();
1295 }
1296
1297 app.updateFormHiddenFields();
1298 app.updateFormHiddenFieldID();
1299
1300 // if ( ! s.didInitHTMLEditorFields ) {
1301
1302 $( '.campaign-builder-htmleditor' ).each( function() {
1303 app.initHTMLEditorFields( $( this ) );
1304 });
1305 $( '.campaign-builder-htmleditor-min' ).each( function() {
1306 app.initHTMLEditorFields( $( this ), true );
1307 });
1308
1309 s.didInitHTMLEditorFields = true;
1310
1311 // }
1312
1313 // check sections to see if there are fields.
1314 elements.$preview.find('.charitable-field-section').each( function() {
1315 app.checkFieldTargetState( $( this ) );
1316 });
1317
1318 // check recommended fields and check/uncheck depending on what was loaded.
1319 app.checkAllRecommendedFields();
1320
1321 // Goal and End Date checks
1322 app.updateGoalRelatedItems();
1323 app.updateEndDateRelatedItems();
1324
1325 // swiitching over to the design panel view.
1326 if ( panel ) {
1327 app.panelSwitch( 'design' );
1328 }
1329
1330 // Handle fields that will likely appear AFTER a template select or dynamic fields added after a form reset.
1331
1332 // This handles the unique suggested donation area that creates dynamic rows.
1333 $( '.charitable-campaign-suggested-donations-mini' ).each( function() {
1334 app.initSuggestedDonationsMini( $( this ) );
1335 app.updateSuggestdDonationsMiniRowsFromSettings( $( this ) );
1336 });
1337
1338 // Check the field max values and set buttons in "add layout" accordingly.
1339 app.checkFieldAllow();
1340 app.checkFieldMax();
1341
1342
1343 // Was there a description in the template data?
1344 if ( typeof response.data.settings.general.description !== 'undefined' && response.data.settings.general.description != '' ) {
1345 s.campaignDescription = response.data.settings.general.description;
1346 elements.$settingsPanel.find( 'input[name="settings[general][description]"').val( s.campaignDescription );
1347 }
1348
1349 // Finally, make sure to note that the campaign isn't saved.
1350 app.setCampaignNotSaved();
1351
1352 $builder.trigger( 'charitableEditorScreenStart' );
1353
1354 } else {
1355
1356 app.formSaveError( response.data );
1357 }
1358
1359 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
1360
1361 app.formSaveError();
1362
1363 } ).always( function() {
1364
1365 } );
1366
1367 },
1368
1369 /**
1370 * Check and see if it's ok to add/remove the "no fields preview" UI.
1371 *
1372 * @since 1.8.0
1373 *
1374 * @param {array} previewAdvancedOptions Advanced Options
1375 *
1376 */
1377 checkNoFieldsPreview( forceModeOff = false ) {
1378
1379 // note: we ignore fields in special tab sections.
1380 if ( forceModeOff === false && elements.$preview.find('.charitable-field-section[data-section-type="fields"] .charitable-field').length === 0 && typeof( charitable_builder.no_field_preview ) !== 'undefined' ) {
1381 // Is there any fields in preview? If not add the no-fields-preview as it goes into the void.
1382 // $('.charitable-campaign-preview-loader').after( charitable_builder.no_field_preview );
1383 $('#charitable-design-wrap').addClass('no-fields-mode');
1384 if ( $builder.find( '.charitable-no-fields-area' ).hasClass('charitable-hidden') ) {
1385 $builder.find( '.charitable-no-fields-area' ).removeClass('charitable-hidden');
1386 }
1387 } else if ( forceModeOff !== false ) {
1388 if ( ! $builder.find( '.charitable-no-fields-area' ).hasClass('charitable-hidden') ) {
1389 $builder.find( '.charitable-no-fields-area' ).addClass('charitable-hidden');
1390 }
1391 $('#charitable-design-wrap').removeClass('no-fields-mode');
1392 } else {
1393 if ( ! $builder.find( '.charitable-no-fields-area' ).hasClass('charitable-hidden') ) {
1394 $builder.find( '.charitable-no-fields-area' ).addClass('charitable-hidden');
1395 }
1396 $('#charitable-design-wrap').removeClass('no-fields-mode');
1397 }
1398
1399 },
1400
1401 /**
1402 *
1403 *
1404 * @since 1.8.0
1405 *
1406 * @param {array} previewAdvancedOptions Advanced Options
1407 *
1408 */
1409 checkFieldTargetState( $section = false ) {
1410
1411 if ( false === $section ) {
1412 return;
1413 }
1414
1415 if ( $section.data('section-type') !== 'fields' && $section.data('section-type') !== 'tabs' ) {
1416 return;
1417 }
1418
1419 if ( $section.data('section-type') === 'fields' ) {
1420
1421 if ( $section.find('.charitable-field').length === 0 ) {
1422
1423 $section.removeClass('charitable-field-target-inactive').addClass('charitable-field-target');
1424
1425 } else {
1426
1427 $section.removeClass('charitable-field-target').addClass('charitable-field-target-inactive');
1428
1429 }
1430
1431 } else if ( $section.data('section-type') === 'tabs' ) {
1432
1433 if ( $section.find('.tab_content_item.active .charitable-field').length === 0 ) {
1434
1435 $section.find('.tab_content_item.active').removeClass('empty-tab').addClass('empty-tab');
1436
1437 } else {
1438
1439 $section.find('.tab_content_item.active').removeClass('empty-tab');
1440 }
1441
1442 }
1443
1444 },
1445
1446 /**
1447 * Setup values in the advanced tab area based on passed in parameters, likely coming from an ajax call when restarting the form.
1448 *
1449 * @since 1.8.0
1450 *
1451 * @param {array} previewAdvancedOptions Advanced Options
1452 *
1453 */
1454 addAdvancedOptions( previewAdvancedOptions ) {
1455
1456 // update advanced options if they came with the theme
1457 var advancedFieldsInTab = $('.charitable-layout-options-tab-advanced').find('input, select');
1458
1459 advancedFieldsInTab.each(function() {
1460 var $thisInput = $( this ),
1461 advancedFieldId = $thisInput.data('advanced-field-id');
1462
1463 if ( typeof previewAdvancedOptions[advancedFieldId] !== 'undefined' && advancedFieldId != '' ) {
1464
1465 if ( $thisInput.is('select') ) {
1466 $thisInput.val( previewAdvancedOptions[advancedFieldId] ).change();
1467 } else {
1468 $thisInput.val( previewAdvancedOptions[advancedFieldId] );
1469 }
1470
1471 }
1472
1473 });
1474
1475 },
1476
1477 /**
1478 * Add HTML of field options to preview.
1479 *
1480 * @since 1.8.0
1481 *
1482 * @param {string} previewFieldOptions Advanced Options
1483 *
1484 */
1485 addFieldOptions( previewFieldOptions ) {
1486
1487 elements.$fieldOptions.find('.charitable-layout-options-group-inner .charitable-select-field-notice').after( previewFieldOptions );
1488
1489 },
1490
1491 /**
1492 * Add HTML of tab options to preview.
1493 *
1494 * @since 1.8.0
1495 *
1496 * @param {string} previewTabOptions Advanced Options
1497 *
1498 */
1499 addTabOptions( previewTabOptions ) {
1500
1501 elements.$fieldOptions.find('.charitable-layout-options-tab.charitable-layout-options-tab-tabs .charitable-layout-options-group-inner').html( previewTabOptions );
1502
1503 },
1504
1505 /**
1506 * Add HTML of the layout to preview area.
1507 *
1508 * @since 1.8.0
1509 *
1510 * @param {string} previewLayout Advanced Options
1511 *
1512 */
1513 addLayoutToPreview( previewLayout ) {
1514
1515 elements.$preview.find('.charitable-design-wrap').replaceWith( previewLayout );
1516
1517 },
1518
1519 /**
1520 * Add align field listeners to field settings in the left column for design panel.
1521 *
1522 * @since 1.8.0
1523 *
1524 * @param {object} $builder JQuery object.
1525 */
1526 alignFieldEvents: function( $builder ) {
1527
1528 // Number Slider field: update hints.
1529 $builder.on( 'click', '.charitable-panel-field-align a', function( e ) {
1530 e.preventDefault();
1531
1532 var clickedLink = $( this ),
1533 panelField = clickedLink.closest( '.charitable-panel-field' ),
1534 alignValue = '' !== clickedLink.data('align-value') ? clickedLink.data('align-value') : 'center';
1535
1536 panelField.find('span').removeClass('active');
1537 panelField.find('input[type="hidden"').val( alignValue );
1538 clickedLink.parent().addClass('active');
1539 app.setCampaignNotSaved();
1540
1541 } );
1542
1543 },
1544
1545 /**
1546 * Add number slider events listeners to field settings in the left column for design panel.
1547 *
1548 * @since 1.8.0
1549 *
1550 * @param {object} $builder JQuery object.
1551 */
1552 numberSliderEvents: function( $builder ) {
1553
1554 elements.$fieldOptions.on( 'input change', 'input[type="range"]', function( e ) { // eslint-disable-line
1555 var minimum_value = ( $( this ).attr('min-actual') ) ? parseInt( $( this ).attr('min-actual') ) : 0,
1556 scrolled_value = $( this ).val();
1557 if ( minimum_value > 0 ) {
1558 if ( scrolled_value <= minimum_value ) {
1559 $( this ).val( minimum_value );
1560 $( this ).next().addClass('min-reach');
1561 } else {
1562 $( this ).next().removeClass('min-reach');
1563 }
1564 }
1565 app.setCampaignNotSaved();
1566 } );
1567
1568 // Number Slider field: update hints.
1569 $builder.on( 'change input', '.charitable-panel-field-number-slider input[type=range]', function( event ) {
1570 var hintEl = $( event.target ).siblings( '.charitable-number-slider-hint' );
1571 hintEl.attr( 'data-hint', event.target.value );
1572 hintEl.html( event.target.value + hintEl.data('symbol') + '<small>minimum</small>' );
1573 } );
1574
1575 // Minimum update.
1576 $builder.on(
1577 'input',
1578 '.charitable-field-option-row-min_max .charitable-input-row .charitable-number-slider-min',
1579 app.fieldNumberSliderUpdateMin
1580 );
1581
1582 // Maximum update.
1583 $builder.on(
1584 'input',
1585 '.charitable-field-option-row-min_max .charitable-input-row .charitable-number-slider-max',
1586 app.fieldNumberSliderUpdateMax
1587 );
1588
1589 // Change default input value.
1590 $builder.on(
1591 'input',
1592 '.charitable-number-slider-default-value',
1593 _.debounce( app.changeNumberSliderDefaultValue, 500 ) // eslint-disable-line
1594 );
1595
1596 // Change step value.
1597 $builder.on(
1598 'input',
1599 '.charitable-number-slider-step',
1600 _.debounce( app.changeNumberSliderStep, 500 ) // eslint-disable-line
1601 );
1602
1603 // Check step value.
1604 $builder.on(
1605 'focusout',
1606 '.charitable-number-slider-step',
1607 app.checkNumberSliderStep
1608 );
1609
1610 // Change value display.
1611 $builder.on(
1612 'input',
1613 '.charitable-number-slider-value-display',
1614 _.debounce( app.changeNumberSliderValueDisplay, 500 ) // eslint-disable-line
1615 );
1616
1617 // Change min value.
1618 $builder.on(
1619 'input',
1620 '.charitable-number-slider-min',
1621 _.debounce( app.changeNumberSliderMin, 500 ) // eslint-disable-line
1622 );
1623
1624 // Change max value.
1625 $builder.on(
1626 'input',
1627 '.charitable-number-slider-max',
1628 _.debounce( app.changeNumberSliderMax, 500 ) // eslint-disable-line
1629 );
1630 },
1631
1632 /**
1633 * Change number slider min option.
1634 *
1635 * @since 1.8.0
1636 *
1637 * @param {object} event Input event.
1638 */
1639 changeNumberSliderMin: function( event ) {
1640
1641 var fieldID = $( event.target ).parents( '.charitable-field-option-row' ).data( 'fieldId' );
1642 var value = parseFloat( event.target.value );
1643
1644 if ( isNaN( value ) ) {
1645 return;
1646 }
1647
1648 app.updateNumberSliderDefaultValueAttr( fieldID, event.target.value, 'min' );
1649 },
1650
1651 /**
1652 * Change number slider max option.
1653 *
1654 * @since 1.8.0
1655 *
1656 * @param {object} event Input event.
1657 */
1658 changeNumberSliderMax: function( event ) {
1659
1660 var fieldID = $( event.target ).parents( '.charitable-field-option-row' ).data( 'fieldId' );
1661 var value = parseFloat( event.target.value );
1662
1663 if ( isNaN( value ) ) {
1664 return;
1665 }
1666
1667 app.updateNumberSliderDefaultValueAttr( fieldID, event.target.value, 'max' )
1668 .updateNumberSliderStepValueMaxAttr( fieldID, event.target.value );
1669 },
1670
1671 /**
1672 * Change number slider value display option.
1673 *
1674 * @since 1.8.0
1675 *
1676 * @param {object} event Input event.
1677 */
1678 changeNumberSliderValueDisplay: function( event ) {
1679
1680 var str = event.target.value;
1681 var fieldID = $( event.target ).parents( '.charitable-field-option-row' ).data( 'fieldId' );
1682 var defaultValue = document.getElementById( 'charitable-field-option-' + fieldID + '-default_value' );
1683
1684 if ( defaultValue ) {
1685 app.updateNumberSliderHintStr( fieldID, str )
1686 .updateNumberSliderHint( fieldID, defaultValue.value );
1687 }
1688 },
1689
1690 /**
1691 * Change number slider step option.
1692 *
1693 * @since 1.8.0
1694 *
1695 * @param {object} event Input event.
1696 */
1697 changeNumberSliderStep: function( event ) {
1698
1699 var value = parseFloat( event.target.value );
1700
1701 if ( isNaN( value ) ) {
1702 return;
1703 }
1704
1705 var max = parseFloat( event.target.max ),
1706 min = parseFloat( event.target.min ),
1707 fieldID = $( event.target ).parents( '.charitable-field-option-row' ).data( 'fieldId' );
1708
1709 if ( value <= 0 ) {
1710 return;
1711 }
1712
1713 if ( value > max ) {
1714 event.target.value = max;
1715 return;
1716 }
1717
1718 if ( value < min ) {
1719 event.target.value = min;
1720 return;
1721 }
1722
1723 app.updateNumberSliderAttr( fieldID, value, 'step' )
1724 .updateNumberSliderDefaultValueAttr( fieldID, value, 'step' );
1725 },
1726
1727 /**
1728 * Check number slider step option.
1729 *
1730 * @since 1.8.0
1731 *
1732 * @param {object} event Focusout event object.
1733 */
1734 checkNumberSliderStep: function( event ) {
1735
1736 var value = parseFloat( event.target.value ),
1737 $input = $( this );
1738
1739 if ( ! isNaN( value ) && value > 0 ) {
1740 return;
1741 }
1742
1743 $.confirm( {
1744 title : charitable_builder.heads_up,
1745 content : charitable_builder.error_number_slider_increment,
1746 icon : 'fa fa-exclamation-circle',
1747 type : 'orange',
1748 buttons : {
1749 confirm: {
1750 text: charitable_builder.ok,
1751 btnClass: 'btn-confirm',
1752 keys: [ 'enter' ],
1753 action: function() {
1754
1755 $input.val( '' ).trigger( 'focus' );
1756 }
1757 }
1758 }
1759 } );
1760
1761 },
1762
1763 /**
1764 * Change number slider default value option.
1765 *
1766 * @since 1.8.0
1767 *
1768 * @param {object} event Input event.
1769 */
1770 changeNumberSliderDefaultValue: function( event ) {
1771
1772 var value = parseFloat( event.target.value );
1773
1774 if ( ! isNaN( value ) ) {
1775 var max = parseFloat( event.target.max ),
1776 min = parseFloat( event.target.min ),
1777 fieldID = $( event.target ).parents( '.charitable-field-option-row-default_value' ).data( 'fieldId' );
1778
1779 if ( value > max ) {
1780 event.target.value = max;
1781 return;
1782 }
1783
1784 if ( value < min ) {
1785 event.target.value = min;
1786 return;
1787 }
1788
1789 app.updateNumberSlider( fieldID, value )
1790 .updateNumberSliderHint( fieldID, value );
1791 }
1792
1793 },
1794
1795 /**
1796 * Update number slider default value attribute.
1797 *
1798 * @since 1.8.0
1799 *
1800 * @param {number} fieldID Field ID.
1801 * @param {*} newValue Default value attribute.
1802 * @param {*} attr Attribute name.
1803 *
1804 * @returns {object} App instance.
1805 */
1806 updateNumberSliderDefaultValueAttr: function( fieldID, newValue, attr ) {
1807
1808 var input = document.getElementById( 'charitable-field-option-' + fieldID + '-default_value' );
1809
1810 if ( input ) {
1811 var value = parseFloat( input.value );
1812
1813 input.setAttribute( attr, newValue );
1814 newValue = parseFloat( newValue );
1815
1816 if ( 'max' === attr && value > newValue ) {
1817 input.value = newValue;
1818 $( input ).trigger( 'input' );
1819 }
1820
1821 if ( 'min' === attr && value < newValue ) {
1822 input.value = newValue;
1823 $( input ).trigger( 'input' );
1824 }
1825 }
1826
1827 return this;
1828 },
1829
1830 /**
1831 * Update number slider value.
1832 *
1833 * @since 1.8.0
1834 *
1835 * @param {number} fieldID Field ID.
1836 * @param {string} value Number slider value.
1837 *
1838 * @returns {object} App instance.
1839 */
1840 updateNumberSlider: function( fieldID, value ) {
1841
1842 var numberSlider = document.getElementById( 'charitable-number-slider-' + fieldID );
1843
1844 if ( numberSlider ) {
1845 numberSlider.value = value;
1846 }
1847
1848 return this;
1849 },
1850
1851 /**
1852 * Update number slider attribute.
1853 *
1854 * @since 1.8.0
1855 *
1856 * @param {number} fieldID Field ID.
1857 * @param {mixed} value Attribute value.
1858 * @param {*} attr Attribute name.
1859 *
1860 * @returns {object} App instance.
1861 */
1862 updateNumberSliderAttr: function( fieldID, value, attr ) {
1863
1864 var numberSlider = document.getElementById( 'charitable-number-slider-' + fieldID );
1865
1866 if ( numberSlider ) {
1867 numberSlider.setAttribute( attr, value );
1868 }
1869
1870 return this;
1871 },
1872
1873 /**
1874 * Update number slider hint string.
1875 *
1876 * @since 1.8.0
1877 *
1878 * @param {number} fieldID Field ID.
1879 * @param {string} str Hint string.
1880 *
1881 * @returns {object} App instance.
1882 */
1883 updateNumberSliderHintStr: function( fieldID, str ) {
1884
1885 var hint = document.getElementById( 'charitable-number-slider-hint-' + fieldID );
1886
1887 if ( hint ) {
1888 hint.dataset.hint = str;
1889 }
1890
1891 return this;
1892 },
1893
1894 /**
1895 * Update number slider Hint value.
1896 *
1897 * @since 1.8.0
1898 *
1899 * @param {number} fieldID Field ID.
1900 * @param {string} value Hint value.
1901 *
1902 * @returns {object} App instance.
1903 */
1904 updateNumberSliderHint: function( fieldID, value ) {
1905
1906 var hint = document.getElementById( 'charitable-number-slider-hint-' + fieldID );
1907
1908 if ( hint ) {
1909 hint.innerHTML = wpchar.sanitizeHTML( hint.dataset.hint ).replace( '{value}', '<b>' + value + '</b>' );
1910 }
1911
1912 return this;
1913 },
1914
1915 /**
1916 * Update min attribute.
1917 *
1918 * @since 1.8.0
1919 *
1920 * @param {object} event Input event.
1921 */
1922 fieldNumberSliderUpdateMin: function( event ) {
1923
1924 var $options = $( event.target ).parents( '.charitable-field-option-row-min_max' ),
1925 max = parseFloat( $options.find( '.charitable-number-slider-max' ).val() ),
1926 current = parseFloat( event.target.value );
1927
1928 if ( isNaN( current ) ) {
1929 return;
1930 }
1931
1932 if ( max <= current ) {
1933 event.preventDefault();
1934 this.value = max;
1935
1936 return;
1937 }
1938
1939 var fieldId = $options.data( 'field-id' ),
1940 numberSlider = $builder.find( '#charitable-field-' + fieldId + ' input[type="range"]' );
1941
1942 numberSlider.attr( 'min', current );
1943 },
1944
1945 /**
1946 * Update max attribute.
1947 *
1948 * @since 1.8.0
1949 *
1950 * @param {object} event Input event.
1951 */
1952 fieldNumberSliderUpdateMax: function( event ) {
1953 var $options = $( event.target ).parents( '.charitable-field-option-row-min_max' ),
1954 min = parseFloat( $options.find( '.charitable-number-slider-min' ).val() ),
1955 current = parseFloat( event.target.value );
1956
1957 if ( isNaN( current ) ) {
1958 return;
1959 }
1960
1961 if ( min >= current ) {
1962 event.preventDefault();
1963 this.value = min;
1964
1965 return;
1966 }
1967
1968 var fieldId = $options.data( 'field-id' );
1969 var numberSlider = $builder.find( '#charitable-field-' + fieldId + ' input[type="range"]' );
1970
1971 numberSlider.attr( 'max', current );
1972 },
1973
1974 /**
1975 * Update max attribute for step value.
1976 *
1977 * @since 1.8.0
1978 *
1979 * @param {number} fieldID Field ID.
1980 * @param {*} newValue Default value attribute.
1981 *
1982 * @returns {object} App instance.
1983 */
1984 updateNumberSliderStepValueMaxAttr: function( fieldID, newValue ) {
1985
1986 var input = document.getElementById( 'charitable-field-option-' + fieldID + '-step' );
1987
1988 if ( input ) {
1989 var value = parseFloat( input.value );
1990
1991 input.setAttribute( 'max', newValue );
1992 newValue = parseFloat( newValue );
1993
1994 if ( value > newValue ) {
1995 input.value = newValue;
1996 $( input ).trigger( 'input' );
1997 }
1998 }
1999
2000 return this;
2001 },
2002
2003 /**
2004 * Builder was visited via back button in browser.
2005 *
2006 * @since 1.8.0
2007 *
2008 * @returns {boolean} True if the builder was visited via back button in browser.
2009 */
2010 isVisitedViaBackButton: function() {
2011
2012 if ( ! performance ) { // todo: check this.
2013 return false;
2014 }
2015
2016 var isVisitedViaBackButton = false;
2017
2018 performance.getEntriesByType( 'navigation' ).forEach( function( nav ) {
2019 if ( nav.type === 'back_forward' ) {
2020 isVisitedViaBackButton = true;
2021 }
2022 } );
2023
2024 return isVisitedViaBackButton;
2025 },
2026
2027 /**
2028 * Remove loading overlay.
2029 *
2030 * @since 1.8.0
2031 */
2032 hideLoadingOverlay: function() {
2033
2034 var $overlay = $( '#charitable-builder-overlay' );
2035
2036 $overlay.addClass( 'fade-out' );
2037 setTimeout( function() {
2038
2039 $overlay.hide();
2040
2041 }, 200 );
2042 },
2043
2044 /**
2045 * Attempts to load a code editor when the page loads, or at other times called.
2046 *
2047 * @since 1.8.4.3.
2048 */
2049 initCodeEditor: function() {
2050
2051 // if a textarea for a coded field is present, init the code editor.
2052 if ( $( '.campaign-builder-codeeditor' ).length > 0 ) {
2053
2054 var field_id = '';
2055
2056 // get the field id which should be the number after charitable-panel-field-settings-field_html_html_ in the id attr.
2057 // for each element that has the id 'charitable-panel-field-settings-field_html_html_' + field_id, init the editor.
2058 $( '.campaign-builder-codeeditor' ).each( function() {
2059 // does this element have 'charitable-panel-field-settings-field_html_html_' in the id?
2060 if ( $( this ).attr( 'id' ).indexOf( 'charitable-panel-field-settings-field_html_html_' ) !== -1 ) {
2061 field_id = $( this ).attr( 'id' ).replace( 'charitable-panel-field-settings-field_html_html_', '' );
2062 wpchar.debug( 'field_id: ' + field_id );
2063 $builder.trigger( 'charitableFieldAddHTML', [ field_id, 'html' ] );
2064 }
2065 });
2066 }
2067
2068 },
2069
2070 /**
2071 * Show loading overlay.
2072 *
2073 * @since 1.8.0
2074 */
2075 showLoadingOverlay: function() {
2076
2077 var $overlay = $( '#charitable-builder-overlay' );
2078
2079 $overlay.removeClass( 'fade-out' );
2080 $overlay.show();
2081 },
2082
2083 /**
2084 * Update some element of form UI.
2085 * Element and value might be to update something speicfic, if not passed assume it's everything.
2086 *
2087 * @since 1.8.0
2088 *
2089 * @param {string} element Template slug.
2090 * @param {string} value Text/label of template.
2091 *
2092 */
2093 updateFormUI: function( element = false, value = false ) {
2094
2095 var title = false;
2096
2097 // Title.
2098 if ( element === 'title' ) {
2099 title = value;
2100 } else {
2101 title = $( 'input.charitable-form-name' ).val();
2102 }
2103
2104 // Clean the string.
2105 title = title.replace(/[^\w\s\u00C0-\u024F\u1E00-\u1EFF\u2C60-\u2C7F\uA720-\uA7FF _.,!"()'\/$[\]:@#%-]/gi, '');
2106
2107 // Should we allow a blank title even temp?
2108 // if ( '' === $.trim(title) ) {
2109 // title = s.campaignTitle;
2110 // }
2111
2112 // Campaign Title.
2113 elements.$campaignNameTopBanner = $( 'input.charitable-form-name' ); // the campaign title in the top banner
2114 elements.$campaignNamePreview = $( '.charitable-preview .charitable-form-name' ); // in the "preview" window
2115 // elements.$campaignNameGeneralSettings = $( '#charitable-panel-settings #charitable-panel-field-settings-campaign_title' ); // this is the name field in settings -> general
2116 elements.$campaignNameFieldTitleSettings = $('input.charitable-campaign-builder-title'); // this is all the campaign title block input text boxes (left panel)
2117
2118 elements.$campaignNameTopBanner.val( title );
2119 elements.$campaignNamePreview.html( title );
2120 // elements.$campaignNameGeneralSettings.val( title );
2121 elements.$campaignNameFieldTitleSettings.val( title );
2122
2123 elements.$preview.find('.charitable-field-campaign-title .charitable-campaign-builder-placeholder-preview-text').html( '<h1 class="charitable-campaign-title">' + ( title ) + '</h1>' );
2124 // elements.$preview.find('.charitable-field-campaign-title .charitable-campaign-builder-placeholder-preview-text').html( '<h5>' + ( title ) + '</h5>' );
2125
2126 if ( element === 'title' ) {
2127 s.campaignTitle = title;
2128 app.resizeTopCampaignTitleInputBox();
2129 }
2130
2131 },
2132
2133 /**
2134 * Element bindings.
2135 *
2136 * @since 1.8.0
2137 */
2138 bindUIActions: function() {
2139
2140 // Campaign Title.
2141 app.bindAutoResizeCampaignTitle();
2142 app.bindUIEditCampaignTitle();
2143 app.bindCampaignTitleBlockTextField();
2144
2145 // General Panels.
2146 app.bindUIActionsPanels();
2147
2148 // Fields Panel.
2149 app.bindUIActionsFields();
2150
2151 // Preview.
2152 app.bindUIActionsPreview();
2153
2154 // Save and Exit.
2155 app.bindUIActionsSaveExit();
2156
2157 // General/ global.
2158 app.bindUIActionsGeneral();
2159
2160 // Tab related.
2161 app.bindUITabs();
2162
2163 // Listen to form and tag form not as saved if there is a change.
2164 $builder.on( 'change', 'input, textarea, select, checkbox, radio', function( e ) { // eslint-disable-line
2165 app.setCampaignNotSaved();
2166 } );
2167
2168 // Advanced Layout Options
2169 app.bindUILayoutOptionsAdvanced();
2170
2171 // Setting "Reveals" (like Recurring Donations).
2172 app.bindUISettingsRevealGroups();
2173
2174 // Auto-santitize numeric/money fields.
2175 app.bindUIMoneyTextFields();
2176
2177 // Check Field Conditionals
2178 app.checkFieldConditionals();
2179
2180 $builder.on( 'change', 'select#charitable-panel-field-settings-campaign_campaign_creator_id', function( e ) { // eslint-disable-line
2181
2182 app.updateCampaignCreatorInfo();
2183
2184 } );
2185
2186 $builder.on( 'click', 'div#charitable-marketing-form a.button-link, div#charitable-payment-form a.button-link, div#charitable-feedback-form a.button-link', function( e ) { // eslint-disable-line
2187
2188 e.preventDefault();
2189 app.initFeedbackForms( $(this) );
2190
2191 } );
2192
2193 // Handle send-feedback links in template panel description.
2194 $builder.on( 'click', 'a.send-feedback', function( e ) {
2195 e.preventDefault();
2196
2197 // Scroll to the top of the charitable-panel-content-wrap div container.
2198 $('.charitable-panel-content-wrap').animate({
2199 scrollTop: 0
2200 }, 500);
2201
2202 $('.charitable-template-list-container').addClass('disabled');
2203 $('.charitable-feedback-form-container').after('<div id="charitable-builder-underlay" class="charitable-builder-underlay"></div>');
2204 $('.charitable-feedback-form-container').css("opacity", "100" ).css("visibility", "visible" );
2205
2206 $('.charitable-feedback-form-container').find('.charitable-feedback-form-interior').removeClass('charitable-hidden');
2207 $('.charitable-feedback-form-container').find('textarea').val('');
2208 $('.charitable-feedback-form-interior-confirmation').addClass('charitable-hidden');
2209
2210 } );
2211
2212 // Suggested Donation Amounts
2213
2214 $builder.on( 'keyup', 'table.charitable-campaign-suggested-donations-mini input', function( e ) { // eslint-disable-line
2215
2216 var field_id = parseInt( $(this).closest('.charitable-panel-field').data('field-id') );
2217
2218 if ( field_id > 0 ) {
2219
2220 var theIndex = $(this).index( '.charitable-panel-field[data-field-id="' + field_id + '"] table.charitable-campaign-suggested-donations-mini input[type="text"].campaign_suggested_donations' );
2221
2222 app.updateSuggestDonationsSettings( $(this), theIndex );
2223
2224 }
2225
2226 } );
2227
2228 $builder.on( 'change keyup blur input', 'table.charitable-campaign-suggested-donations input[type="text"].campaign_suggested_donations', function( e ) { // eslint-disable-line
2229
2230 var theIndex = $(this).index( '.charitable-campaign-suggested-donations input[type="text"].campaign_suggested_donations' ),
2231 field_name = $(this).attr('name');
2232
2233 if ( field_name.indexOf('recurring') < 1 ) {
2234 app.updateSuggestDonationsSettings( $(this), theIndex );
2235 }
2236
2237
2238 } );
2239
2240 $builder.on( 'change', '.charitable-campaign-builder-allow-custom-donations input[type="checkbox"]', function( e ) { // eslint-disable-line
2241
2242 // make sure all checkboxes with the same "charitable-campaign-builder-allow-custom-donations" class are matching the same value.
2243 $('.charitable-campaign-builder-allow-custom-donations input[type="checkbox"]').prop('checked', $(this).is(':checked') );
2244
2245 app.updateAllowCustomDonationSettings( $( this ).is(':checked') );
2246
2247 } );
2248
2249 $builder.on( 'change', 'input[type="radio"].campaign_suggested_donations', function( e ) { // eslint-disable-line
2250
2251 var field_id = parseInt( $(this).closest('.charitable-panel-field').data('field-id') ), // eslint-disable-line
2252 field_name = $(this).attr('name'); // eslint-disable-line
2253
2254 // do not proceed with updating other suggest amount boxes if the field_name contains the string 'recurring' (only do this for the default non-recurring).
2255 if ( field_name.indexOf('recurring') < 1 ) {
2256 app.updateSuggestedDonationAmountDefault( $( this ).val() );
2257 }
2258
2259 } );
2260
2261
2262 // General/ global.
2263
2264 app.bindUIInputRange();
2265
2266 $builder.on( 'click', '.education-buttons button.update-to-pro-link', function( e ) { // eslint-disable-line
2267
2268 // if there is a link right above this button element, have the window open to the url that is assigned to the href in that link.
2269 if ( $( this ).prev('a').length > 0 ) {
2270 window.open( $( this ).prev('a').attr('href'), '_blank' );
2271 } else {
2272 window.open("https://wpcharitable.com/lite-vs-pro/", "_blank");
2273 }
2274
2275 } );
2276
2277
2278 },
2279
2280 /**
2281 * Element bindings for general panel tasks.
2282 *
2283 * @since 1.8.0
2284 */
2285 bindUILayoutOptionsAdvanced: function() {
2286
2287 // This is the area at the top of the builder where you can change the campaign title.
2288 $builder.on( 'change', 'select#charitable-design-layout-options-advanced-tab-style', function( e ) {
2289 e.preventDefault();
2290
2291 var $this = $( this ),
2292 $preview_nav = $( 'nav.charitable-campaign-preview-nav' );
2293
2294
2295 // get all possible values so we can review any possible css that might exist?
2296 $( $this ).find('option').each(function() {
2297 $preview_nav.removeClass('tab-style-' + this.value );
2298 });
2299
2300 $preview_nav.addClass( 'tab-style-' + $this.find(":selected").val() );
2301
2302 } );
2303
2304 $builder.on( 'change', 'select#charitable-design-layout-options-advanced-tab-size', function( e ) {
2305 e.preventDefault();
2306
2307 var $this = $( this ),
2308 $preview_nav = $( 'nav.charitable-campaign-preview-nav' );
2309
2310 // get all possible values so we can review any possible css that might exist?
2311 $( $this ).find('option').each(function() {
2312 $preview_nav.removeClass('tab-size-' + this.value );
2313 });
2314
2315 $preview_nav.addClass( 'tab-size-' + $this.find(":selected").val() );
2316
2317 } );
2318
2319 },
2320
2321 /**
2322 * Add new group tab.
2323 *
2324 * @since 1.8.0
2325 *
2326 * @param {object} el jQuery element.
2327 * @param {object} e Event.
2328 *
2329 */
2330 tabGroupsAdd: function( el, e ) {
2331
2332 e.preventDefault();
2333
2334 // count the number of tabs currently created.
2335 var count = elements.$sortableTabContent.find('.tab_content_item').length;
2336
2337 if ( count >= s.maxNumberOfTabs ) { // 4 for now is the tab limit
2338 app.formGenericNotice('You cannot have more than ' + s.maxNumberOfTabs + ' tabs in your campaign template.');
2339 return;
2340 }
2341
2342 var $this = $( el ),
2343 $groupLast = $this.parent().find( '.charitable-group.charitable-layout-options-tab-group.hidden' ).last(),
2344 $newGroup = $groupLast.clone(),
2345 tab_content = elements.$preview.find('.tab-content'),
2346 default_tab_content_type = 'html';
2347
2348 // Get the cloned group_id and add +1.
2349 var dataList = $(".charitable-group.charitable-layout-options-tab-group").map(function() {
2350 return parseInt($(this).attr("data-group_id"));
2351 }).get();
2352
2353 var groupID = Math.max.apply(null, dataList) + 1;
2354
2355 if ( typeof groupID === 'undefined' ) {
2356 app.formGenericError('Error ecountered attempting to add another tab.');
2357 return;
2358 }
2359
2360 $newGroup.attr( 'data-group_id', groupID );
2361
2362 // update the data-tab-id.
2363 $newGroup.attr( 'data-tab-id', groupID );
2364 $newGroup.find('.charitable-group-row').attr( 'data-tab-id', groupID );
2365
2366 // Replace the __xx__ in the names of the fields in the clone with the groupID, thereby making them unique.
2367 $newGroup.find('input, select, textarea, label').each( function(index) { // eslint-disable-line
2368 var $this = $( this ),
2369 forAttr = $this.attr('for'),
2370 nameAttr = $this.attr('name'),
2371 idAttr = $this.attr('id');
2372
2373 if ( typeof idAttr !== 'undefined' && idAttr !== false ) {
2374 $this.attr( 'id', $this.attr('id').replace('_xxx_', '_' + groupID + '_') );
2375 }
2376
2377 if ( typeof nameAttr !== 'undefined' && nameAttr !== false ) {
2378 $this.attr( 'name', $this.attr('name').replace('_xxx_', '_' + groupID + '_') );
2379 }
2380
2381 // check if there is a "for" (label) and update that too.
2382 if ( typeof forAttr !== 'undefined' && forAttr !== false ) {
2383 $this.attr( 'for', $this.attr('for').replace('_xxx_', '_' + groupID + '_') );
2384 }
2385
2386 });
2387
2388 // Assign default values to items that have been cloned in the group.
2389 $newGroup.find( 'input[name="tabs__' + groupID + '__title"]' ).val('New Tab');
2390 $newGroup.find( 'textarea[name="tabs__' + groupID + '__desc"]' ).html('Content');
2391
2392 // Whip the active class to reset the tab area on the left.
2393 $('.charitable-layout-options-tab-group').removeClass('active');
2394 $newGroup.addClass('active');
2395
2396 // Make sure the clone is open (not closed) and not hidden.
2397 $newGroup.removeClass('charitable-closed').addClass('charitable-open').removeClass('hidden').find('.charitable-angle-right').removeClass('charitable-angle-right').addClass('charitable-angle-down');
2398
2399 $this.before( $newGroup );
2400
2401 app.bindUIActionPanelsTabs();
2402 app.checkHideTabNavigation();
2403
2404 // Update now the preview area, making the latest new tab the active one (first clearing the actives).
2405 $('nav.charitable-campaign-preview-nav ul li').removeClass('active');
2406 tab_content.find('ul li').removeClass('active');
2407
2408 $('nav.charitable-campaign-preview-nav ul').append('<li data-tab-type="html" data-tab-id="' + groupID + '" class="tab_title active" id="tab_' + groupID + '_title"><a href="#">' + charitable_builder.new_tab + '</a></li>');
2409
2410 tab_content.find('ul').append('<li id="tab_' + groupID + '_content" class="tab_content_item empty-tab active tab_type_' + default_tab_content_type + '" data-tab-type="' + default_tab_content_type + '" data-tab-id="' + groupID + '"><div class="charitable-tab-wrap ui-sortable"><p class="empty-tab-notice">' + charitable_builder.empty_tab + '</p></div></li>');
2411
2412 tab_content.removeClass('empty-tabs');
2413 tab_content.find('.no-tab-notice').remove();
2414
2415 $builder.trigger( 'charitableAddNewTab', groupID );
2416
2417 },
2418
2419 //--------------------------------------------------------------------//
2420 // General Panels
2421 //--------------------------------------------------------------------//
2422
2423 /**
2424 * Element bindings for tabs.
2425 *
2426 * @since 1.8.0
2427 */
2428 bindUITabs: function() {
2429
2430 // Conditional add new group tab.
2431 $builder.on( 'click', 'button.charitable-tab-groups-add', function( e ) {
2432 app.tabGroupsAdd( this, e );
2433 } );
2434
2435 // Moving the tabs around.
2436 $builder.find('.charitable-layout-options-tab-tabs .charitable-layout-options-group-inner').sortable({
2437 handle: '.charitable-draggable',
2438 update: function( event, ui ) { // eslint-disable-line
2439
2440 // get the list of tabs in the fields
2441 var dataList = $(".charitable-group.charitable-layout-options-tab-group").map(function() {
2442 if ( ! $(this).hasClass('hidden') ) {
2443 return parseInt($(this).attr("data-group_id"));
2444 }
2445 }).get();
2446 var activeList = $(".charitable-group.charitable-layout-options-tab-group").map(function() {
2447 if ( $(this).hasClass('active') ) {
2448 return parseInt($(this).attr("data-group_id"));
2449 }
2450 }).get();
2451
2452 $('nav.charitable-campaign-preview-nav ul').empty();
2453
2454 dataList.forEach( function( groupID ) {
2455 // do something with `item`
2456 var $tab = $("#charitable-field-options").find('[data-group_id="' + groupID + '"]'),
2457 textFieldnName = groupID,
2458 isActive = activeList.indexOf( groupID ) >= 0 ? 'active' : '',
2459 tabType = ( groupID !== 0 && '' !== $tab.find('input[name="tabs__' + textFieldnName + '__type"').val() ) ? $tab.find('input[name="tabs__' + textFieldnName + '__type"').val() : 'html',
2460 tabTitle = $tab.find('input[name="tabs__' + textFieldnName + '__title"').val();
2461
2462 $('nav.charitable-campaign-preview-nav ul').append('<li data-tab-type="' + tabType + '" data-tab-id="' + groupID + '" class="tab_title ' + isActive + '" id="tab_' + groupID + '_title"><a href="#">' + tabTitle + '</a></li>');
2463 });
2464
2465 }
2466
2467 });
2468
2469 $builder.on( 'click', 'input, select, textarea, .ql-editor', '.charitable-layout-options-tab-tabs .charitable-layout-options-group-inner .charitable-layout-options-tab-group', function( e ) { // eslint-disable-line
2470
2471 if ( ! $( this ).closest('.charitable-layout-options-tab-group').hasClass( 'active' ) ) {
2472 var group_id = $( this ).closest('.charitable-layout-options-tab-group').data('group_id');
2473 $('.charitable-preview nav.charitable-campaign-preview-nav li[data-tab-id="' + group_id + '"] a').click();
2474 }
2475
2476 } );
2477
2478 // Tab Links that do things in the admin UI.
2479 $builder.on( 'click', 'a.charitable-configure-tab-settings', function( e ) { // eslint-disable-line
2480 // make sure to switch over to the "tabs" tab under layout options
2481 app.fieldTabToggle('layout-options');
2482 $('.charitable-layout-options-tab').removeClass('active');
2483 $('.charitable-layout-options-tab.charitable-layout-options-tab-tabs').addClass('active');
2484 } );
2485
2486
2487 // Clickable Tabs
2488 $builder.on( 'click', 'nav li.tab_title a', function( e ) { // eslint-disable-line
2489
2490 e.preventDefault();
2491
2492 var $preview = $( '.charitable-campaign-preview' ),
2493 tab_id = $( this ).parent().data('tab-id'),
2494 tab_type = $preview.find( 'li#tab_' + tab_id + '_title').attr( 'data-tab-type' ); // eslint-disable-line
2495
2496 // clear the active states of the tabs and content areas in the preview area
2497 $('.tab-content ul li').removeClass('active');
2498 $('nav li.tab_title').removeClass('active');
2499
2500 // make the clicked on tab and it's content area active
2501 $( this ).parent().addClass('active');
2502 $('.tab-content ul li#tab_' + tab_id + '_content').addClass('active');
2503
2504 // make sure to switch over to the "tabs" tab under layout options
2505 $('.charitable-layout-options-tab').removeClass('active');
2506 $('.charitable-layout-options-tab.charitable-layout-options-tab-tabs').addClass('active');
2507
2508 // Using cookies make sure the tabs are open or closed
2509 $( '.charitable-layout-options-tab' ).find('.charitable-layout-options-tab-group').each(function() {
2510 $( this ).removeClass('charitable-open');
2511 var cookieValue = wpCookies.get( 'charitable_panel_layout_options_tabs_tab_open_' + $( this ).data('group_id') );
2512
2513 if ( cookieValue === 'true' ) {
2514 $( this ).addClass('charitable-open');
2515 $( this ).find('.charitable-general-layout-heading a.charitable-toggleable-group i').addClass('charitable-angle-down');
2516 $( this ).find('.charitable-general-layout-heading a.charitable-toggleable-group i').removeClass('charitable-angle-right');
2517 $( this ).removeClass('charitable-closed');
2518 } else {
2519 $( this ).removeClass('charitable-open');
2520 $( this ).find('.charitable-general-layout-heading a.charitable-toggleable-group i').removeClass('charitable-angle-down');
2521 $( this ).find('.charitable-general-layout-heading a.charitable-toggleable-group i').addClass('charitable-angle-right');
2522 $( this ).addClass('charitable-closed');
2523 }
2524 });
2525
2526 // now clear any of the left option panel 'tabs' from being active... then add active to the proper one
2527 $('.charitable-layout-options-tab .charitable-group').removeClass('active');
2528 $(".charitable-layout-options-tab").find('[data-group_id=' + tab_id + ']').addClass('active').removeClass('charitable-closed');
2529 $(".charitable-layout-options-tab").find('[data-group_id=' + tab_id + ']').find('.charitable-group-rows').show();
2530 $(".charitable-layout-options-tab").find('[data-group_id=' + tab_id + ']').find('.charitable-toggleable-group i').removeClass('.charitable-angle-right').addClass('charitable-angle-down');
2531 $( '#layout-options a' ).click();
2532
2533 $("#charitable-preview-tab-container").addClass('active');
2534
2535 wpCookies.set( 'charitable_panel_tab_section_tab_id', tab_id, 2592000 ); // 1 month.
2536 wpCookies.set( 'charitable_panel_layout_options_tabs_tab_open_' + tab_id, true, 2592000 ); // 1 month.
2537
2538 wpCookies.set( 'charitable_panel_content_section', '', 2592000 );
2539 wpCookies.set( 'charitable_panel_active_field_id', '', 2592000 );
2540
2541 } );
2542
2543 // Change the tab type when the select dropdown is changed.
2544 $builder.on( 'change', '.charitable-group select.tab_type', function( e ) {
2545
2546 e.preventDefault();
2547
2548 var $this = $( this ),
2549 group_id = $this.closest('.charitable-group').data('group_id'),
2550 $preview = $( '.charitable-campaign-preview' ),
2551 selected_value = $this.val();
2552
2553 // Update the tabs in the preview area with this new type.
2554 $preview.find('nav li.tab_title[data-tab-id=' + group_id + ']').attr( 'data-tab-type', selected_value );
2555 $preview.find('div.tab-content ul li#tab_' + group_id + '_content').attr( 'data-tab-type', selected_value );
2556 $preview.find('div.tab-content ul li#tab_' + group_id + '_content').removeClass (function (index, className) {
2557 return (className.match (/(^|\s)tab_type_\S+/g) || []).join(' ');
2558 });
2559
2560 $preview.find('div.tab-content ul li#tab_' + group_id + '_content').addClass( 'tab_type_' + selected_value );
2561
2562 var data = {
2563 action : 'charitable_tab_content_preview',
2564 type : selected_value,
2565 group_id : group_id,
2566 nonce : charitable_builder.nonce
2567 };
2568
2569 return $.post( charitable_builder.ajax_url, data, function( response ) {
2570
2571 if ( response.success ) {
2572
2573 $preview.find('div.tab-content ul li#tab_' + group_id + '_content').html( response.data.output );
2574
2575 } else {
2576 app.formSaveError( response.data );
2577 }
2578
2579 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
2580
2581 app.formSaveError();
2582
2583 } ).always( function() {
2584
2585
2586 } );
2587
2588 } );
2589
2590 $builder.on( 'input', 'input#charitable-panel-field-settings-charitable-campaign-enable-tabs', function( e ) { // eslint-disable-line
2591
2592 const isChecked = $( this ).is(':checked');
2593
2594 if ( isChecked ) {
2595 // fade an disable the tab area on the preview.
2596 $("#charitable-preview-tab-container").addClass('disabled');
2597 // also fade and disable the style/size tab attributes in the advanced tab.
2598 $("#charitable-design-layout-options-advanced-tab-style").prop('disabled', true).addClass('disabled');
2599 $("#charitable-design-layout-options-advanced-tab-size").prop('disabled', true).addClass('disabled');
2600 } else {
2601 // remove the fade on preview tabs.
2602 $("#charitable-preview-tab-container").removeClass('disabled');
2603 // unfade and enable the style/size tab attributes in the advanced tab.
2604 $("#charitable-design-layout-options-advanced-tab-style").prop('disabled', false).removeClass('disabled');
2605 $("#charitable-design-layout-options-advanced-tab-size").prop('disabled', false).removeClass('disabled');
2606 }
2607
2608 } );
2609
2610 $builder.on( 'input', 'input.charitable-settings-tab-visible-nav', function( e ) { // eslint-disable-line
2611
2612 const isChecked = $( this ).is(':checked'),
2613 tabID = $( this ).closest('.charitable-tab-title-row').data('tab-id');
2614
2615 if ( isChecked ) {
2616 elements.$preview.find('nav li#tab_' + tabID + '_title').addClass('charitable-tab-hide');
2617 } else {
2618 elements.$preview.find('nav li#tab_' + tabID + '_title').removeClass('charitable-tab-hide');
2619 }
2620
2621 } );
2622
2623 $builder.on( 'keydown', '.charitable-tab-title-row input[type="text"]', function( e ) { // eslint-disable-line
2624 var k = e.keyCode || e.which,
2625 ok = k >= 65 && k <= 90 || // A-Z
2626 ( k >= 96 ) && ( k <= 105 ) || // a-z
2627 ( k >= 35 ) && ( k <= 40 ) || // arrows
2628 ( k == 32 ) || // space
2629 ( e.ctrlKey && k == 65 ) || // Ctrl + A
2630 ( e.ctrlKey && k == 67 ) || // Ctrl + C
2631 ( e.ctrlKey && k == 88 ) || // Ctrl + X
2632 ( e.ctrlKey && k == 86 ) || // Ctrl + V
2633 ( e.metaKey && k == 65 ) || // command + a
2634 ( e.metaKey && k == 67 ) || // command + c
2635 ( e.metaKey && k == 88 ) || // command + x
2636 ( e.metaKey && k == 86 ) || // command + v
2637 ( k >= 96 && k <= 105 ) || // allow numbers entered from a number pad
2638 ( k == 110 || k == 190 ) || // period or decimal point
2639 ( k >= 37 && k <= 40 ) || // allow arrow keys
2640 ( k == 46 ) || // allow delete key
2641 ( k == 173 ) || // dash
2642 ( k == 8 ) || // backspaces
2643 ( k >= 48 && k <= 57 ); // only 0-9 (ok SHIFT options)
2644
2645 if ( !ok || ( e.ctrlKey && e.altKey ) ) {
2646 e.preventDefault();
2647 }
2648
2649 } );
2650
2651 $builder.on( 'change', '.charitable-tab-title-row input[type="text"]', function( e ) { // eslint-disable-line
2652 // If the text input box is empty, then add a default value.
2653 if ( $( this ).val().trim() === '' ) {
2654 $( this ).val( 'New Tab' );
2655 }
2656 } );
2657
2658
2659 },
2660
2661 /**
2662 * Element bindings for input type range UI elements in settings, usually in left panel for 'design'.
2663 *
2664 * @since 1.8.0
2665 */
2666 bindUIInputRange: function() {
2667
2668 /* charitable indicator */
2669
2670 $builder.on( 'mouseenter', 'input[type="range"].charitable-indicator-on-hover, .charitable-panel-field-align a', function( e ) { // eslint-disable-line
2671
2672 var field_id = parseInt( $(this).closest('.charitable-panel-field').data('field-id') );
2673
2674 if ( field_id > 0 ) {
2675
2676 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-preview-field-indicator').removeClass('charitable-hidden');
2677
2678 }
2679
2680 } );
2681
2682 $builder.on( 'mouseleave', 'input[type="range"].charitable-indicator-on-hover, .charitable-panel-field-align a', function( e ) { // eslint-disable-line
2683
2684 var field_id = parseInt( $(this).closest('.charitable-panel-field').data('field-id') );
2685
2686 if ( field_id > 0 ) {
2687
2688 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-preview-field-indicator').addClass('charitable-hidden');
2689
2690 }
2691
2692 } );
2693
2694 },
2695
2696 /**
2697 * Element bindings for campign title fields.
2698 *
2699 * @since 1.8.0
2700 */
2701 bindAutoResizeCampaignTitle: function() {
2702
2703 // Resize the width of the HTML input text field based on the number of characters that are being typed.
2704 $builder.on( 'input', 'input#charitable_settings_title', function( e ) { // eslint-disable-line
2705
2706 var $this = $( this ),
2707 the_text = $this.val(),
2708 the_text_characters = the_text.length,
2709 the_padding = 10,
2710 the_input_box_width = ( the_text_characters * 12.8 ) + the_padding;
2711
2712 wpchar.debug( the_text );
2713 wpchar.debug( the_text_characters );
2714 wpchar.debug( $('#charitable_settings_title').val() );
2715
2716 // $this.css( 'width', the_input_box_width + 'px' );
2717
2718 app.setCampaignNotSaved(); // if the title is changed, that's a trigger.
2719
2720 app.updateFormUI( 'title', the_text );
2721
2722 app.resizeTopCampaignTitleInputBox();
2723
2724 });
2725
2726 // If the field loses focus, then update the title status.
2727 $builder.on( 'focusout', 'input#charitable_settings_title', function( e ) { // eslint-disable-line
2728
2729 // app.setCampaignTitleSet();
2730
2731 });
2732
2733 },
2734
2735 /**
2736 * Element bindings for campign title fields.
2737 *
2738 * @since 1.8.0
2739 */
2740 resizeTopCampaignTitleInputBox: function() {
2741
2742 // var $this = $( 'input#charitable_settings_title' ),
2743 // the_text = $this.val(),
2744 // the_text_characters = the_text.length,
2745 // the_padding = 10,
2746 // the_input_box_width = ( the_text_characters * 12.8 ) + the_padding;
2747
2748 // $this.css( 'width', the_input_box_width + 'px' );
2749
2750 },
2751
2752 /**
2753 * Update the title when it gets changed in the campaign title input text field.
2754 *
2755 * @since 1.8.0
2756 */
2757 bindCampaignTitleBlockTextField: function () {
2758
2759 $builder.on( 'keyup', 'input.charitable-campaign-builder-title', function( e ) { // eslint-disable-line
2760 app.updateFormUI( 'title', $( this ).val() );
2761 } );
2762
2763 },
2764
2765 /**
2766 * Element bindings for campign title fields.
2767 *
2768 * @since 1.8.0
2769 */
2770 bindUIEditCampaignTitle: function() {
2771
2772 // This is the area at the top of the builder where you can change the campaign title.
2773 // $builder.on( 'click', '.charitable-edit-campaign-title-area', function( e ) {
2774 // e.preventDefault();
2775 // app.allowTitleUpdate( $( this ) );
2776 // } );
2777
2778 // $builder.on( 'focusout', 'input#charitable_settings_title', function( e ) { // eslint-disable-line
2779
2780 // $('.charitable-edit-campaign-title-area').removeClass( 'edit' );
2781 // if ( $('#charitable_settings_title' ).val().trim() === '' ) {
2782 // $( '#charitable_settings_title' ).val( charitable_builder.default_campaign_title ); // leave a default title, this should not be blank.
2783 // }
2784 // $('#charitable_settings_title').attr('disabled', true);
2785
2786 // app.updateFormUI( 'title', $('#charitable_settings_title').val() );
2787
2788 // var $this = $( 'input#charitable_settings_title' ),
2789 // the_text = $this.val(),
2790 // the_text_characters = the_text.length,
2791 // the_padding = 10,
2792 // the_input_box_width = ( the_text_characters * 11 ) + the_padding;
2793
2794 // $this.css( 'width', the_input_box_width + 'px' );
2795
2796 // } );
2797
2798 // $(document).on( 'mouseup', $builder, function( e ) {
2799 // if ($(e.target).closest(".charitable-edit-campaign-title-area").length === 0) {
2800 // $('.charitable-edit-campaign-title-area').removeClass( 'edit' );
2801 // if ( $('#charitable_settings_title' ).val().trim() === '' ) {
2802 // $( '#charitable_settings_title' ).val( charitable_builder.default_campaign_title ); // leave a default title, this should not be blank.
2803 // }
2804 // $('#charitable_settings_title').attr('disabled', true);
2805 // }
2806 // app.updateFormUI( 'title', $('#charitable_settings_title').val() );
2807 // } );
2808
2809 },
2810
2811 allowTitleUpdate: function( $container ) {
2812
2813 if ( $container.hasClass( 'edit' ) ) {
2814 $('#charitable_settings_title').attr('disabled', true);
2815 $container.removeClass( 'edit' );
2816 } else {
2817 $('#charitable_settings_title').removeAttr('disabled');
2818 $container.addClass( 'edit' );
2819 $('#charitable_settings_title').focus().select();
2820 }
2821
2822 },
2823
2824 /**
2825 * Element bindings for general panel tasks.
2826 *
2827 * @since 1.8.0
2828 */
2829 bindUIActionsPanels: function() {
2830
2831 // Panel switching.
2832 $builder.on( 'click', '#charitable-panels-toggle button:not(.charitable-panel-help-button):not(.charitable-panel-form-upgrade), .charitable-panel-switch', function( e ) {
2833 e.preventDefault();
2834
2835 // if the button has a disabled class, then we can't switch the panel because it's disabled.
2836 if ( ! $( this ).hasClass('disabled') ) {
2837 app.panelSwitch( $( this ).data( 'panel' ) );
2838 }
2839 } );
2840
2841 // Form panel (Lite) — opens the Pro upgrade modal instead of switching panels.
2842 $builder.on( 'click', '.charitable-panel-form-upgrade', function( e ) {
2843 e.preventDefault();
2844 e.stopImmediatePropagation();
2845 app.formPanelUpgradeModal();
2846 } );
2847
2848 // Help button - allow the link to work by ensuring the anchor's href is followed.
2849 $builder.on( 'click', '.charitable-panel-help-button, #charitable-panels-toggle a:has(.charitable-panel-help-button)', function( e ) {
2850 var $anchor = $( this ).closest( 'a' );
2851 if ( ! $anchor.length ) {
2852 $anchor = $( this ).is( 'a' ) ? $( this ) : $( this ).closest( 'a' );
2853 }
2854 if ( $anchor.length && $anchor.attr( 'href' ) ) {
2855 // Stop event propagation to prevent other handlers from interfering
2856 e.stopPropagation();
2857 e.stopImmediatePropagation();
2858 // Navigate to the URL - let the anchor handle it naturally
2859 var href = $anchor.attr( 'href' );
2860 var target = $anchor.attr( 'target' ) || '_blank';
2861 if ( target === '_blank' ) {
2862 window.open( href, target );
2863 } else {
2864 window.location.href = href;
2865 }
2866 return false;
2867 }
2868 } );
2869
2870 // Panel sections switching.
2871 $builder.on( 'click', '.charitable-panel .charitable-panel-sidebar-section:not(.charitable-need-upgrade):not(.charitable-not-available):not(.charitable-not-installed):not(.charitable-not-activated):not(.charitable-installed-refresh):not(.charitable-not-available):not(.charitable-addon-file-missing)', function( e ) {
2872 app.panelSectionSwitch( this, e );
2873 } );
2874
2875 // Panel sidebar toggle.
2876 $builder.on( 'click', '.charitable-panels .charitable-panel-sidebar-content .charitable-panel-sidebar-toggle', function() {
2877 $( this ).parent().toggleClass( 'charitable-panel-sidebar-closed' );
2878 } );
2879
2880
2881 $builder.on( 'focusout', 'input[name="tabs__campaign__title"]', function( e ) {
2882 e.preventDefault();
2883 app.updateFormUI( 'tabs__campaign__title', $( this ).val() );
2884 } );
2885
2886 app.bindUIActionPanelsTabs();
2887
2888 },
2889
2890 /**
2891 * Element bindings for general panel tasks.
2892 *
2893 * @since 1.8.0
2894 */
2895 bindUIActionPanelsTabs: function() {
2896
2897 $builder.on( 'input', '.charitable-tab-title-row input[type="text"]', function( e ) {
2898
2899 e.preventDefault();
2900
2901 var $textBox = $( this ),
2902 $the_element = $textBox.closest('.charitable-group'),
2903 theGroupID = $the_element.attr("data-group_id"),
2904 updatedText = ( 0 === $textBox.val().length ) ? 'New Tab' : $textBox.val(),
2905 updatedTextClean = app.removeTags( updatedText )
2906
2907 // Change the title of the current tab that the text field is in.
2908 $the_element.find('.charitable-general-layout-heading').find('span').html( updatedTextClean );
2909
2910 // Update in preview area.
2911 $('nav.charitable-campaign-preview-nav ul').find('#tab_' + theGroupID + '_title a').html( updatedTextClean );
2912
2913 } );
2914
2915 },
2916
2917 /**
2918 * This specifically checks to see if we have to force on and disable thie "hide tab navigation" functionality... if there is more than on tab, the user can't hide nav for any tab.
2919 *
2920 * @since 1.8.0
2921 *
2922 * @param {string} theString The string.
2923 *
2924 */
2925 checkHideTabNavigation: function () {
2926
2927 const tab_count = $('#charitable-field-options .charitable-layout-options-tab-group').length - 1;
2928
2929 if ( tab_count > 1 ) {
2930 // disable and force on.
2931 $('#charitable-field-options').find('input.charitable-settings-tab-visible-nav').each(function () {
2932 $( this ).prop( 'checked', false ).attr( 'disabled', true );
2933 $( this ).parent().find('label').addClass( 'charitable-disabled' );
2934 elements.$preview.find('nav.charitable-campaign-preview-nav li').removeClass( 'charitable-tab-hide' );
2935 });
2936 } else {
2937 $('#charitable-field-options').find('input.charitable-settings-tab-visible-nav').each(function () {
2938 $( this ).removeAttr( 'disabled' );
2939 $( this ).parent().find('label').removeClass( 'charitable-disabled' );
2940 });
2941 }
2942
2943 },
2944
2945 /**
2946 * Remove tags from a string.
2947 *
2948 * @since 1.8.0
2949 *
2950 * @param {string} theString The string.
2951 *
2952 */
2953 removeTags: function ( theString ) {
2954
2955 return theString;
2956
2957 // var rex = /(<([^>]+)>)/ig;
2958 // return( theString.replace( rex , "" ) );
2959 },
2960
2961
2962 /**
2963 * Switch Panels.
2964 *
2965 * @since 1.8.0
2966 *
2967 * @param {string} panel Panel slug.
2968 *
2969 * @returns {mixed} Void or false.
2970 */
2971 panelSwitch: function( panel ) {
2972
2973 var $panel = $( '#charitable-panel-' + panel ),
2974 $panelBtn = $( '.charitable-panel-' + panel + '-button' ),
2975 cookieName = 'charitable_panel';
2976
2977 /* check and see if a template has been selected */
2978 if ( ! app.hasTemplate() && panel !== 'template' ) {
2979 return;
2980 }
2981
2982 if ( ! $panel.hasClass( 'active' ) ) {
2983
2984 const event = CharitableUtils.triggerEvent( $builder, 'charitablePanelSwitch', [ panel ] );
2985
2986 // Allow callbacks on `charitablePanelSwitch` to cancel panel switching by triggering `event.preventDefault()`.
2987 if ( event.isDefaultPrevented() || ! charitable_panel_switch ) {
2988 return false;
2989 }
2990
2991 $( '#charitable-panels-toggle' ).find( 'button' ).removeClass( 'active' );
2992 $( '.charitable-panel' ).removeClass( 'active' );
2993 $panelBtn.addClass( 'active' );
2994 $panel.addClass( 'active' );
2995
2996 history.replaceState( {}, null, wpchar.updateQueryString( 'view', panel ) );
2997
2998 $builder.trigger( 'charitablePanelSwitched', [ panel ] );
2999
3000 wpCookies.set( cookieName, panel, 2592000 ); // 1 month.
3001 wpCookies.set( 'charitable_panel_content_section', '', 2592000 );
3002 wpCookies.set( 'charitable_panel_active_field_id', '', 2592000 );
3003 }
3004 },
3005
3006 /**
3007 * Switch Panel section.
3008 *
3009 * @since 1.8.0
3010 *
3011 * @param {object} el DOM element object.
3012 * @param {object} e Event object.
3013 *
3014 */
3015 panelSectionSwitch: function( el, e ) {
3016
3017 if ( e ) {
3018 e.preventDefault();
3019 }
3020
3021 var $this = $( el ),
3022 $panel = $this.parent().parent(),
3023 section = $this.data( 'section' ),
3024 $sectionButtons = $panel.find( '.charitable-panel-sidebar-section' ),
3025 $sectionButton = $panel.find( '.charitable-panel-sidebar-section-' + section );
3026
3027 if ( $this.hasClass( 'upgrade-modal' ) || $this.hasClass( 'education-modal' ) ) {
3028 return;
3029 }
3030
3031 if ( ! $sectionButton.hasClass( 'active' ) ) {
3032 app.panelSectionSwitchTo( section, $panel, $sectionButtons, $sectionButton );
3033 }
3034 },
3035
3036 /**
3037 * Switch Panel section to something specific.
3038 *
3039 * @since 1.8.0
3040 *
3041 * @param {string} section The section slug.
3042 * @param {object} $panel Panel object.
3043 * @param {object} $sectionButtons Buttons object.
3044 * @param {object} $sectionButton Button object.
3045 *
3046 */
3047 panelSectionSwitchTo: function ( section, $panel, $sectionButtons, $sectionButton ) {
3048
3049 const event = CharitableUtils.triggerEvent( $builder, 'charitablePanelSectionSwitch', section );
3050
3051 // Allow callbacks on `charitablePanelSectionSwitch` to cancel panel section switching by triggering `event.preventDefault()`.
3052 if ( event.isDefaultPrevented() || ! charitable_panel_switch ) {
3053 return false;
3054 }
3055
3056 $sectionButtons.removeClass( 'active' );
3057 $sectionButton.addClass( 'active' );
3058 $panel.find( '.charitable-panel-content-section' ).hide();
3059 $panel.find( '.charitable-panel-content-section-' + section ).show();
3060
3061 var cookieName = 'charitable_panel_content_section';
3062 wpCookies.set( cookieName, section, 2592000 ); // 1 month
3063
3064 },
3065
3066 //--------------------------------------------------------------------//
3067 // Fields Panel
3068 //--------------------------------------------------------------------//
3069
3070 /**
3071 * Element bindings for Fields panel.
3072 *
3073 * @since 1.8.0
3074 */
3075 bindUIActionsFields: function() {
3076
3077 // Field sidebar tab toggle.
3078 $builder.on( 'click', '.charitable-tab a', function( e ) {
3079 e.preventDefault();
3080 app.fieldTabToggle( $( this ).parent().attr( 'id' ) );
3081 if ( 'add-layout' === $( this ).parent().attr( 'id' ) ) {
3082 app.resetPreviewArea();
3083 }
3084 } );
3085
3086 $builder.on( 'click', '.charitable-layout-options-tab-tabs .charitable-toggleable-group', function( e ) {
3087 e.preventDefault();
3088 app.fieldGroupTogglev2( $( this ), 'click' );
3089 } );
3090
3091 $builder.on( 'click', '.charitable-add-fields .charitable-toggleable-group', function( e ) {
3092 e.preventDefault();
3093 app.fieldGroupTogglev3( $( this ), 'click' );
3094 } );
3095
3096 // Tab delete.
3097 $builder.on( 'click', '.charitable-tab-group-delete', function( e ) {
3098
3099 e.preventDefault();
3100 e.stopPropagation();
3101
3102 if ( app.isFormPreviewActionsDisabled( this ) ) {
3103 return;
3104 }
3105
3106 var $the_element = $( this ).closest('.charitable-group'),
3107 theGroupID = $the_element.attr("data-group_id");
3108
3109 app.tabDelete( theGroupID );
3110 } );
3111
3112 // Form field preview clicking.
3113 $builder.on( 'click', '.charitable-field', function( e ) {
3114
3115 // Allow clicking on the dismiss button inside the field.
3116 if ( e.target.classList.contains( 'charitable-dismiss-button' ) ) {
3117 return;
3118 }
3119
3120 e.stopPropagation();
3121
3122 $( this ).find('.charitable-field-edit').click();
3123
3124 } );
3125
3126 // Field delete.
3127 $builder.on( 'click', '.charitable-field-delete', function( e ) {
3128
3129 e.preventDefault();
3130 e.stopPropagation();
3131
3132 var confirmation = $( this ).parent().hasClass('charitable-missing-addon-content' ) ? false : true,
3133 field_id = parseInt( $( this ).closest('.charitable-field').data( 'field-id' ) );
3134
3135 app.fieldDelete( field_id, confirmation );
3136
3137 } );
3138
3139 // Field duplicate.
3140 $builder.on( 'click', '.charitable-field-duplicate', function( e ) {
3141
3142 e.preventDefault();
3143 e.stopPropagation();
3144
3145 app.fieldDuplicate( $( this ).parent().data( 'field-id' ) );
3146 } );
3147
3148 // Field edit
3149 $builder.on( 'click', '.charitable-field-edit', function( e ) {
3150
3151 e.preventDefault();
3152 e.stopPropagation();
3153
3154 app.resetPreviewArea();
3155
3156 $( this ).parent().addClass('active');
3157 $('ul.charitable-tabs li#add-layout a').removeClass('active');
3158 $('ul.charitable-tabs li#layout-options a').addClass('active');
3159
3160 var field_id = parseInt( $( this ).parent().data( 'field-id' ) );
3161
3162 wpCookies.set( 'charitable_panel_active_field_id', field_id, 2592000 ); // 1 month.
3163
3164 app.fieldEdit( $( this ).data( 'type' ), $( this ).data( 'section' ), $( this ).parent().data( 'field-id' ), field_id, $( this ).parent().data( 'field-type' ) );
3165 } );
3166
3167 // Field add.
3168 $builder.on( 'click', '.charitable-add-fields-button', function( e ) {
3169
3170 e.preventDefault();
3171
3172 const $field = $( this );
3173
3174 if ( $field.hasClass( 'ui-draggable-disabled' ) ) {
3175 return;
3176 }
3177
3178 let type = $field.data( 'field-type' ),
3179 event = CharitableUtils.triggerEvent( $builder, 'charitableBeforeFieldAddOnClick', [ type, $field ] );
3180
3181 // Allow callbacks on `charitableBeforeFieldAddOnClick` to cancel adding field
3182 // by triggering `event.preventDefault()`.
3183 if ( event.isDefaultPrevented() ) {
3184 return;
3185 }
3186
3187 app.fieldAdd( type, { $sortable: 'default' } );
3188 } );
3189
3190
3191 // New field choices should be sortable
3192 $builder.on( 'charitableFieldAdd', function( event, id, type ) {
3193
3194 const fieldTypes = [
3195 'campaign-title',
3196 'campaign-description',
3197 'campaign-overview',
3198 'progress-bar',
3199 'donation-options',
3200 'social-sharing',
3201 'social-links',
3202 'photo',
3203 'organizer',
3204 'html',
3205 'donate-button',
3206 'donate-amount',
3207 'campaign-summary',
3208 'donate-form',
3209 'donor-wall',
3210 'shortcode',
3211 'text',
3212 'video'
3213 ];
3214
3215 if ( $.inArray( type, fieldTypes ) !== -1 ) {
3216 app.fieldChoiceSortable( type, `#charitable-field-option-row-${id}-choices ul` );
3217 } else {
3218 // not found in the fieldTypes.
3219 }
3220 } );
3221
3222 // Field Options group tabs.
3223 $builder.on( 'click', '.charitable-group-toggle', function( e ) { // was charitable-field-option-group-toggle
3224
3225 const event = CharitableUtils.triggerEvent( $builder, 'charitableFieldOptionGroupToggle' );
3226
3227 // Allow callbacks on `charitableFieldOptionGroupToggle` to cancel tab toggle by triggering `event.preventDefault()`.
3228 if ( event.isDefaultPrevented() ) {
3229 return false;
3230 }
3231
3232 e.preventDefault();
3233
3234 app.resetPreviewArea();
3235
3236 var $group = $( this ).closest( '.charitable-layout-options-tab' );
3237
3238 $group.siblings( '.charitable-layout-options-tab' ).removeClass( 'active' );
3239 $group.addClass( 'active' );
3240
3241 if ( $( this ).parent().hasClass('charitable-layout-options-tab-tabs') ) {
3242 $("#charitable-preview-tab-container").toggleClass('active');
3243 } else {
3244 $("#charitable-preview-tab-container").removeClass('active');
3245 }
3246
3247 if ( $( this ).parent().hasClass('charitable-layout-options-tab-general') ) {
3248 wpCookies.set( 'charitable_panel_design_layout_options_group', 'general', 2592000 ); // 1 month.
3249 } else if ( $( this ).parent().hasClass('charitable-layout-options-tab-tabs') ) {
3250 wpCookies.set( 'charitable_panel_design_layout_options_group', 'tabs', 2592000 ); // 1 month.
3251 } else if ( $( this ).parent().hasClass('charitable-layout-options-tab-advanced') ) {
3252 wpCookies.set( 'charitable_panel_design_layout_options_group', 'advanced', 2592000 ); // 1 month.
3253 }
3254
3255 // reset cookies for anything else
3256 wpCookies.set( 'charitable_panel_active_field_id', '', 2592000 ); // 1 month.
3257
3258 $('.charitable-layout-options-tab-general .charitable-layout-options-group-inner').html();
3259 $('.charitable-campaign-preview .charitable-select-field').removeClass('active');
3260
3261 } );
3262
3263 // Preview
3264 $builder.on( 'click', '.charitable-field-edit', function( e ) {
3265
3266 e.preventDefault();
3267
3268 $( '#layout-options a' ).addClass( 'active' );
3269 $( '.charitable-add-fields' ).hide();
3270 $( '.charitable-field-options' ).show();
3271
3272 var $group = $( '.charitable-layout-options-tab-general' );
3273
3274 $group.siblings( '.charitable-layout-options-tab' ).removeClass( 'active' );
3275 $group.addClass( 'active' );
3276
3277 } );
3278
3279 // On any click check if we had focusout event.
3280 $builder.on( 'click', function() {
3281 app.focusOutEvent();
3282 } );
3283
3284 // Generalized field/block types.
3285
3286 /* photo */
3287 app.photoFieldEvents( $builder );
3288
3289 /* campaign summary */
3290 app.campaignSummaryEvents( $builder );
3291
3292 /* social sharing */
3293 app.socialSharingEvents( $builder );
3294
3295 /* social linking */
3296 app.socialLinkingEvents( $builder );
3297
3298 /* donate button */
3299 app.donateButtonEvents( $builder );
3300
3301 /* donate wall */
3302 app.donateWallEvents( $builder );
3303
3304 /* progress bar */
3305 app.progressBarEvents( $builder );
3306
3307 /* shortcodes */
3308 app.shortcodeEvents( $builder );
3309
3310 /* organizer */
3311 app.organizerEvents( $builder );
3312
3313 // Generalized form fields (in settings/left pane) types.
3314
3315 /* text fields */
3316 app.cssTextFieldEvents( $builder );
3317 app.textFieldEvents( $builder );
3318
3319 /* headlines */
3320 app.headlineEvents( $builder );
3321
3322 /* number sliders */
3323 app.numberSliderEvents( $builder );
3324
3325 /* align */
3326 app.alignFieldEvents( $builder );
3327
3328 / *highlights */
3329 app.highlightEvents( $builder );
3330
3331 // Layout advanced.
3332 app.advancedLayoutOptionsEvents( $builder );
3333
3334 // Preview related.
3335 app.previewHover( $builder );
3336
3337 // Goal related
3338 app.goalEvents( $builder );
3339
3340 // End date related
3341 app.endDateEvents( $builder );
3342
3343 },
3344
3345 /**
3346 * Logic: checks if there is a campaign goal and depending if there is or isn't, hides and disables items in the preview field.
3347 *
3348 * @since 1.8.0
3349 *
3350 * @param {object} $builder JQuery object.
3351 */
3352 endDateEvents: function( $builder ) {
3353
3354 $builder.on( 'change', '#charitable-panel-field-settings-campaign_end_date', function( e ) { // eslint-disable-line
3355
3356 app.updateEndDateRelatedItems( $( this ).val() );
3357
3358 } );
3359
3360 },
3361
3362 /**
3363 * Logic: checks what the end date for the campaign is (if any) and adjusts the preview area accordingly.
3364 *
3365 * @since 1.8.0
3366 *
3367 * @param {object} $builder JQuery object.
3368 */
3369 updateEndDateRelatedItems: function( endDate = '', theField = $('#charitable-panel-field-settings-campaign_end_date') ) {
3370
3371 if ( endDate === '' ) {
3372 // if no end Date has been passed in, get the value from theField if theField exists.
3373 if ( theField.length ) {
3374 endDate = theField.val();
3375 }
3376 }
3377
3378 if ( endDate.trim() === '' ) {
3379
3380 wpchar.debug('updateEndDateRelatedItems - no end date');
3381
3382 // if there is no value, then there is no end date... for each campaign summary field, check and see if it has a charitable-hidden class... and if it doesn't, add it.
3383 elements.$preview.find('.charitable-field-campaign-summary').each(function () {
3384 var field_id = $( this ).attr('data-field-id');
3385
3386 $(this).find('.campaign-time-left.campaign-summary-item').addClass('charitable-hidden');
3387 // ... and also disable the checkbox field to hide the amount raised.
3388 wpchar.debug( $('input[name="_fields[' + field_id + '][show_hide][campaign_hide_time_remaining]' ) );
3389 // uncheck checkbox
3390 elements.$fieldOptions.find( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_time_remaining]' ).attr( 'disabled', true ).addClass('charitable-disabled').prop( 'checked', false ).next().addClass('charitable-disabled');
3391
3392 });
3393
3394 } else {
3395
3396 wpchar.debug('updateEndDateRelatedItems - there is an end date');
3397
3398 elements.$preview.find('.charitable-field-campaign-summary').each(function () {
3399 var field_id = $( this ).attr('data-field-id');
3400
3401 // $(this).find('.campaign-time-left.campaign-summary-item').removeClass('charitable-hidden');
3402
3403 //$(this).find('.campaign-time-left.campaign-summary-item').removeClass('charitable-hidden');
3404 // ... and also enable the checkbox field to hide the amount raised.
3405 elements.$fieldOptions.find( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_time_remaining]' ).attr( 'disabled', false ).removeClass('charitable-disabled').next().removeClass('charitable-disabled');
3406
3407 });
3408
3409 }
3410
3411 },
3412
3413 /**
3414 * Logic: checks if there is a campaign goal and depending if there is or isn't, hides and disables items in the preview field.
3415 *
3416 * @since 1.8.0
3417 *
3418 * @param {object} $builder JQuery object.
3419 */
3420 goalEvents: function( $builder ) {
3421
3422 $builder.on( 'change', '#charitable-panel-field-settings-campaign_goal', function( e ) { // eslint-disable-line
3423
3424 app.updateGoalRelatedItems( $( this ).val() );
3425
3426 } );
3427
3428 },
3429
3430 /**
3431 * Logic: checks if there is a campaign goal and depending if there is or isn't, hides and disables items in the preview field.
3432 *
3433 * @since 1.8.0
3434 *
3435 * @param {object} $builder JQuery object.
3436 */
3437 updateGoalRelatedItems: function( goalAmount = '', theField = $('#charitable-panel-field-settings-campaign_goal') ) {
3438
3439 // If the user adds or removes a goal in settings, then we should adjust the progress bar field in preview live.
3440 wpchar.debug ( goalAmount, 'goalAmount');
3441
3442 if ( goalAmount === '' ) {
3443 // if no Goal Amount has been passed in, get the value from theField if theField exists.
3444 if ( theField.length ) {
3445 goalAmount = theField.val();
3446 }
3447 }
3448
3449 if ( goalAmount === '' || goalAmount === '0' || goalAmount === '0.00' ) {
3450
3451 wpchar.debug('updateGoalRelatedItems - checkpoint 0');
3452
3453 // if there is no value, then there is no goal and therefore no progress bar.
3454 elements.$preview.find('.charitable-field-progress-bar .progress').addClass('charitable-campaign-preview-not-available').addClass('charitable-hidden');
3455
3456 // for each campaign summary field, check and see if it has a charitable-hidden class... and if it doesn't, add it.
3457 elements.$preview.find('.charitable-field-campaign-summary').each(function () {
3458 var field_id = $( this ).attr('data-field-id');
3459
3460 $(this).find('.campaign-raised.campaign-summary-item').addClass('charitable-hidden');
3461 // ... and also disable the checkbox field to hide the amount raised.
3462 wpchar.debug( elements.$fieldOptions.find( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_percent_raised]' ) );
3463 elements.$fieldOptions.find( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_percent_raised]' ).prop( 'checked', false ).attr( 'disabled', true ).addClass('charitable-disabled').next().addClass('charitable-disabled');
3464
3465 });
3466
3467 // update the goal field.
3468 elements.$preview.find('.charitable-field-progress-bar .campaign-goal').each(function () {
3469 var goalLabel = $( this ).find('span').html();
3470 $( this ).html( '<span>' + goalLabel + '</span>' + '∞' );
3471 });
3472
3473 } else {
3474
3475 // does the goalAmount have a decimal point?
3476 var regex_decemial = /^\d+\.\d+$/,
3477 regex_comma = /^\d+\,\d+$/; // eslint-disable-line
3478
3479 if ( regex_comma.test( goalAmount ) ) {
3480
3481 wpchar.debug('updateGoalRelatedItems - checkpoint 1');
3482
3483 app.updateGoalisPresentRelatedUI();
3484
3485 elements.$preview.find('.charitable-field-progress-bar .campaign-goal').each(function () {
3486 var goalLabel = $( this ).find('span').html(),
3487 santitizedValue = parseFloat( goalAmount ).toFixed(2); // eslint-disable-line
3488
3489 wpchar.debug( charitable_builder.currency_symbol + goalAmount );
3490
3491 $( this ).html( '<span>' + goalLabel + '</span>' + ' ' + charitable_builder.currency_symbol + goalAmount );
3492
3493 });
3494
3495 } else if ( regex_decemial.test( goalAmount ) ) {
3496
3497 wpchar.debug('updateGoalRelatedItems - checkpoint 2');
3498
3499 app.updateGoalisPresentRelatedUI();
3500
3501 elements.$preview.find('.charitable-field-progress-bar .campaign-goal').each(function () {
3502 var goalLabel = $( this ).find('span').html(),
3503 santitizedValue = parseFloat( goalAmount ).toFixed(2);
3504
3505 // Add commas to the interger part of the number.
3506 santitizedValue = goalAmount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
3507
3508 $( this ).html( '<span>' + goalLabel + '</span>' + ' ' + charitable_builder.currency_symbol + santitizedValue );
3509
3510 });
3511
3512 } else {
3513
3514 wpchar.debug('updateGoalRelatedItems - checkpoint 3');
3515
3516 app.updateGoalisPresentRelatedUI();
3517
3518 // Remove all characters except commas, decimeals, and numbers.
3519 goalAmount = goalAmount.replace(/[^0-9\.\,]/g, ''); // eslint-disable-line
3520
3521 elements.$preview.find('.charitable-field-progress-bar .campaign-goal').each(function () {
3522 var goalLabel = $( this ).find('span').html();
3523
3524 $( this ).html( '<span>' + goalLabel + '</span>' + ' ' + charitable_builder.currency_symbol + goalAmount );
3525
3526 });
3527
3528 }
3529
3530 }
3531
3532 },
3533
3534 /**
3535 * Updates UI related to the campaign goal in settings being present.
3536 *
3537 * @since 1.8.0
3538 */
3539 updateGoalisPresentRelatedUI: function () {
3540
3541 elements.$preview.find('.charitable-field-campaign-summary').each(function () {
3542 var field_id = $( this ).attr('data-field-id'),
3543 $preview_summary = $( this );
3544
3545 // if the checkbox to show this unchecked don't show it even though a goal has been added.
3546 if ( $( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_percent_raised]' ).is(':checked') ) {
3547 if ( ! $( this ).hasClass('charitable-hidden') ) {
3548 $preview_summary.find('.campaign-raised.campaign-summary-item').removeClass('charitable-hidden');
3549 }
3550 }
3551
3552 // ... and also enable the checkbox field to hide the amount raised.
3553 elements.$fieldOptions.find( 'input[name="_fields[' + field_id + '][show_hide][campaign_hide_percent_raised]' ).attr( 'disabled', false ).removeClass('charitable-disabled').next().removeClass('charitable-disabled');
3554
3555 });
3556
3557 elements.$preview.find('.charitable-field-progress-bar .progress').removeClass('charitable-campaign-preview-not-available').removeClass('charitable-hidden');
3558
3559
3560 },
3561
3562 /**
3563 * Add handlers for "highlighting" certain areas of settings (like the recurring donations area).
3564 *
3565 * @since 1.8.0
3566 *
3567 * @param {object} $builder JQuery object.
3568 */
3569 highlightEvents: function( $builder ) {
3570
3571 // Custom settings button redirects (recurring).
3572 $builder.on( 'click', 'a.charitable-addon-installed-charitable-lite, a.charitable-addon-installed-charitable-pro', function( e ) { // eslint-disable-line
3573 e.preventDefault();
3574
3575 if ( $( this ).hasClass('charitable-addon-recurring-donations') ) {
3576 elements.$settingsPanel.find('.charitable-panel-fields-group-recurring-donations').removeClass('charitable-highlight');
3577 $('a.charitable-panel-sidebar-section-donation-options').click();
3578 // highlight the area when the user goes to the section.
3579 elements.$settingsPanel.find('.charitable-panel-fields-group-recurring-donations').addClass('charitable-highlight');
3580 }
3581
3582 } );
3583
3584 $builder.on( 'webkitAnimationEnd oanimationend msAnimationEnd animationend', '.charitable-panel-fields-group-recurring-donations', function( e ) { // eslint-disable-line
3585 elements.$settingsPanel.find('.charitable-panel-fields-group-recurring-donations').removeClass('charitable-highlight');
3586 } );
3587
3588 },
3589
3590 /**
3591 * Add handlers for changing advanced options settings.
3592 *
3593 * @since 1.8.0
3594 *
3595 * @param {object} $builder JQuery object.
3596 */
3597 advancedLayoutOptionsEvents: function( $builder ) {
3598
3599 $builder.on( 'change', 'select#charitable-design-layout-options-show-field-names', function( e ) { // eslint-disable-line
3600
3601 const theField = $( this );
3602
3603 if ( theField.val() === 'hide' ) {
3604 elements.$formPreview.addClass('charitable-preview-hide-field-names');
3605 } else if ( theField.val() === 'show' ) {
3606 elements.$formPreview.removeClass('charitable-preview-hide-field-names');
3607 }
3608
3609 } );
3610
3611
3612 $builder.on( 'change', 'select#charitable-design-layout-options-preview-mode', function( e ) { // eslint-disable-line
3613
3614 const theField = $( this );
3615
3616 if ( theField.val() === 'normal' ) {
3617 elements.$formPreview.removeClass('charitable-preview-minimum-preview');
3618 } else if ( theField.val() === 'minimum' ) {
3619 elements.$formPreview.addClass('charitable-preview-minimum-preview');
3620 }
3621
3622 } );
3623
3624 },
3625
3626 /**
3627 * Add handlers for hovering over preview (live effect).
3628 *
3629 * @since 1.8.0
3630 *
3631 * @param {object} $builder JQuery object.
3632 */
3633 previewHover: function( $builder ) {
3634
3635 $builder.on( 'mouseover', '.charitable-view-campaign-external-link', function( e ) { // eslint-disable-line
3636
3637 elements.$formPreview.addClass('charitable-preview-live');
3638
3639 } );
3640
3641 $builder.on( 'mouseleave', '.charitable-view-campaign-external-link', function( e ) { // eslint-disable-line
3642
3643 elements.$formPreview.removeClass('charitable-preview-live');
3644
3645 } );
3646
3647 },
3648
3649 /**
3650 * Add handlers for preview: updating the donate button text.
3651 *
3652 * @since 1.8.0
3653 *
3654 * @param {object} $builder JQuery object.
3655 */
3656 donateButtonEvents: function( $builder ) {
3657
3658 $builder.on( 'input', '.charitable-panel-field-text input[type="text"].charitable-campaign-builder-donate-button-button-label', function( e ) { // eslint-disable-line
3659
3660 const theTextBox = $( this ),
3661 field_id = theTextBox.closest('.charitable-panel-field').data('field-id');
3662
3663 app.updateDonateButtonPreview( field_id, theTextBox.attr('name'), theTextBox.val() );
3664
3665 } );
3666
3667 },
3668
3669 /**
3670 * Updating the donate button text.
3671 *
3672 * @since 1.8.0
3673 *
3674 * @param {integer} field_id Field ID.
3675 * @param {string} textFieldName Not currently used.
3676 * @param {string} $label_value Value of text to update.
3677 */
3678 updateDonateButtonPreview: function( field_id = 0, textFieldName = '', label_value = 'Donate' ) { // eslint-disable-line
3679
3680 const preview_field = $('#charitable-field-' + field_id );
3681
3682 label_value = label_value.length > 0 ? CharitableUtils.santitizeTextInput( label_value ) : 'Donate';
3683
3684 preview_field.find('.placeholder').html( label_value );
3685
3686 },
3687
3688 /**
3689 * Add handlers for preview: updating the donate wall text.
3690 *
3691 * @since 1.8.0
3692 *
3693 * @param {object} $builder JQuery object.
3694 */
3695 donateWallEvents: function( $builder ) {
3696
3697 $builder.on( 'input', '.charitable-panel-field.charitable-campaign-builder-donor-wall input, .charitable-panel-field.charitable-campaign-builder-donor-wall select', function( e ) { // eslint-disable-line
3698
3699 const theFormField = $( this ),
3700 field_id = theFormField.closest('.charitable-panel-field').data('field-id');
3701
3702 app.updateDonateWallPreview( field_id, theFormField );
3703
3704 } );
3705
3706 },
3707
3708 /**
3709 * Updating the donate wall text.
3710 *
3711 * @since 1.8.0
3712 *
3713 * @param {integer} field_id Field ID.
3714 * @param {string} theFormField Not currently used.
3715 */
3716 updateDonateWallPreview: function( field_id = 0, theFormField ) { // eslint-disable-line
3717
3718 var data = {};
3719
3720 $('.charitable-panel-field.charitable-campaign-builder-donor-wall[data-field-id="' + field_id + '"]').each(function () {
3721 var theArea = $( this );
3722
3723 theArea.find('input.charitable-checkbox-for-toggle[type="checkbox"]:checked').each(function () {
3724 var theName = $(this).closest('.charitable-toggle-control[data-field-id="' + field_id + '"]').data('ajax-label');
3725 if ( $( this ).is(':checked') ) {
3726 data[theName] = $( this ).val();
3727 } else {
3728 data[theName] = 0;
3729 }
3730 });
3731
3732 theArea.find('input[type="radio"]:checked,input[type="text"],input[type="number"],select').each(function () {
3733 var theName = $(this).closest('.charitable-panel-field[data-field-id="' + field_id + '"]').data('ajax-label');
3734 data[theName] = $( this ).val();
3735 });
3736
3737 data.campaign_id = s.formID;
3738 data.field_type = 'donation-wall';
3739 data.field_id = field_id;
3740 data.action = 'charitable_builder_field_content_preview',
3741 data.nonce = charitable_builder.nonce
3742
3743 } );
3744
3745 // Disable stuff - move this into it's own function.
3746 app.disableFormActions();
3747
3748 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + field_id + '"]').addClass('charitable-loading');
3749 $('#charitable-field-' + field_id + ' .charitable-preview-field-container span.placeholder').addClass('charitable-loading').parent().prepend('<div class="charitable-loading-spinner preview-ajax"></div>');
3750
3751 return $.post( charitable_builder.ajax_url, data, function( res ) { // eslint-disable-line complexity
3752
3753 if ( ! res.success ) {
3754 wpchar.debug( 'Add field AJAX call is unsuccessful:', res );
3755 return;
3756 }
3757
3758 $('#charitable-field-' + field_id + ' .charitable-preview-field-container').replaceWith( res.data.output );
3759
3760 var theTextBox = $( '.charitable-panel-field-text[data-field-id="' + field_id + '"] input[type="text"].charitable-campaign-builder-headline' );
3761
3762 app.updateHeadlinePreview( field_id, theTextBox.attr('name'), theTextBox.val(), theTextBox );
3763
3764 app.enableFormActions();
3765
3766 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + field_id + '"]').removeClass('charitable-loading');
3767 $('#charitable-field-' + field_id + ' .charitable-preview-field-container span.placeholder').removeClass('charitable-loading').parent().remove('.charitable-loading');
3768
3769 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
3770
3771 wpchar.debug( 'Add field AJAX call failed:', xhr.responseText );
3772
3773 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + field_id + '"]').removeClass('charitable-loading');
3774 $('#charitable-field-' + field_id + ' .charitable-preview-field-container span.placeholder').removeClass('charitable-loading').parent().remove('.charitable-loading');
3775
3776 } ).always( function() {
3777
3778 } );
3779
3780 },
3781
3782 /**
3783 * Add handlers for preview: progress bars.
3784 *
3785 * @since 1.8.0
3786 *
3787 * @param {object} $builder JQuery object.
3788 */
3789 progressBarEvents: function( $builder ) {
3790
3791 $builder.on( 'change', '.charitable-panel-field.charitable-campaign-builder-progress-bar input, .charitable-panel-field.charitable-campaign-builder-progress-bar select', function( e ) { // eslint-disable-line
3792
3793 var theFormField = $( this ),
3794 field_id = theFormField.attr('data-field-id');
3795
3796 app.progressBarEventsPreview( field_id, theFormField );
3797
3798 } );
3799
3800 $builder.on( 'keyup', '.charitable-panel-field.charitable-panel-field-text input.donate_label, .charitable-panel-field.charitable-panel-field-text input.donate_goal', function( e ) { // eslint-disable-line
3801
3802 var theFormField = $( this ),
3803 field_id = theFormField.attr('data-field-id');
3804
3805 app.progressBarEventsPreviewLabels( field_id, theFormField );
3806
3807 } );
3808
3809 },
3810
3811 /**
3812 * Updating the labels for the progress bar field.
3813 *
3814 * @since 1.8.0
3815 *
3816 * @param {integer} field_id Field ID.
3817 * @param {string} theFormField Not currently used.
3818 */
3819 progressBarEventsPreviewLabels: function( field_id = 0, theFormField = '' ) {
3820
3821 const preview_field = $('#charitable-field-' + field_id );
3822
3823 if ( theFormField.hasClass('donate_label') ) {
3824 preview_field.find('.campaign-percent-raised span' ).html( theFormField.val() );
3825 }
3826 if ( theFormField.hasClass('donate_goal') ) {
3827 preview_field.find('.campaign-goal span' ).html( theFormField.val() );
3828 }
3829
3830 },
3831
3832 /**
3833 * Updating the fields for the progress bar field.
3834 *
3835 * @since 1.8.0
3836 *
3837 * @param {integer} field_id Field ID.
3838 * @param {string} theFormField Not currently used.
3839 */
3840 progressBarEventsPreview: function( field_id = 0, theFormField = '' ) {
3841
3842 const preview_field = $('#charitable-field-' + field_id );
3843
3844 if ( theFormField.val() === 'show_donated' ) {
3845 preview_field.find('.campaign-percent-raised' ).toggleClass('charitable-hidden');
3846 }
3847 if ( theFormField.val() === 'show_goal' ) {
3848 preview_field.find('.campaign-goal' ).toggleClass('charitable-hidden');
3849 }
3850
3851 },
3852
3853 /**
3854 * Add handlers for preview: CSS text boxes.
3855 *
3856 * @since 1.8.0
3857 *
3858 * @param {object} $builder JQuery object.
3859 */
3860 cssTextFieldEvents: function( $builder ) {
3861
3862 $builder.on( 'input', '.charitable-panel-field-text input[data-ajax-label="css_class"]', function( e ) { // eslint-disable-line
3863
3864 const theTextBox = $( this ),
3865 textboxString = CharitableUtils.santitizeCSSInput( theTextBox.val() ); // Clean the string.
3866
3867 $( this ).val( textboxString );
3868
3869 app.setCampaignNotSaved();
3870
3871 } );
3872
3873 },
3874
3875 /**
3876 * Add handlers for preview: generic text boxes.
3877 *
3878 * @since 1.8.0
3879 *
3880 * @param {object} $builder JQuery object.
3881 */
3882 textFieldEvents: function( $builder ) {
3883
3884 $builder.on( 'input', '.charitable-panel-field-text input[type="text"][data-ajax-label!="css_class"]:not(.charitable-campaign-builder-headline, .charitable-campaign-builder-donate-button-button-label)', function( e ) { // eslint-disable-line
3885
3886 const theTextBox = $( this ),
3887 textboxString = CharitableUtils.santitizeTextInput( theTextBox.val() ); // Clean the string.
3888
3889 $( this ).val( textboxString );
3890
3891 app.setCampaignNotSaved();
3892
3893 } );
3894
3895 },
3896
3897 /**
3898 * Add handlers for preview: headlines.
3899 *
3900 * @since 1.8.0
3901 *
3902 * @param {object} $builder JQuery object.
3903 */
3904 headlineEvents: function( $builder ) {
3905
3906 $builder.on( 'input', '.charitable-panel-field-text input[type="text"].charitable-campaign-builder-headline', function( e ) { // eslint-disable-line
3907
3908 const theTextBox = $( this ),
3909 field_id = theTextBox.closest('.charitable-panel-field').data('field-id');
3910
3911 app.updateHeadlinePreview( field_id, theTextBox.attr('name'), theTextBox.val(), theTextBox );
3912
3913 app.setCampaignNotSaved();
3914
3915 } );
3916
3917 },
3918
3919 /**
3920 * Updating the fields for headlines.
3921 *
3922 * @since 1.8.0
3923 *
3924 * @param {integer} field_id Field ID.
3925 * @param {string} textFieldName Not currently used.
3926 * @param {string} $label_value Value of text to update.
3927 */
3928 updateHeadlinePreview: function( field_id = 0, textFieldName = '', label_value = '', theTextBox ) { // eslint-disable-line
3929
3930 const preview_field = $('#charitable-field-' + field_id ),
3931 headline = CharitableUtils.santitizeTitle( label_value ), // Clean the string.
3932 headline_html = label_value.length > 0 ? '<h5 class="charitable-field-preview-headline">' + headline + '</h5>': '',
3933 tempPlaceholderContainer = preview_field.find('.charitable-placeholder').length > 0 ? '.charitable-placeholder' : '.placeholder',
3934 placeholderContainer = preview_field.find('.charitable-field-preview-social-sharing-headline-container').length > 0 ? '.charitable-field-preview-social-sharing-headline-container' : tempPlaceholderContainer;
3935
3936 theTextBox.val( headline );
3937 preview_field.find( placeholderContainer ).find( 'h5.charitable-field-preview-headline' ).remove();
3938 preview_field.find( placeholderContainer ).first().prepend( headline_html );
3939
3940 },
3941
3942 /**
3943 * Add handlers for preview: shortcodes.
3944 *
3945 * @since 1.8.0
3946 *
3947 * @param {object} $builder JQuery object.
3948 */
3949 shortcodeEvents: function( $builder ) {
3950
3951 $builder.on( 'input', '.charitable-panel-field-text input[type="text"].charitable-campaign-builder-shortcode', function( e ) { // eslint-disable-line
3952
3953 const theTextBox = $( this ),
3954 field_id = theTextBox.closest('.charitable-panel-field').data('field-id');
3955
3956 app.updateShortcodePreview( field_id, theTextBox.attr('name'), theTextBox.val() );
3957
3958 } );
3959
3960 },
3961
3962 /**
3963 * Updating the fields for shortcodes.
3964 *
3965 * @since 1.8.0
3966 *
3967 * @param {integer} field_id Field ID.
3968 * @param {string} textFieldName Not currently used.
3969 * @param {string} $label_value Value of text to update.
3970 */
3971 updateShortcodePreview: function( field_id = 0, textFieldName = '', label_value = '' ) { // eslint-disable-line
3972
3973 const preview_field = $('#charitable-field-' + field_id ),
3974 headline_html = label_value.length > 0 ? '<h5 class="charitable-field-preview-shortcode">' + label_value + '</h5>': '';
3975
3976 preview_field.find('.placeholder.shortcode-preview').find( 'h5.charitable-field-preview-shortcode' ).remove();
3977 preview_field.find('.placeholder.shortcode-preview').first().prepend( headline_html );
3978
3979 },
3980
3981 /**
3982 * Add handlers for preview: organizer.
3983 *
3984 * @since 1.8.0
3985 *
3986 * @param {object} $builder JQuery object.
3987 */
3988 organizerEvents: function( $builder ) {
3989
3990 $builder.on( 'change', '.charitable-panel-field.campaign-builder-campaign-creator-id-mini select', function( e ) { // eslint-disable-line
3991
3992 var theFormField = $( this ),
3993 theAvatarURL = $( this ).find('option:selected').data('avatar'),
3994 field_id = theFormField.closest('.charitable-panel-field').attr('data-field-id');
3995
3996 app.organizerEventsPreview( field_id, theFormField, theAvatarURL );
3997
3998 } );
3999
4000 },
4001
4002 /**
4003 * Updating the fields for social sharing.
4004 *
4005 * @since 1.8.0
4006 *
4007 * @param {integer} field_id Field ID.
4008 * @param {object} $theFormField Value of text to update.
4009 */
4010 organizerEventsPreview: function( field_id = 0, theFormField = '', theAvatarURL = '' ) {
4011
4012 // update the creator dropdown in settings
4013 $('select#charitable-panel-field-settings-campaign_campaign_creator_id').select2( "val", theFormField.val() );
4014
4015 // update the name in the preview area.
4016 const preview_field = $('#charitable-field-' + field_id );
4017 preview_field.find('.charitable-organizer-name').html( theFormField.find("option:selected").text() );
4018 preview_field.find('.charitable-organizer-image').attr( 'style', 'background-image: url(' + theAvatarURL + ');' );
4019
4020 },
4021
4022 /**
4023 * Add handlers for preview: campaign summary
4024 *
4025 * @since 1.8.0
4026 *
4027 * @param {object} $builder JQuery object.
4028 */
4029 campaignSummaryEvents: function( $builder ) {
4030
4031 $builder.on( 'click', '.charitable-campaign-summary-checkboxes input[type="checkbox"]', function( e ) { // eslint-disable-line
4032
4033 const theCheckbox = $( this ),
4034 field_id = theCheckbox.closest('.charitable-panel-field').data('field-id');
4035
4036 app.updateCampaignSummaryPreview( field_id, theCheckbox.attr('name') );
4037
4038 } );
4039
4040 },
4041
4042 /**
4043 * Updating the fields for social sharing.
4044 *
4045 * @since 1.8.0
4046 *
4047 * @param {integer} field_id Field ID.
4048 * @param {string} $checkboxName
4049 */
4050 updateCampaignSummaryPreview: function( field_id = 0, checkboxName = '' ) {
4051
4052 const preview_field = $('#charitable-field-' + field_id );
4053
4054 if ( checkboxName.indexOf( 'campaign_hide_percent_raised' ) >= 0 ) {
4055 preview_field.find('.campaign_hide_percent_raised' ).toggleClass('charitable-hidden');
4056 }
4057 if ( checkboxName.indexOf( 'campaign_hide_amount_donated' ) >= 0 ) {
4058 preview_field.find('.campaign_hide_amount_donated' ).toggleClass('charitable-hidden');
4059 }
4060 if ( checkboxName.indexOf( 'campaign_hide_number_of_donors' ) >= 0 ) {
4061 preview_field.find('.campaign_hide_number_of_donors' ).toggleClass('charitable-hidden');
4062 }
4063 if ( checkboxName.indexOf( 'campaign_hide_time_remaining' ) >= 0 ) {
4064 preview_field.find('.campaign_hide_time_remaining' ).toggleClass('charitable-hidden');
4065 }
4066
4067 },
4068
4069 /**
4070 * Add handlers for preview: social sharing icons.
4071 *
4072 * @since 1.8.0
4073 *
4074 * @param {object} $builder JQuery object.
4075 */
4076 socialSharingEvents: function( $builder ) {
4077
4078 $builder.on( 'click', '.charitable-social-network-checkboxes input[type="checkbox"]', function( e ) { // eslint-disable-line
4079
4080 const theCheckbox = $( this ),
4081 field_id = theCheckbox.closest('.charitable-panel-field').data('field-id');
4082
4083 app.updateSocialSharingPreview( field_id, theCheckbox.attr('name') );
4084
4085 } );
4086
4087 },
4088
4089 /**
4090 * Updating the fields for social sharing.
4091 *
4092 * @since 1.8.0
4093 *
4094 * @param {integer} field_id Field ID.
4095 * @param {string} textFieldName Not currently used.
4096 * @param {string} $label_value Value of text to update.
4097 */
4098 updateSocialSharingPreview: function( field_id = 0, checkboxName = '' ) {
4099
4100 const preview_field = $('#charitable-field-' + field_id );
4101
4102 if ( checkboxName.indexOf( 'twitter' ) >= 0 ) {
4103 preview_field.find('.charitable-social-sharing-preview-twitter' ).toggleClass('charitable-hidden');
4104 }
4105 if ( checkboxName.indexOf( 'facebook' ) >= 0 ) {
4106 preview_field.find('.charitable-social-sharing-preview-facebook' ).toggleClass('charitable-hidden');
4107 }
4108 if ( checkboxName.indexOf( 'linkedin' ) >= 0 ) {
4109 preview_field.find('.charitable-social-sharing-preview-linkedin' ).toggleClass('charitable-hidden');
4110 }
4111 if ( checkboxName.indexOf( 'instagram' ) >= 0 ) {
4112 preview_field.find('.charitable-social-sharing-preview-instagram' ).toggleClass('charitable-hidden');
4113 }
4114 if ( checkboxName.indexOf( 'tiktok' ) >= 0 ) {
4115 preview_field.find('.charitable-social-sharing-preview-tiktok' ).toggleClass('charitable-hidden');
4116 }
4117 if ( checkboxName.indexOf( 'pinterest' ) >= 0 ) {
4118 preview_field.find('.charitable-social-sharing-preview-pinterest' ).toggleClass('charitable-hidden');
4119 }
4120 if ( checkboxName.indexOf( 'mastodon' ) >= 0 ) {
4121 preview_field.find('.charitable-social-sharing-preview-mastodon' ).toggleClass('charitable-hidden');
4122 }
4123 if ( checkboxName.indexOf( 'threads' ) >= 0 ) {
4124 preview_field.find('.charitable-social-sharing-preview-threads' ).toggleClass('charitable-hidden');
4125 }
4126 if ( checkboxName.indexOf( 'bluesky' ) >= 0 ) {
4127 preview_field.find('.charitable-social-sharing-preview-bluesky' ).toggleClass('charitable-hidden');
4128 }
4129
4130 },
4131
4132 /**
4133 * Add handlers for preview: social linking.
4134 *
4135 * @since 1.8.0
4136 *
4137 * @param {object} $builder JQuery object.
4138 */
4139 socialLinkingEvents: function( $builder ) {
4140
4141 $builder.on( 'input', '.charitable-panel-field-text input[type="url"].charitable-campaign-builder-social-links-text-field', function( e ) { // eslint-disable-line
4142
4143 var theTextField = $( this ),
4144 field_id = theTextField.attr('data-field-id');
4145
4146 app.updateSocialLinksPreview( field_id, theTextField.attr('name'), theTextField.val() );
4147
4148 } );
4149
4150 },
4151
4152 /**
4153 * Updating the fields for social links.
4154 *
4155 * @since 1.8.0
4156 *
4157 * @param {integer} field_id Field ID.
4158 * @param {string} textFieldName Not currently used.
4159 * @param {string} $label_value Value of text to update.
4160 */
4161 updateSocialLinksPreview: function( field_id = 0, textFieldName = '', linkURL = '' ) {
4162
4163 const preview_field = $('#charitable-field-' + field_id ),
4164 social_networks = [ 'twitter', 'facebook', 'linkedin', 'instagram', 'tiktok', 'pinterest', 'mastodon', 'youtube', 'threads', 'bluesky' ];
4165
4166 var visibleNetworks = 0;
4167
4168 $.each(
4169 social_networks
4170 ,
4171 function( _index, network ) {
4172
4173 if ( textFieldName.indexOf( network ) >= 0 && app.isValidURL( linkURL ) ) {
4174 preview_field.find('.charitable-social-linking-preview-' + network ).removeClass('charitable-hidden');
4175 } else if ( textFieldName.indexOf( network ) >= 0 ) {
4176 preview_field.find('.charitable-social-linking-preview-' + network ).addClass('charitable-hidden');
4177 }
4178
4179 if ( preview_field.find('.charitable-social-linking-preview-' + network ).hasClass('charitable-hidden') ) {
4180 // Do Nothing.
4181 } else {
4182 visibleNetworks++;
4183 }
4184 }
4185 );
4186
4187 if ( visibleNetworks > 0 ) {
4188 preview_field.find('.charitable-social-linking-no-links').addClass('charitable-hidden');
4189 } else {
4190 preview_field.find('.charitable-social-linking-no-links').removeClass('charitable-hidden');
4191 }
4192
4193 },
4194
4195 /**
4196 * Add handlers for preview: photo field.
4197 *
4198 * @since 1.8.0
4199 *
4200 * @param {object} $builder JQuery object.
4201 */
4202 photoFieldEvents: function( $builder ) {
4203
4204 // WP 3.5+ uploader
4205 var file_frame;
4206 window.formfield = '';
4207
4208 $builder.on( 'click', '.charitable-campaign-builder-upload-button', function( e ) {
4209 e.preventDefault();
4210
4211 var button = $( this ),
4212 field_id = $( this ).closest('.charitable-panel-field').data('field-id');
4213
4214 window.formfield = $( this ).parent().prev();
4215 window.field_id = field_id;
4216
4217 // If the media frame already exists, reopen it.
4218 if ( file_frame ) {
4219 //file_frame.uploader.uploader.param( 'post_id', set_to_post_id );
4220 file_frame.open();
4221 return;
4222 }
4223
4224 // Create the media frame.
4225 file_frame = wp.media.frames.file_frame = wp.media( { // eslint-disable-line
4226 title : button.data( 'uploader_title' ),
4227 library : { type: 'image' },
4228 button : { text: button.data( 'uploader_button_text' ) },
4229 multiple : false
4230 } );
4231
4232 file_frame.on( 'menu:render:default', function( view ) {
4233 // Store our views in an object.
4234 const views = {};
4235
4236 // Unset default menu items
4237 view.unset( 'library-separator' );
4238 view.unset( 'gallery' );
4239 view.unset( 'featured-image' );
4240 view.unset( 'embed' );
4241
4242 // Initialize the views in our view object.
4243 view.set( views );
4244 } );
4245
4246 // When an image is selected, run a callback.
4247 file_frame.on( 'select', function() {
4248 const selection = file_frame.state().get( 'selection' );
4249 selection.each( function( attachment, index ) { // eslint-disable-line
4250 attachment = attachment.toJSON();
4251 window.formfield.val( attachment.url );
4252 app.updateImagePhotoPreview( window.field_id, attachment.url );
4253 window.field_id = false;
4254 } );
4255 } );
4256
4257 // Finally, open the modal
4258 file_frame.open();
4259
4260 } );
4261
4262 $builder.on( 'click', '.charitable-campaign-builder-clear-button', function( e ) {
4263 e.preventDefault();
4264
4265 const button = $( this ),
4266 field_id = $( this ).closest('.charitable-panel-field').data('field-id');
4267
4268 button.closest('.charitable-internal').find('input[type="url"]').val('');
4269 app.updateImagePhotoPreview( field_id, false );
4270
4271 } );
4272
4273 },
4274
4275 /**
4276 * Updating the photo of the photo field in the preview area.
4277 *
4278 * @since 1.8.0
4279 *
4280 * @param {integer} field_id Field ID.
4281 * @param {string} imageUrl The URL of the image file.
4282 */
4283 updateImagePhotoPreview: function( field_id = 0, imageUrl = '' ) {
4284
4285 const preview_field = $('#charitable-field-' + field_id ),
4286 preview_field_image = preview_field.find('img.charitable-campaign-builder-preview-photo');
4287
4288
4289 if ( false === imageUrl || '' === imageUrl || ! app.isValidURL( imageUrl ) ) {
4290 preview_field.find('.primary-image-container .primary-image img').remove();
4291 preview_field.find('.primary-image-container .primary-image').append('<img src="../../images/campaign-builder/fields/photo/temp-icon.svg" class="temp-icon" alt="" />');
4292 preview_field.find('.primary-image-container').removeClass('has-image');
4293
4294 } else {
4295
4296 preview_field.find('i.temp-icon').remove();
4297
4298 if ( preview_field_image.length > 0 ) {
4299
4300 preview_field_image.attr('src', imageUrl );
4301
4302 } else {
4303
4304 preview_field.find('.primary-image-container .primary-image').append('<img src="' + imageUrl + '" class="charitable-campaign-builder-preview-photo" />');
4305 preview_field.find('.primary-image-container').addClass('has-image');
4306 }
4307
4308 }
4309
4310 },
4311
4312 /**
4313 * A utiliay funciton that checks to see if an input is a valid URL.
4314 *
4315 * @since 1.8.0
4316 *
4317 * @param {string} str The URL of the image file.
4318 */
4319 isValidURL: function( str ) {
4320
4321 var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
4322 '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
4323 '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
4324 '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
4325 '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
4326 '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
4327 return !!pattern.test(str);
4328
4329 },
4330
4331 /**
4332 * General reset of the preview area (maybe when tabs and/or fields are added and/or removed and/or moved)
4333 *
4334 * @since 1.8.0
4335 *
4336 * @param {string} str The URL of the image file.
4337 */
4338 resetPreviewArea: function() {
4339
4340 elements.$formPreview.find('.charitable-field').each(function() {
4341 $( this ).removeClass( 'active' );
4342 });
4343
4344 $('.charitable-select-field-notice').show();
4345 $('.charitable-layout-options-tab-general .charitable-panel-field').removeClass('active');
4346
4347 $("#charitable-preview-tab-container").removeClass('active');
4348
4349 },
4350
4351 /**
4352 * Delete a field.
4353 *
4354 * @param {int} id Field ID.
4355 * @param {boolean} confirmation Show a confirmation popup - true or false.
4356 *
4357 * @since 1.8.0
4358 */
4359 fieldDelete: function( id, confirmation = true ) {
4360
4361 var $field = $( '#charitable-field-' + id ),
4362 type = $field.data( 'field-type' );
4363
4364 if ( $field.hasClass( 'no-delete' ) ) {
4365 app.youCantRemoveFieldPopup();
4366 return;
4367 }
4368
4369 if ( confirmation ) {
4370 app.confirmFieldDeletion( id, type );
4371 } else {
4372 app.fieldDeleteById( id, type );
4373 }
4374 },
4375
4376 /**
4377 * Show the confirmation popup before the field deletion.
4378 *
4379 * @param {int} id Field ID.
4380 * @param {string} type Field type.
4381 *
4382 * @since 1.8.0
4383 */
4384 confirmFieldDeletion: function( id, type ) {
4385
4386 var fieldData = {
4387 'id' : id,
4388 'message' : charitable_builder.delete_confirm
4389 };
4390
4391 var event = CharitableUtils.triggerEvent( $builder, 'charitableBeforeFieldDeleteAlert', [ fieldData, type ] );
4392
4393 // Allow callbacks on `charitableBeforeFieldDeleteAlert` to prevent field deletion by triggering `event.preventDefault()`.
4394 if ( event.isDefaultPrevented() ) {
4395 return;
4396 }
4397
4398 $.confirm( {
4399 title : false,
4400 content : fieldData.message,
4401 icon : 'fa fa-exclamation-circle',
4402 type : 'orange',
4403 buttons: {
4404 confirm: {
4405 text : charitable_builder.ok,
4406 btnClass : 'btn-confirm',
4407 keys : [ 'enter' ],
4408 action: function() {
4409 app.fieldDeleteById( id, type );
4410 }
4411 },
4412 cancel: {
4413 text: charitable_builder.cancel,
4414 keys: [ 'esc' ]
4415 }
4416 }
4417 } );
4418 },
4419
4420 /**
4421 * Add handlers to create confirmation popup before the campaign deletion.
4422 *
4423 * @since 1.8.0
4424 */
4425 confirmCampaignDeletion: function() {
4426
4427 // Embed form.
4428 $builder.on( 'click', '.charitable-button.alert.delete-campaign', function( e ) {
4429
4430 e.preventDefault();
4431
4432 $.alert( {
4433 title: false,
4434 content : charitable_builder.campaign_delete_confirm,
4435 icon: 'fa fa-info-circle',
4436 type: 'red',
4437 buttons: {
4438 confirm: {
4439 text : charitable_builder.ok,
4440 btnClass : 'btn-confirm',
4441 keys : [ 'enter' ],
4442 action: function() {
4443 // delete the campaign.
4444 }
4445 },
4446 cancel: {
4447 text: charitable_builder.cancel,
4448 keys: [ 'esc' ]
4449 }
4450 }
4451 } );
4452
4453 } );
4454
4455 },
4456
4457 /**
4458 * Remove the field by ID.
4459 *
4460 * @since 1.8.0
4461 *
4462 * @param {int} id Field ID.
4463 * @param {string} type Field type/slug
4464 * @param {int} duration Duration of animation.
4465 */
4466 fieldDeleteById: function( id = false, type = '', duration = 200 ) { // eslint-disable-line
4467
4468 if ( id === false ) {
4469 return;
4470 }
4471
4472 $( `#charitable-field-${id}` ).fadeOut( duration, function() {
4473
4474 const $field = $( this ),
4475 section = $field.closest('.charitable-field-section'),
4476 type = $field.data( 'field-type' ),
4477 max = typeof $field.data('field-max') !== 'undefined' ? parseInt( $field.data('field-max') ) : 99;
4478
4479 $builder.trigger( 'charitableBeforeFieldDelete', [ id, type ] );
4480
4481 $field.remove();
4482 $( '#charitable-field-option-' + id ).remove();
4483 $( '.charitable-field, .charitable-preview-top-bar' ).removeClass( 'active' );
4484
4485 // Remove the settings boxes, assuming they existed.
4486 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + id + '"]').remove();
4487 $('.charitable-select-field-notice').show();
4488
4489 app.checkNoFieldsPreview();
4490 app.checkFieldTargetState( section );
4491
4492 // Possible enable the field button on the left if the field had a max and deleteing it just freed up something.
4493 if ( max === 0 || elements.$preview.find('.charitable-field.charitable-field-donate-amount').length < max ) {
4494 $('#charitable-panel-design button#charitable-add-fields-donate-amount').removeClass('charitable-disabled');
4495 }
4496
4497 // if this was removed in a tab, check to see if the tab is now empty
4498 app.checkIfTabsAreEmpty();
4499
4500 if ( $('.charitable-layout-options-tab.charitable-layout-options-tab-general').hasClass('active') ) {
4501 app.fieldTabToggle( 'add-layout' );
4502 }
4503
4504 const $fieldsOptions = $( '.charitable-field-option' ),
4505 $submitButton = $builder.find( '.charitable-field-submit' );
4506
4507 // No fields remains.
4508 if ( $fieldsOptions.length < 1 ) {
4509 elements.$sortableFieldsWrap.append( elements.$noFieldsPreview.clone() );
4510 elements.$fieldOptions.append( elements.$noFieldsOptions.clone() );
4511 $submitButton.hide();
4512 }
4513
4514 // Only Layout fields remains.
4515 if ( ! $fieldsOptions.filter( ':not(.charitable-field-option-layout)' ).length ) {
4516 $submitButton.hide();
4517 }
4518
4519 // Check and see if deleting this changed any enabling of add field buttons on the left.
4520 app.checkFieldAllow();
4521 app.checkFieldMax( type, max );
4522
4523 // Check and see if this enables/disables a checkmark for recommended buttons on the left.
4524 app.checkRecommendedFields( type );
4525
4526 if ( wpCookies.get( 'charitable_panel_active_field_id' ) === id ) {
4527 wpCookies.set( 'charitable_panel_active_field_id', '', 2592000 ); // 1 month.
4528 }
4529
4530 // Finally, make sure to note that the campaign isn't saved.
4531 app.setCampaignNotSaved();
4532
4533 $builder.trigger( 'charitableFieldDelete', [ id, type ] );
4534
4535 } );
4536 },
4537
4538 /**
4539 * Edit the field by ID.
4540 *
4541 * @since 1.8.0
4542 *
4543 * @param {int} id Field ID.
4544 * @param {string} type Field type (deprecated)
4545 * @param {int} duration Duration of animation.
4546 */
4547 fieldEdit: function ( type, section, edit_field_id, field_id, field_type ) {
4548
4549 if ( section === 'general' || section === 'standard' || section === 'pro' || section === 'recommended' ) {
4550
4551 app.panelSwitch( 'design' );
4552
4553 $('.charitable-select-field-notice').hide();
4554 $('.charitable-layout-options-tab-general .charitable-panel-field').removeClass('active');
4555 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + field_id + '"]').addClass('active');
4556
4557 // If the first setting is a form field go ahead and make that the focus.
4558 $('.charitable-layout-options-tab-general .charitable-panel-field[data-field-id="' + field_id + '"]').find('input[type=text],input[type=button],input[type=range],input[type=url],textarea,select').filter(':visible:first').focus();
4559
4560 if ( 'html' === field_type ) {
4561 // CharitableCampaignBuilderFieldHTML.codemirrorInit( field_id ); // eslint-disable-line
4562 // trigger an event that another JS file can listen for.
4563 $builder.trigger( 'charitableFieldAddHTML', [ field_id, field_type ] );
4564 } else {
4565 $builder.trigger( 'charitableFieldEdit', [ type, section, edit_field_id, field_id, field_type ] );
4566 }
4567
4568 }
4569
4570 },
4571
4572 /**
4573 * Duplicate field.
4574 *
4575 * @since 1.8.0
4576 *
4577 * @param {string} id Field id.
4578 */
4579 fieldDuplicate: function( id ) {
4580
4581 const $field = $( `#charitable-field-${id}` );
4582
4583 if ( $field.hasClass( 'no-duplicate' ) ) {
4584 $.alert( {
4585 title: charitable_builder.field_locked,
4586 content: charitable_builder.field_locked_no_duplicate_msg,
4587 icon: 'fa fa-info-circle',
4588 type: 'blue',
4589 buttons: {
4590 confirm: {
4591 text: charitable_builder.close,
4592 btnClass: 'btn-confirm',
4593 keys: [ 'enter' ]
4594 }
4595 }
4596 } );
4597
4598 return;
4599 }
4600
4601 $.confirm( {
4602 title : false,
4603 content : charitable_builder.duplicate_confirm,
4604 icon : 'fa fa-exclamation-circle',
4605 type : 'orange',
4606 buttons : {
4607 confirm: {
4608 text: charitable_builder.ok,
4609 btnClass: 'btn-confirm',
4610 keys: [ 'enter' ],
4611 action: function() {
4612
4613 // Disable the current button to avoid firing multiple click events.
4614 // By default, "jconfirm" tends to destroy any modal DOM element upon button click.
4615 this.$$confirm.prop( 'disabled', true );
4616
4617 const beforeEvent = CharitableUtils.triggerEvent( $builder, 'charitableBeforeFieldDuplicate', [ id, $field ] );
4618
4619 // Allow callbacks on `charitableFieldBeforeDuplicate` to cancel field duplication.
4620 if ( beforeEvent.isDefaultPrevented() ) {
4621 return;
4622 }
4623
4624 const newFieldId = app.fieldDuplicateRoutine( id ),
4625 $newField = $( `#charitable-field-${newFieldId}` );
4626
4627 CharitableUtils.triggerEvent( $builder, 'charitableFieldDuplicated', [ id, $field, newFieldId, $newField ] );
4628
4629 }
4630 },
4631 cancel: {
4632 text: charitable_builder.cancel,
4633 keys: [ 'esc' ]
4634 }
4635 }
4636 } );
4637
4638 },
4639
4640 /**
4641 * Duplicate field routine.
4642 *
4643 * @since 1.8.0
4644 *
4645 * @param {integer|number|string} id Field Id.
4646 *
4647 * @returns {number} New field Id.
4648 */
4649 fieldDuplicateRoutine: function( id ) { // eslint-disable-line max-lines-per-function, complexity
4650
4651 const $field = $( `#charitable-field-${id}` ),
4652 $fieldActiveFields = elements.$sortableFieldsWrap.find( '> .active' ),
4653 $fieldActiveTabs = elements.$sortableFieldsWrap.find( '> .active' ),
4654 $newField = $field.clone(),
4655 newFieldID = parseInt( elements.$nextFieldId.val(), 10 ) + 1,
4656 $settingsArea = $( '#charitable-field-options .charitable-layout-options-group-inner .charitable-panel-field[data-field-id="' + id + '"]' );
4657
4658 if ( $newField ) {
4659 // Update next field id hidden input value.
4660 app.updateFormHiddenFieldID( newFieldID );
4661 }
4662
4663 // Toggle visibility states.
4664 $field.after( $newField );
4665 $fieldActiveFields.removeClass( 'active' );
4666 $fieldActiveTabs.removeClass( 'active' );
4667 $newField.addClass( 'active' ).attr( {
4668 'id' : `charitable-field-${newFieldID}`,
4669 'data-field-id': newFieldID
4670 } );
4671
4672 $settingsArea.each(function(){
4673 var $newSettingsField = $(this).clone(),
4674 isContains = $newSettingsField.text().indexOf('ID:') > -1;
4675
4676 $newSettingsField.attr('data-field-id', newFieldID );
4677
4678 $(this).removeClass('active');
4679 $newSettingsField.addClass('active');
4680
4681 if ( isContains ) {
4682 $newSettingsField.text(function(index,text){
4683 return text.replace( 'ID: ' + id, 'ID: ' + newFieldID );
4684 });
4685 }
4686
4687 if ( typeof $newSettingsField.attr('name') !== 'undefined' && $newSettingsField.attr('name').length > 0 ) {
4688 $newSettingsField.attr('name', $newSettingsField.attr('name').replace( id, newFieldID ) );
4689 }
4690 if ( typeof $newSettingsField.attr('id') !== 'undefined' && $newSettingsField.attr('id').length > 0 ) {
4691 $newSettingsField.attr('id', $newSettingsField.attr('id').replace( id, newFieldID ) );
4692 }
4693
4694 $newSettingsField.find('*').each(function () {
4695
4696 var $subField = $( this );
4697
4698 if ( typeof $subField.attr('for') !== 'undefined' && $subField.attr('for').length > 0 ) {
4699 $subField.attr('for', $subField.attr('for').replace( id, newFieldID ) );
4700 }
4701 if ( typeof $subField.attr('name') !== 'undefined' && $subField.attr('name').length > 0 ) {
4702 $subField.attr('name', $subField.attr('name').replace( id, newFieldID ) );
4703 }
4704 if ( typeof $subField.attr('id') !== 'undefined' && $subField.attr('id').length > 0 ) {
4705 $subField.attr('id', $subField.attr('id').replace( id, newFieldID ) );
4706 }
4707 if ( typeof $subField.data('field-id') !== 'undefined' && $subField.data('field-id').length > 0 ) {
4708 $newSettingsField.attr('data-field-id', newFieldID );
4709 }
4710
4711
4712 });
4713
4714 $newSettingsField.find('.charitable-toggle-control').attr('data-field-id', newFieldID );
4715 $newSettingsField.find('.charitable-checkbox-for-toggle').attr('data-field-id', newFieldID );
4716
4717 $('#charitable-field-options .charitable-layout-options-tab-general .charitable-layout-options-group-inner').append( $newSettingsField );
4718
4719 // Is this setting a WYSIWYG field for a campaign description?
4720 if ( typeof $newSettingsField.data('special-type') !== 'undefined' && $newSettingsField.data('special-type') === 'campaign_description' ) {
4721 // replace the current field in the "each" with a field then init it for the quill.
4722 let contentToCopy = $newSettingsField.find('.campaign-builder-htmleditor .ql-editor').html();
4723 $newSettingsField.find( '.ql-toolbar' ).remove();
4724 $newSettingsField.find('.campaign-builder-htmleditor').html( '<div data-textarea-name="_fields[' + newFieldID + '][content]" id="charitable-panel-field-settings-field_campaign-description_html_' + newFieldID + '" class="campaign-builder-htmleditor">' + contentToCopy + '</div>' );
4725 app.initHTMLEditorFields( $newSettingsField.find('.campaign-builder-htmleditor'), false );
4726 }
4727
4728 // Check and see if duplicated this changed any enabling of add field buttons on the left.
4729 app.checkFieldAllow();
4730 app.checkFieldMax();
4731
4732
4733 });
4734
4735 return newFieldID;
4736
4737 },
4738
4739 /**
4740 * Check if we had focusout event from certain fields.
4741 *
4742 * @since 1.8.0
4743 */
4744 focusOutEvent: function() {
4745 if ( elements.$focusOutTarget === null ) {
4746 return;
4747 }
4748
4749 elements.$focusOutTarget = null;
4750 },
4751
4752 /**
4753 * Determine if form wrapper has sorting locked.
4754 *
4755 * @since 1.8.0
4756 *
4757 * @param {mixed} el DOM element or jQuery object of some container on the field preview.
4758 *
4759 * @returns {bool} True if form preview wrapper sorting is disabled.
4760 */
4761 isFormPreviewActionsDisabled: function( el ) {
4762
4763 return $( el ).closest( '.charitable-field-wrap' ).hasClass( 'ui-sortable-disabled' );
4764 },
4765
4766 /**
4767 * Toggle field group visibility in the field sidebar.
4768 *
4769 * @since 1.8.0
4770 *
4771 * @param {mixed} el DOM element or jQuery object.
4772 * @param {string} action Action.
4773 */
4774 fieldGroupTogglev2: function( el, action ) {
4775
4776 var $this = $( el ),
4777 groupName = $this.closest('.charitable-group').data( 'group_id' ),
4778 $nearestGroup = $this.closest('.charitable-group'),
4779 $rows = $nearestGroup.find( '.charitable-group-rows' ),
4780 $group = $rows.parent(), // eslint-disable-line
4781 $icon = $this.find( 'i' ),
4782 cookieName = 'charitable_panel_layout_options_tabs_tab_open_' + groupName;
4783
4784 if ( action === 'click' ) {
4785
4786 $icon.toggleClass( 'charitable-angle-right' );
4787 $rows.stop().slideToggle( '', function() {
4788 $nearestGroup.toggleClass( 'charitable-closed' );
4789 if ( $nearestGroup.hasClass( 'charitable-closed' ) ) {
4790
4791 $nearestGroup.removeClass( 'charitable-open' );
4792
4793 wpCookies.remove( cookieName );
4794 } else {
4795 wpCookies.set( cookieName, 'true', 2592000 ); // 1 month
4796 }
4797 } );
4798
4799 return;
4800 }
4801
4802 },
4803
4804 /**
4805 * Toggle field group visibility in the field sidebar.
4806 *
4807 * @since 1.8.0
4808 *
4809 * @param {mixed} el DOM element or jQuery object.
4810 * @param {string} action Action.
4811 */
4812 fieldGroupTogglev3: function( el, action = 'click' ) {
4813
4814 var $this = $( el ),
4815 $nearestGroup = $this.closest('.charitable-add-fields-group'),
4816 $rows = $nearestGroup.find( '.charitable-group-rows' ),
4817 $icon = $this,
4818 groupName = 'test',
4819 cookieName = 'charitable_panel_add_layout_blocks_open_' + groupName;
4820
4821 if ( action === 'click' ) {
4822
4823 $icon.toggleClass( 'charitable-angle-right' );
4824 $rows.stop().slideToggle( '', function() {
4825 $nearestGroup.toggleClass( 'charitable-closed' );
4826 if ( $nearestGroup.hasClass( 'charitable-closed' ) ) {
4827
4828 $nearestGroup.removeClass( 'charitable-open' );
4829
4830 wpCookies.remove( cookieName );
4831 } else {
4832
4833 wpCookies.set( cookieName, 'true', 2592000 ); // 1 month
4834 }
4835 } );
4836
4837 return;
4838 }
4839
4840 },
4841
4842 /**
4843 * Toggle field group visibility in the field sidebar.
4844 *
4845 * @since 1.8.0
4846 *
4847 * @param {mixed} el DOM element or jQuery object.
4848 * @param {string} action Action.
4849 */
4850 fieldGroupToggle: function( el, action ) {
4851
4852 var $this = $( el ),
4853 $buttons = $this.next( '.charitable-add-fields-buttons' ),
4854 $group = $buttons.parent(),
4855 $icon = $this.find( 'i' ),
4856 groupName = $this.data( 'group' ),
4857 cookieName = 'charitable_field_group_' + groupName;
4858
4859 if ( groupName == 'general-layout-campaign' || groupName == 'general-layout-faq' ) {
4860 //$buttons = $this.next('.charitable-field-option-row');
4861 $group = $this.parent().find('.charitable-' + groupName + '-group-inner');
4862 $buttons = $group;
4863 }
4864
4865 if ( action === 'click' ) {
4866
4867 if ( $group.hasClass( 'charitable-closed' ) ) {
4868 wpCookies.remove( cookieName );
4869 } else {
4870 wpCookies.set( cookieName, 'true', 2592000 ); // 1 month
4871 }
4872 $icon.toggleClass( 'charitable-angle-right' );
4873 $buttons.stop().slideToggle( '', function() {
4874 $group.toggleClass( 'charitable-closed' );
4875 } );
4876
4877 return;
4878
4879 } else if ( action === 'load' ) {
4880
4881 $buttons = $this.find( '.charitable-add-fields-buttons' );
4882 $icon = $this.find( '.charitable-add-fields-heading i' );
4883 groupName = $this.find( '.charitable-add-fields-heading' ).data( 'group' );
4884 cookieName = 'charitable_field_group_' + groupName;
4885
4886 if ( wpCookies.get( cookieName ) === 'true' ) {
4887 $icon.toggleClass( 'charitable-angle-right' );
4888 $buttons.hide();
4889 $this.toggleClass( 'charitable-closed' );
4890 }
4891 }
4892 },
4893
4894 /**
4895 * Add new field.
4896 *
4897 * @since 1.8.0
4898 *
4899 * @param {string} type Field type.
4900 * @param {object} options Additional options.
4901 *
4902 */
4903 fieldMove: function( type, options ) { // eslint-disable-line max-lines-per-function
4904
4905 var $field = options.field;
4906
4907 $field.find('.charitable-field-edit').click();
4908
4909 // remove disabled from add fields.
4910 $builder.find( '.charitable-add-fields .charitable-add-fields-button' ).prop( 'disabled', false );
4911
4912 // if a field moved, check the section it landed on and adjust the field target state.
4913 var section = $field.closest('.charitable-field-section');
4914 app.checkFieldTargetState( section );
4915
4916 // if a field moved, also check where it came FROM to see if it's now empty.
4917 if ( options.section ) {
4918 app.checkFieldTargetState( options.section );
4919 }
4920
4921 // add a trigger for anything else that wants to hook.
4922 $builder.trigger( 'charitableFieldMove', [ options.fieldId, type ] );
4923
4924 },
4925
4926 /**
4927 * Add new field.
4928 *
4929 * @since 1.8.0
4930 *
4931 * @param {string} type Field type.
4932 * @param {object} options Additional options.
4933 *
4934 * @returns {promise|void} jQuery.post() promise interface.
4935 */
4936 fieldAdd: function( type, options ) { // eslint-disable-line max-lines-per-function
4937
4938 const $btn = $( `#charitable-add-fields-${type}` ); // eslint-disable-line
4939
4940 adding = true;
4941
4942 if ( Charitable.Admin.Builder.DragFields && typeof Charitable.Admin.Builder.DragFields.disableDragAndDrop === 'function' ) {
4943 Charitable.Admin.Builder.DragFields.disableDragAndDrop();
4944 }
4945
4946 app.disableFormActions();
4947
4948 let defaults = {
4949 campaign_title: s.campaignTitle,
4950 position: 'bottom',
4951 $sortable: 'base',
4952 placeholder: false,
4953 scroll: true,
4954 defaults: false,
4955 column_id : options.column_id,
4956 tab_id : options.tab_id,
4957 section_id : options.section_id,
4958 area: 'fields' // vs tabs
4959 };
4960
4961 options = $.extend( {}, defaults, options );
4962
4963 let data = {
4964 action : 'charitable_new_field_' + type,
4965 id : s.formID,
4966 column_id : options.column_id,
4967 tab_id : options.tab_id,
4968 section_id : options.section_id,
4969 field_id : parseInt( elements.$nextFieldId.val() ) + 1,
4970 type : type,
4971 campaign_title : options.campaign_title,
4972 defaults : options.defaults,
4973 nonce : charitable_builder.nonce
4974 };
4975
4976 return $.post( charitable_builder.ajax_url, data, function( res ) { // eslint-disable-line complexity
4977
4978 if ( ! res.success ) {
4979 wpchar.debug( 'Add field AJAX call is unsuccessful:', res );
4980 return;
4981 }
4982
4983 wpchar.debug ('fieldAdd return');
4984
4985 app.refreshTabFieldsSortDrag();
4986
4987 // define the base container.
4988 let $baseFieldsContainer = ( options.area === 'tabs' ) ? elements.$sortableTabContent.find( 'li[data-tab-id="' + data.tab_id + '"] .charitable-tab-wrap' ) : elements.$formPreview.find( '.charitable-field-wrap[data-section-id="' + data.section_id + '"]' );
4989
4990 wpchar.debug( $baseFieldsContainer );
4991
4992 const $newField = $( res.data.preview ),
4993 maxAllowed = typeof res.data.max !== 'undefined' ? parseInt( res.data.max ): 99,
4994 $newOptions = $( res.data.options );
4995
4996 let $fieldContainer = options.$sortable;
4997
4998 adding = false;
4999
5000 $newField.css( 'display', 'none' );
5001
5002 if ( options.placeholder ) {
5003 options.placeholder.remove();
5004 }
5005
5006 if ( options.$sortable === 'default' || ! options.$sortable.length ) {
5007 $fieldContainer = $baseFieldsContainer.find( '.charitable-fields-sortable-default' );
5008 }
5009
5010 if ( options.$sortable === 'base' || ! $fieldContainer.length ) {
5011 $fieldContainer = $baseFieldsContainer;
5012 }
5013
5014 let event = CharitableUtils.triggerEvent(
5015 $builder,
5016 'charitableBeforeFieldAddToDOM',
5017 [ options, $newField, $newOptions, $fieldContainer ]
5018 );
5019
5020 // Allow callbacks on `charitableBeforeFieldAddToDOM` to cancel adding field
5021 // by triggering `event.preventDefault()`.
5022 if ( event.isDefaultPrevented() ) {
5023 return;
5024 }
5025
5026 // Add field to the base level of fields.
5027 // Allow callbacks on `charitableBeforeFieldAddToDOM` to skip adding field to the base level
5028 // by setting `event.skipAddFieldToBaseLevel = true`.
5029 if ( ! event.skipAddFieldToBaseLevel ) {
5030 // wpchar.debug('adding to base level');
5031 app.fieldAddToBaseLevel( options, $newField, $newOptions );
5032 }
5033
5034 // Do we now disable the field add button because of "max"?
5035 if ( elements.$preview.find('.charitable-field.charitable-field-donate-amount').length >= maxAllowed ) {
5036 $('#charitable-panel-design button#charitable-add-fields-donate-amount').addClass('charitable-disabled');
5037 }
5038
5039 // IT IT SHOWTIME... FADE IN THE FIELD.
5040 $newField.fadeIn();
5041
5042 app.checkFieldTargetState( $('.charitable-field-section[data-section-id="' + data.section_id + '"]') );
5043
5044 $newField.find('.charitable-field-edit').click();
5045
5046 if ( $( '.charitable-field-option:not(.charitable-field-option-layout)' ).length ) {
5047 $builder.find( '.charitable-field-submit' ).show();
5048 }
5049
5050 // Update next field id hidden input value.
5051 app.updateFormHiddenFieldID( res.data.field_id );
5052
5053 // add the invisible field under 'settings' tab so the user can instantly edit the settings for the field
5054 if ( res.data.edit_field_html !== '' ) {
5055
5056 $('.charitable-layout-options-tab-general .charitable-layout-options-group-inner').append( res.data.edit_field_html ).find('.charitable-panel-field[data-field-id="' + res.data.field_id + '"]').addClass('active');
5057
5058 // Does this new field have any WYSIWYG fields in the settings?
5059 if ( res.data.html_field != '' ) {
5060 // this requires a quill assignment, an HTML field.
5061 $('.charitable-panel-field[data-field-id="' + res.data.field_id + '"]').find( '.campaign-builder-htmleditor' ).each( function() {
5062 app.initHTMLEditorFields( $( this ), false );
5063 });
5064 }
5065
5066 // check and see if the field default text requires an update, say maybe for campaign description.
5067 if ( type === 'campaign-description' && typeof s.campaignDescription !== 'undefined' ) {
5068 $newField.find('.charitable-campaign-builder-no-description-preview').html( '<div>' + s.campaignDescription + '</div>' );
5069
5070 if ( $( '#charitable-panel-field-settings-field_campaign-description_html_' + data.field_id ).find('.ql-editor').length === 0 ) {
5071 app.initHTMLEditorFields( $( '#charitable-panel-field-settings-field_campaign-description_html_' + data.field_id ) );
5072 }
5073
5074 $( '#charitable-panel-field-settings-field_campaign-description_html_' + data.field_id).find('.ql-editor').html( s.campaignDescription );
5075 }
5076
5077 }
5078
5079 // if this is a new organizer field, we need to init the dropdown.
5080 if ( res.data.field.type === 'organizer' ) {
5081 $('.campaign-builder-campaign-creator-id-mini[data-field-id="' + res.data.field_id + '"] select').select2({
5082 templateResult: app.campaignCreatorFormatOptions
5083 });
5084 }
5085
5086 //wpchar.initTooltips();
5087
5088 // Check and see if duplicated this changed any enabling of add field buttons on the left.
5089 app.checkFieldAllow();
5090 app.checkFieldMax();
5091
5092 app.updateEndDateRelatedItems();
5093 app.updateGoalRelatedItems();
5094
5095 // Check and see if this enables/disables a checkmark for recommended buttons on the left.
5096 app.checkRecommendedFields( type );
5097
5098 // determine if we add/remove the no fields preview area
5099 var fieldContainerSectionType = $fieldContainer.data( 'section-type' ),
5100 forceDeleteNoPreview = false;
5101
5102 if (typeof fieldContainerSectionType !== 'undefined' && fieldContainerSectionType === 'fields' ) { // could also be 'tabs' but we want to just check 'fields'.
5103 forceDeleteNoPreview = true; // eslint-disable-line
5104 }
5105
5106 app.checkNoFieldsPreview();
5107
5108 // if this is a photo, auto open the media library.
5109 if ( 'photo' === type ) {
5110 $( '.charitable-panel-field-uploader[data-field-id="' + res.data.field_id + '"] input.charitable-campaign-builder-upload-button' ).click();
5111 } else if ( 'donate-amount' === type ) {
5112 $( '.charitable-campaign-suggested-donations-mini' ).each( function() {
5113 app.initSuggestedDonationsMini( $( this ) );
5114 app.updateSuggestdDonationsMiniRowsFromSettings( $( this ) );
5115 });
5116 }
5117
5118 // finally... just trigger and we're done.
5119
5120 $builder.trigger( 'charitableFieldAdd', [ res.data.field.id, type ] );
5121
5122 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
5123
5124 adding = false;
5125
5126 } ).always( function() {
5127
5128 $builder.find( '.charitable-add-fields .charitable-add-fields-button' ).prop( 'disabled', false );
5129
5130 if ( ! adding ) {
5131 if ( Charitable.Admin.Builder.DragFields && typeof Charitable.Admin.Builder.DragFields.enableDragAndDrop === 'function' ) {
5132 Charitable.Admin.Builder.DragFields.enableDragAndDrop();
5133 }
5134 app.enableFormActions();
5135 }
5136 } );
5137 },
5138
5139 /**
5140 * Add new field to the base level of fields.
5141 *
5142 * @since 1.8.0
5143 *
5144 * @param {object} options Field add additional options.
5145 * @param {jQuery} $newField New field preview object.
5146 * @param {jQuery} $newOptions New field options object.
5147 */
5148 fieldAddToBaseLevel: function( options, $newField, $newOptions ) { // eslint-disable-line
5149
5150 wpchar.debug ('options');
5151 wpchar.debug ( options );
5152
5153 let $baseFieldsContainer = '';
5154
5155 if ( options.area === 'tabs' ) {
5156 $baseFieldsContainer = elements.$sortableTabContent.find( 'li[data-tab-id="' + options.tab_id + '"] .charitable-tab-wrap' );
5157 $baseFieldsContainer.parent().removeClass('empty-tab');
5158 } else {
5159 $baseFieldsContainer = elements.$formPreview.find( '.charitable-field-wrap[data-section-id="' + options.section_id + '"]' );
5160 }
5161
5162 wpchar.debug ('baseFieldsContainer');
5163 wpchar.debug ( $baseFieldsContainer );
5164
5165 // $baseFieldsContainer = elements.$sortableFieldsWrap,
5166 const $baseFields = $baseFieldsContainer.find( '> :not(.charitable-field-drag-pending)' ),
5167 $lastBaseField = $baseFields.last(),
5168 totalBaseFields = $baseFields.length;
5169
5170 let $fieldInPosition = elements.$fieldOptions;
5171
5172 if ( options.position === 'top' ) {
5173 // Add field to top of base level fields.
5174 $baseFieldsContainer.prepend( $newField );
5175 return;
5176 }
5177
5178 if (
5179 options.position === 'bottom' && (
5180 ! $lastBaseField.length ||
5181 ! $lastBaseField.hasClass( 'charitable-field-stick' )
5182 )
5183 ) {
5184
5185 wpchar.debug ('adding field to the bottom!!!!');
5186
5187 // Add field to the bottom of base level fields.
5188 $baseFieldsContainer.append( $newField );
5189 return;
5190 }
5191
5192 if ( options.position === 'bottom' ) {
5193 options.position = totalBaseFields;
5194 }
5195
5196 if (
5197 options.position === totalBaseFields &&
5198 $lastBaseField.length && $lastBaseField.hasClass( 'charitable-field-stick' )
5199 ) {
5200
5201 // Check to see if the last field we have is configured to
5202 // be stuck to the bottom, if so add the field above it.
5203 $lastBaseField.before( $newField );
5204 // $fieldOptions.find( `#charitable-field-option-${lastBaseFieldId}` ).before( $newOptions );
5205
5206 return;
5207 }
5208
5209 $fieldInPosition = $baseFieldsContainer.children( ':not(.charitable-field-drag-pending)' ).eq( options.position );
5210
5211 if ( $fieldInPosition.length ) {
5212 // Add field to a specific location.
5213 $fieldInPosition.before( $newField );
5214 // $fieldOptions.find( `#charitable-field-option-${fieldInPositionId}` ).before( $newOptions );
5215
5216 return;
5217 }
5218
5219 // Something is wrong. Just add the field. This should never occur.
5220 $baseFieldsContainer.append( $newField );
5221
5222 },
5223
5224 /**
5225 * Check if the tabs in the preview area have no fields.
5226 *
5227 * @since 1.8.0
5228 */
5229 checkIfTabsAreEmpty: function () {
5230
5231 elements.$preview.find('.tab-content ul li.tab_content_item').each(function(){
5232 var numfields = $(this).find('.charitable-field').length;
5233 if ( numfields === 0 ) {
5234 $(this).addClass('empty-tab');
5235 }
5236 });
5237
5238 },
5239
5240 /**
5241 * Disable Preview, Embed, Save form actions and Form Builder exit button.
5242 *
5243 * @since 1.8.0
5244 *
5245 * @param {boolean} disableExit Exit button.
5246 * @param {boolean} disableCampaignTitleEdit Campaign title edit.
5247 * @param {boolean} disablePreviewButton Preview button.
5248 *
5249 */
5250 disableFormActions: function( disableExit = false, disableCampaignTitleEdit = true, disablePreviewButton = false ) {
5251
5252 $.each(
5253 [
5254 elements.$embedButton,
5255 elements.$statusButton,
5256 elements.$saveButton
5257 ],
5258 function( _index, button ) {
5259 button.prop( 'disabled', true ).addClass( 'charitable-disabled' );
5260 }
5261 );
5262
5263 if ( disableExit ) {
5264 $.each(
5265 [
5266 elements.$exitButton
5267 ],
5268 function( _index, button ) {
5269 button.prop( 'disabled', true ).addClass( 'charitable-disabled' );
5270 }
5271 );
5272 }
5273
5274 if ( disableCampaignTitleEdit ) {
5275 $('.charitable-edit-campaign-title-area').addClass('charitable-disabled');
5276 $('.charitable-edit-campaign-title-area input').prop( 'disabled', true );
5277 $('.charitable-edit-campaign-title-area a').addClass('charitable-disabled');
5278 }
5279
5280 if ( disablePreviewButton ) {
5281 elements.$previewButton.prop( 'disabled', true ).addClass( 'charitable-disabled' );
5282 }
5283
5284 },
5285
5286 /**
5287 * Enable Preview, Embed, Save form actions and Form Builder exit button.
5288 *
5289 * @since 1.8.0
5290 */
5291 enableFormActions: function() {
5292
5293 $.each(
5294 [
5295 // elements.$previewButton,
5296 // elements.$embedButton,
5297 elements.$statusButton,
5298 elements.$saveButton,
5299 elements.$exitButton
5300 ],
5301 function( _index, button ) {
5302 button.prop( 'disabled', false ).removeClass( 'charitable-disabled' );
5303 }
5304 );
5305
5306 $('.charitable-edit-campaign-title-area').removeClass('charitable-disabled');
5307 $('.charitable-edit-campaign-title-area input').prop( 'disabled', false );
5308 $('.charitable-edit-campaign-title-area a').removeClass('charitable-disabled');
5309
5310 if ( s.formStatus === 'publish' ) {
5311 elements.$embedButton.prop( 'disabled', false ).removeClass( 'charitable-disabled' );
5312 }
5313
5314 },
5315
5316 /**
5317 * Field choice delete error alert.
5318 *
5319 * @since 1.8.0
5320 */
5321 fieldChoiceDeleteAlert: function() {
5322
5323 $.alert( {
5324 title : false,
5325 content : charitable_builder.error_choice,
5326 icon : 'fa fa-info-circle',
5327 type : 'blue',
5328 buttons : {
5329 confirm: {
5330 text: charitable_builder.ok,
5331 btnClass: 'btn-confirm',
5332 keys: [ 'enter' ]
5333 }
5334 }
5335 } );
5336
5337 },
5338
5339 /**
5340 * Make field choices sortable.
5341 * Currently used for select, radio, and checkboxes field types.
5342 *
5343 * @since 1.8.0
5344 *
5345 * @param {string} type Field type.
5346 * @param {string} selector Element.
5347 */
5348 fieldChoiceSortable: function( type, selector ) {
5349
5350 selector = typeof selector !== 'undefined' ? selector : '.charitable-field-option-' + type + ' .charitable-field-option-row-choices ul';
5351
5352 $( selector ).sortable( {
5353 items : 'li',
5354 axis : 'y',
5355 delay : 100,
5356 opacity: 0.6,
5357 handle : '.move',
5358 stop:function( e, ui ) {
5359 var id = ui.item.parent().data( 'field-id' );
5360 app.fieldChoiceUpdate( type, id );
5361 $builder.trigger( 'charitableFieldChoiceMove', ui );
5362 },
5363 update: function( e, ui ) { // eslint-disable-line
5364 }
5365 } );
5366 },
5367
5368 /**
5369 * Delete a tab.
5370 *
5371 * @param {int} id Field ID.
5372 *
5373 * @since 1.8.0
5374 */
5375 tabDelete: function( groupID ) {
5376
5377 app.confirmTabDeletion( groupID );
5378
5379 },
5380
5381 /**
5382 * Show the confirmation popup before the tab deletion.
5383 *
5384 * @param {int} groupID Group ID.
5385 *
5386 * @since 1.8.0
5387 */
5388 confirmTabDeletion: function( groupID ) {
5389
5390 var tabData = {
5391 'id' : groupID,
5392 'message' : charitable_builder.delete_tab_confirm
5393 };
5394
5395 var event = CharitableUtils.triggerEvent( $builder, 'charitableBeforeTabDeleteAlert', [ tabData ] );
5396
5397 // Allow callbacks on `charitableBeforeFieldDeleteAlert` to prevent field deletion by triggering `event.preventDefault()`.
5398 if ( event.isDefaultPrevented() ) {
5399 return;
5400 }
5401
5402 $.confirm( {
5403 title : false,
5404 content : charitable_builder.delete_tab_confirm,
5405 icon : 'fa fa-exclamation-circle',
5406 type : 'orange',
5407 buttons : {
5408 confirm: {
5409 text : charitable_builder.ok,
5410 btnClass : 'btn-confirm',
5411 keys : [ 'enter' ],
5412 action : function() {
5413 app.tabDeleteById( groupID );
5414 }
5415 },
5416 cancel: {
5417 text: charitable_builder.cancel,
5418 keys: [ 'esc' ]
5419 }
5420 }
5421 } );
5422
5423 },
5424
5425 /**
5426 * Actual deletion of a tab.
5427 *
5428 * @param {int} groupID Group ID.
5429 *
5430 * @since 1.8.0
5431 */
5432 tabDeleteById: function ( groupID ) {
5433
5434 const tab_settings = elements.$fieldOptions.find('[data-group_id="' + groupID + '"]'),
5435 tab_preview_nav = $('nav ul li#tab_' + groupID + '_title'),
5436 tab_preview_content = elements.$preview.find('.tab-content ul li.tab_content_item[data-tab-id="' + groupID + '"]'),
5437 tab_count = elements.$preview.find('.tab-content .tab_content_item').length - 1,
5438 previous_group_ID = Math.abs( groupID - 1 ),
5439 tab_content = elements.$preview.find('.tab-content');
5440
5441 tab_settings.remove();
5442 tab_preview_nav.remove();
5443 tab_preview_content.remove();
5444
5445 if ( elements.$preview.find('nav.charitable-campaign-preview-nav ul li[data-tab-id="' + previous_group_ID + '"] a').length > 0 ) {
5446 elements.$preview.find('nav.charitable-campaign-preview-nav ul li[data-tab-id="' + previous_group_ID + '"] a' ).click();
5447 } else if ( tab_count === 1 ) { // there's at least one tab left, we need to make some tab active
5448 elements.$preview.find('nav.charitable-campaign-preview-nav ul li a' ).first().click();
5449 }
5450
5451 // adjust the CSS of the tab-content div to reflect no more tabs, if that's the case.
5452 if ( tab_count === 0 ) {
5453 tab_content.addClass('empty-tabs');
5454 // add the HTML message.
5455 tab_content.append('<p class="no-tab-notice">' + charitable_builder.no_tabs + '</p>');
5456 } else {
5457 tab_content.removeClass('empty-tabs');
5458 tab_content.find('.no-tab-notice').remove();
5459 }
5460
5461 app.checkHideTabNavigation();
5462
5463 },
5464
5465 //--------------------------------------------------------------------//
5466 // Preview
5467 //--------------------------------------------------------------------//
5468
5469 /**
5470 * Element bindings for Embed and Save/Exit items.
5471 *
5472 * @since 1.8.0
5473 */
5474 bindUIActionsPreview: function() {
5475
5476 // Embed form.
5477 $('body').on( 'click', 'a#charitable-preview-btn', function( e ) {
5478
5479 e.preventDefault();
5480 e.stopPropagation();
5481
5482 if ( parseInt( elements.$campaignID.val() ) === 0 ) {
5483
5484 $.alert( {
5485 title : charitable_builder.no_preview_must_save,
5486 content : charitable_builder.no_preview_must_save_msg,
5487 icon : 'fa fa-info-circle',
5488 type : 'blue',
5489 buttons : {
5490 confirm: {
5491 text: charitable_builder.close,
5492 btnClass: 'btn-confirm',
5493 keys: [ 'enter' ]
5494 }
5495 }
5496 } );
5497
5498 return;
5499
5500 } else {
5501
5502 var openNewTabURL = '';
5503
5504 if ( typeof this.href !== 'undefined' ) {
5505 openNewTabURL = this.href.replace( 'charitable_campaign_preview=0', 'charitable_campaign_preview=' + s.campaignID );
5506 }
5507
5508 // We are assuming that if they are able to click preview then the campaign is either in draft/not posiblished OR published but they made a change.
5509
5510 // Determine if this is an already published campaign... if so, then save the campaign data TEMPORARILY so we can preview it...
5511 if ( typeof s.formStatus !== 'undefined' && s.formStatus === 'publish' ) {
5512 // Passing in "true" as the second parameter to formSave() will tell it to save the campaign data temporarily.
5513 app.formSave( false, true, openNewTabURL ); // false - no redirect, true - yes this is a preview.
5514
5515 } else if ( typeof s.formStatus !== 'undefined' && s.formStatus === 'draft' ) {
5516 app.formSave( false, true, openNewTabURL ); // still previewing, so pass true.
5517 }
5518
5519
5520
5521 }
5522
5523 } );
5524
5525 },
5526
5527 //--------------------------------------------------------------------//
5528 // Save and Exit
5529 //--------------------------------------------------------------------//
5530
5531 /**
5532 * Element bindings for Embed and Save/Exit items.
5533 *
5534 * @since 1.8.0
5535 */
5536 bindUIActionsSaveExit: function() {
5537
5538 // Embed form.
5539 $builder.on( 'click', '#charitable-embed', function( e ) {
5540
5541 e.preventDefault();
5542 e.stopPropagation();
5543
5544 if ( $( this ).hasClass( 'charitable-disabled' ) ) {
5545 return;
5546 }
5547
5548 CharitableCampaignEmbedWizard.openPopup( s.formID );
5549
5550 } );
5551
5552 // Change Status.
5553 $builder.on( 'click', '#charitable-status-button', function( e ) {
5554
5555 e.preventDefault();
5556 e.stopPropagation();
5557
5558 if ( $( this ).hasClass( 'charitable-disabled' ) ) {
5559 return;
5560 }
5561
5562 if ( $( this ).hasClass( 'active' ) ) {
5563 $( this ).parent().find('ul#charitable-status-dropdown').addClass('charitable-hidden');
5564 $( this ).removeClass( 'active' );
5565 } else {
5566 $( this ).parent().find('ul#charitable-status-dropdown').removeClass('charitable-hidden');
5567 $( this ).addClass( 'active' );
5568 }
5569
5570 } );
5571
5572 $builder.on( 'click', 'ul#charitable-status-dropdown a', function( e ) {
5573
5574 e.preventDefault();
5575 e.stopPropagation();
5576
5577 if ( $( '#charitable-status-button' ).hasClass( 'charitable-disabled' ) ) {
5578 return;
5579 }
5580
5581 var newStatus = $( this ).data('status'),
5582 newStatusLabel = $( this ).data('status-label'),
5583 $statusDropdown = $( "ul#charitable-status-dropdown" );
5584
5585 $statusDropdown.addClass('charitable-hidden');
5586 $( "#charitable-status-button" ).removeClass('active');
5587
5588 // change status
5589 // s.formStatus = newStatus;
5590 $( '#charitable-status-button span.text').html( newStatusLabel );
5591 $( '#charitable-status-button').attr('data-status', newStatus );
5592
5593 $statusDropdown.find('a').removeClass('charitable-hidden');
5594 $statusDropdown.find('a.switch-' + newStatus).addClass('charitable-hidden');
5595 $statusDropdown.find('a.' + newStatus).addClass('charitable-hidden');
5596
5597 if ( newStatus === 'draft' ) {
5598 // $statusDropdown.find('a[class*="switch-"]').addClass('charitable-hidden');
5599 } else if ( newStatus === 'publish' ) {
5600 // $statusDropdown.find('a.draft').addClass('charitable-hidden');
5601 // $statusDropdown.find('a.pending').addClass('charitable-hidden');
5602 // $statusDropdown.find('a.review').addClass('charitable-hidden');
5603 }
5604
5605 s.formStatus = newStatus;
5606 s.formStatusLabel = newStatusLabel;
5607
5608 } );
5609
5610 $builder.on("click", function(e) {
5611 if ( $(e.target).is("#charitable-status-dropdown") === false) {
5612 $( "ul#charitable-status-dropdown" ).addClass('charitable-hidden');
5613 $( "#charitable-status-button" ).removeClass('active');
5614 }
5615 });
5616
5617 // Save form.
5618 $builder.on( 'click', '#charitable-save', function( e ) {
5619 e.preventDefault();
5620 app.formSave( false );
5621 } );
5622
5623 // Exit builder.
5624 $builder.on( 'click', '#charitable-exit', function( e ) {
5625 e.preventDefault();
5626 app.formExit();
5627 } );
5628
5629 // After form save.
5630 $builder.on( 'charitableSaved', function( e, data ) { // eslint-disable-line
5631
5632 $('#charitable_settings_title').attr('disabled', true);
5633
5634 /**
5635 * Remove `newform` parameter, if it's in URL, otherwise we can to get a "race condition".
5636 * E.g. form settings will be updated before some provider connection is loaded.
5637 */
5638 wpchar.removeQueryParam( 'newform' );
5639 } );
5640
5641 },
5642
5643 /**
5644 * Update the campaign <form> ID that gets stored in a few key places (like hidden fields) - usually happens when a new form is saved/gets it's ID.
5645 *
5646 * @since 1.8.0
5647 */
5648 updateFormID: function () {
5649
5650 var data = {
5651 action: 'charitable_get_campaign_form_id',
5652 id : s.formID,
5653 nonce : charitable_builder.nonce
5654 };
5655
5656 return $.post( charitable_builder.ajax_url, data, function( response ) {
5657
5658 if ( response.success ) {
5659
5660 var campaignID = response.data.campaign_id,
5661 redirect = false;
5662
5663 // update the campaign ID
5664 $('form#charitable-builder-form').attr('data-id', campaignID );
5665 $('form#charitable-builder-form input[name="id"]').val( campaignID );
5666
5667 // update the field ID
5668 if ( response.data.field_id ) {
5669 elements.$nextFieldId.val( parseInt( response.data.field_id ) );
5670 }
5671
5672 wpchar.savedState = wpchar.getFormState( '#charitable-builder-form' );
5673 wpchar.initialSave = false;
5674
5675 $builder.trigger( 'charitableSaved', response.data );
5676
5677 // send the updated form of the campaign builder form to the debug window, along with the returned campaign id.
5678 app.updateDebugWindow( response.data, response.data.campaign_id );
5679
5680 if ( true === redirect ) {
5681 window.location.href = charitable_builder.exit_url;
5682 }
5683
5684 } else {
5685
5686 app.formSaveError( response.data );
5687
5688 }
5689
5690 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
5691
5692 app.formSaveError();
5693
5694 } ).always( function() {
5695
5696 } );
5697
5698 },
5699
5700 /**
5701 * Make sure critical hidden fields with critical data have the latest.
5702 *
5703 * @since 1.8.0
5704 */
5705 updateFormHiddenFields: function () {
5706
5707 // fields area.
5708
5709 $('#charitable-panel-design .charitable-panel-content-wrap input[type="hidden"]').remove();
5710
5711 $('.charitable-field-wrap .charitable-field, .charitable-tab-wrap .charitable-field').each(function(){
5712
5713 var row_type = $( this ).closest('.row').data('row-type'),
5714 row_id = $( this ).closest('.row').data('row-id'),
5715 row_css = $( this ).closest('.row').data('row-css').length > 0 ? $( this ).closest('.row').data('row-css') : 'no-css',
5716 column_id = $( this ).closest('.column.charitable-field-column').data('column-id'),
5717 section_type = $( this ).closest('.section.charitable-field-section').data('section-type'),
5718 section_id = $( this ).closest('.section.charitable-field-section').data('section-id'),
5719 field_id = $( this ).data('field-id'),
5720 tab_id = ( 'tabs' === section_type ) ? $( this ).closest('li.tab_content_item').data('tab-id') : false;
5721
5722 if ( typeof row_id !== 'undefined' ) {
5723
5724 if ( 'tabs' === section_type ) {
5725
5726 elements.$formPreview.append('<input type="hidden" name="layout[row][row-type-' + row_type + '][' + row_id + '][' + row_css + '][column][' + column_id + '][section][section-type-' + section_type +'][' + section_id + '][tabs][' + tab_id + '][fields][' + field_id + ']" value="' + $(this).data('field-type') + '" />');
5727
5728 } else {
5729
5730 elements.$formPreview.append('<input type="hidden" name="layout[row][row-type-' + row_type + '][' + row_id + '][' + row_css + '][column][' + column_id + '][section][section-type-' + section_type +'][' + section_id + '][fields][' + field_id + ']" value="' + $(this).data('field-type') + '" />');
5731
5732 }
5733
5734 }
5735
5736 });
5737
5738 },
5739
5740 /**
5741 * This updates the field id hidden field that tells the form what the current/next field ID is
5742 * which is useful if you are loading a template from the start and immedately want to add a field with a new incredmental ID (like ID: 3).
5743 *
5744 * @param {int} newValue New value.
5745 *
5746 * @since 1.8.0
5747 */
5748 updateFormHiddenFieldID: function ( newValue = 0 ) {
5749
5750 if ( 0 === newValue ) {
5751 // Let's do a count ourselves.
5752 var field_id = 0;
5753 elements.$formPreview.find('.charitable-field').each(function() {
5754 var value = parseFloat($(this).attr('data-field-id'));
5755 field_id = (value > field_id) ? value : field_id;
5756 });
5757 newValue = field_id;
5758 }
5759
5760 elements.$nextFieldId.val( parseInt( newValue ) );
5761
5762 },
5763
5764 /**
5765 * This updates the link on the preview button in the top header, especially if this is a campaign saved for the first time.
5766 *
5767 * @param {int} newValue New value.
5768 *
5769 * @since 1.8.0
5770 */
5771 updatePreviewLink: function( previewLink = '') {
5772
5773 elements.$previewButton.attr( 'href', previewLink );
5774
5775 },
5776
5777 /**
5778 * This updates the donation amount settings in the settings area when a user updates a value in the donation amount field settings in the preview/design area to keep insync.
5779 *
5780 * @param {object} $element The element on focus.
5781 * @param {int} theIndex Position of text box relative to the other text boxes.
5782 *
5783 * @since 1.8.0
5784 */
5785 updateSuggestDonationsSettings: function( $element, theIndex ) {
5786
5787 if ( ! $element.is("input") || app.getInputType( $element ) !== 'text' ) {
5788 return;
5789 }
5790
5791 // Update settings.
5792 $('table#campaign_donation_amounts tbody').find('input[type="text"].campaign_suggested_donations').eq( theIndex ).val( $element.val() );
5793
5794 // Update any donation amount setting fields on the left.
5795 $('.charitable-campaign-suggested-donations-mini tbody').find('input[type="text"].campaign_suggested_donations').eq( theIndex ).val( $element.val() );
5796
5797 // Update preview area on the right.
5798 elements.$preview.find('.charitable-field-donate-amount li.charitable-preview-donation-amount').eq( ( theIndex / 2 ) - 1 ).find('span').first().html( $element.val() );
5799 },
5800
5801 /**
5802 * This updates the allow custom donation settings in the donation amount field in the preview area depending on the checkbox in the settings area.
5803 *
5804 * @param {object} $element The element on focus.
5805 * @param {int} theIndex Position of text box relative to the other text boxes.
5806 *
5807 * @since 1.8.0
5808 */
5809 updateAllowCustomDonationSettings: function ( isChecked ) {
5810
5811 // // Update settings
5812 // $('#charitable-panel-field-settings-campaign_allow_custom_donations').prop( "checked", isChecked );
5813
5814 // // Update ANY toggle in field settings.
5815 // $('.charitable-panel-field.charitable-campaign-builder-allow-custom-donations input[type="checkbox"]').prop( "checked", isChecked );
5816
5817 // Hide or show field in preview depending on setting
5818 const $custom_preview_fields = elements.$preview.find('.charitable-preview-donation-options .custom-donation-amount');
5819
5820 if ( isChecked ) {
5821 $custom_preview_fields.removeClass('charitable-hidden');
5822 } else {
5823 $custom_preview_fields.addClass('charitable-hidden');
5824 }
5825
5826 },
5827
5828 /**
5829 * This updates the suggested donation amount settings in the settings area when a user updates a value in the donation amount field settings in the preview/design area to keep insync.
5830 *
5831 * @since 1.8.0
5832 *
5833 * @param {int} selectedValue The value of the selected radio button.
5834 */
5835 updateSuggestedDonationAmountDefault: function( selectedValue = 0 ) {
5836
5837 if ( selectedValue > 0 ) {
5838
5839 $('input[type="radio"].campaign_suggested_donations').prop('checked', false );
5840 $('input[type="radio"][value=' + selectedValue + '].campaign_suggested_donations').prop('checked', true );
5841
5842 elements.$preview.find('.charitable-field.charitable-field-donate-amount li').removeClass( 'selected' );
5843 elements.$preview.find('.charitable-preview-donation-amounts li:nth-child(' + ( selectedValue ) + ')').addClass('selected');
5844
5845 if ( elements.$preview.find('.charitable-field.charitable-field-donate-amount input[type="radio"]').length > 0 ) {
5846 elements.$preview.find('.charitable-field.charitable-field-donate-amount input[type="radio"]:eq(' + ( selectedValue - 1 ) + ')').prop( 'checked', true );
5847 }
5848
5849
5850 } else {
5851
5852 $('input:radio].campaign_suggested_donations').prop('checked', false );
5853 }
5854
5855 },
5856
5857 /**
5858 * Gets input type of a field, see updateSuggestDonationsSettings().
5859 *
5860 * @since 1.8.0
5861 *
5862 * @param {object} $element The element on focus.
5863 */
5864 getInputType: function( $element ) {
5865
5866 var thistest = $element;
5867
5868 return thistest[0].tagName.toString().toLowerCase() === "input" ?
5869 $(thistest[0]).prop("type").toLowerCase() :
5870 thistest[0].tagName.toLowerCase();
5871
5872 },
5873
5874 /**
5875 * This does an ajax call to update the "campaign creator" info, usually when the user selects a different valud in the user dropdown.
5876 *
5877 * @since 1.8.0
5878 */
5879 updateCampaignCreatorInfo: function () {
5880
5881 // Update the campaign creator info if the select dropdown is changed upon usually a form save.
5882 var data = {
5883 action : 'charitable_update_campaign_creator',
5884 creator_id : parseInt( $( 'select#charitable-panel-field-settings-campaign_campaign_creator_id' ).val() ),
5885 campaign_id : s.formID,
5886 nonce : charitable_builder.nonce
5887 };
5888
5889 return $.post( charitable_builder.ajax_url, data, function( response ) {
5890
5891 if ( response.success ) {
5892
5893 $('#campaign-creator .charitable-campaign-creator-avatar img').attr('src', response.data.avatar_url );
5894 $('#campaign-creator h3.creator-name').html( response.data.creator_name );
5895 $('#campaign-creator p.joined-on span').html( response.data.joined_on );
5896 $('#campaign-creator a.public-profile-link').attr('href', response.data.public_profile_link );
5897 $('#campaign-creator a.edit-profile-link').attr('href', response.data.edit_profile_link );
5898
5899
5900 } else {
5901
5902 app.formSaveError( response.data );
5903
5904 }
5905
5906 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
5907
5908 app.formSaveError();
5909
5910 } ).always( function() {
5911
5912
5913 } );
5914
5915 },
5916
5917 /**
5918 * General check for form save errors.
5919 *
5920 * @since 1.8.1.12
5921 *
5922 */
5923 formSaveCheck: function() {
5924
5925 if ( $('#charitable_settings_title').val().length === 0 ) {
5926 $.alert( {
5927 title : charitable_builder.error_title,
5928 content : charitable_builder.error_no_title,
5929 icon : 'fa fa-info-circle',
5930 type : 'red',
5931 buttons : {
5932 confirm: {
5933 text: charitable_builder.ok,
5934 btnClass: 'btn-confirm',
5935 keys: [ 'enter' ]
5936 }
5937 }
5938 } );
5939 return false;
5940 }
5941
5942 },
5943
5944 /**
5945 * Save form.
5946 *
5947 * @since 1.8.0
5948 *
5949 * @param {boolean} redirect Whether to redirect after save.
5950 */
5951 formSave: function( redirect, preview = false, openNewTabURL = '', refreshAfterSave = false ) {
5952
5953 var $saveBtn = elements.$saveButton,
5954 $icon = $saveBtn.find( 'img.topbar_icon' ),
5955 $spinner = $saveBtn.find( 'i.charitable-loading-spinner' ),
5956 $label = $saveBtn.find( 'span' ),
5957 currentPostStatus = s.formSavedStatus;
5958
5959 // Saving a revision directly is not allowed. We need to notify the user that it will overwrite the current version.
5960 if ( $builder.hasClass( 'charitable-is-revision' ) && ! $builder.hasClass( 'charitable-revision-is-saving' ) ) {
5961 app.confirmSaveRevision();
5962 return;
5963 }
5964
5965 // Check and see if there is a name for this campaign.
5966 if ( app.formSaveCheck() === false ) {
5967 return;
5968 }
5969
5970 if ( typeof tinyMCE !== 'undefined' ) {
5971 tinyMCE.triggerSave();
5972 }
5973
5974 var event = CharitableUtils.triggerEvent( $builder, 'charitableBeforeSave' );
5975
5976 // Allow callbacks on `charitableBeforeSave` to cancel form submission by triggering `event.preventDefault()`.
5977 if ( event.isDefaultPrevented() ) {
5978 return;
5979 }
5980
5981 // Un-disable certain fields so they can be included in the data submit.
5982 $( '#charitable_settings_title' ).removeAttr('disabled');
5983
5984 // disable the save button.
5985 $saveBtn.prop( 'disabled', true );
5986
5987 // don't show the spinner if this is a preview save.
5988 if ( preview === false ) {
5989 $label.text( charitable_builder.saving );
5990 $icon.addClass( 'charitable-hidden' );
5991 $spinner.removeClass( 'charitable-hidden' );
5992 }
5993
5994 app.updateFormHiddenFields();
5995
5996 var data = {
5997 action : 'charitable_save_campaign',
5998 data : JSON.stringify( $( '#charitable-builder-form' ).serializeArray() ),
5999 id : s.formID,
6000 status : s.formStatus,
6001 statusLabel : s.formStatusLabel,
6002 preview : preview,
6003 nonce : charitable_builder.nonce
6004 };
6005
6006 return $.post( charitable_builder.ajax_url, data, function( response ) {
6007
6008 if ( response.success ) {
6009
6010 var campaignID = parseInt( response.data.campaign_id );
6011
6012 if ( refreshAfterSave ) {
6013 // is the current url the same as the new url? if so, reload the page.
6014 if ( window.location.href === app.addCampaignIDToURL( campaignID ) ) {
6015 window.location.href = app.addCampaignIDToURL( campaignID );
6016 return;
6017 } else {
6018 // otherwise, redirect to the new url.
6019 window.location.href = app.addCampaignIDToURL( campaignID );
6020 return;
6021 }
6022 }
6023
6024 // update the campaign ID.
6025 $('form#charitable-builder-form').attr('data-id', campaignID );
6026 $('form#charitable-builder-form input[name="id"]').val( campaignID );
6027
6028 s.formID = campaignID; // need to update this var so adding fields for new forms can work.
6029
6030 // update the field ID
6031 if ( response.data.field_id ) {
6032 elements.$nextFieldId.val( parseInt( response.data.field_id ) );
6033 }
6034
6035 wpchar.savedState = wpchar.getFormState( '#charitable-builder-form' );
6036 wpchar.initialSave = false;
6037
6038 $builder.trigger( 'charitableSaved', response.data );
6039
6040 // send the updated form of the campaign builder form to the debug window, along with the returned campaign id.
6041 // app.updateDebugWindow( JSON.stringify( $( '#charitable-builder-form' ).serializeArray() ), response.data.campaign_id );
6042 app.updateDebugWindow( response.data, response.data.campaign_id );
6043
6044 // If we got back a preview url, update the preview button.
6045 if ( typeof response.data.preview_url !== 'undefined' && response.data.preview_url.length > 0 ) {
6046 app.updatePreviewLink( response.data.preview_url );
6047 }
6048
6049 // Don't set the campaign to "saved" if this is just a save for a preview.
6050 if ( preview === false ) {
6051 app.setCampaignSaved();
6052 }
6053
6054 if ( true === redirect ) {
6055 window.location.href = charitable_builder.exit_url;
6056 }
6057
6058 if ( history.pushState ) {
6059 var newUrl = app.addCampaignIDToURL( campaignID );
6060 window.history.pushState( {path:newUrl} ,'', newUrl );
6061 } else {
6062 window.location.href = app.addCampaignIDToURL( campaignID );
6063 }
6064
6065 s.campaignID = campaignID;
6066
6067 // If this campaign was draft and now published, open the congrats popup.
6068 if ( ( 'draft' === currentPostStatus || '' === currentPostStatus ) && 'publish' === response.data.post_status && response.data.permalink ) {
6069 CharitableCampaignCongratsWizard.openPopup( s.formID, response.data.permalink );
6070 }
6071
6072 // If this campaign is published, make sure the 'view' button is clickable and has the correct URL, otherwise disable it.
6073 if ( 'publish' === response.data.post_status ) {
6074 elements.$viewCampaignButton.removeClass('charitable-disabled');
6075 elements.$viewCampaignButton.attr('href', response.data.permalink );
6076 $('a.charitable-admin-campaign-link').attr('href', response.data.permalink );
6077 $('a.charitable-admin-campaign-link.show-url').html( response.data.permalink );
6078 } else {
6079 elements.$viewCampaignButton.addClass('charitable-disabled');
6080 }
6081
6082 // Depending if the campaign is published, make the embed button disabled or not.
6083 if ( 'publish' === response.data.post_status ) {
6084 elements.$embedButton.prop( 'disabled', false ).removeClass( 'charitable-disabled' );
6085 } else {
6086 elements.$embedButton.prop( 'disabled', true ).addClass( 'charitable-disabled' );
6087 }
6088
6089 s.formSavedStatus = response.data.post_status;
6090 s.formSavedStatusLabel = response.data.post_status_label;
6091
6092 if ( openNewTabURL !== '' ) {
6093 window.open( openNewTabURL );
6094 }
6095
6096 // Make sure the shortcode in the embed wizard is updated.
6097 var campaignEmedCode = '[campaign id=&quot;' + campaignID + '&quot;]';
6098 $( '#charitable-admin-campaign-embed-wizard-shortcode-wrap #charitable-admin-campaign-embed-wizard-shortcode' ).remove();
6099 $( '#charitable-admin-campaign-embed-wizard-shortcode-wrap').prepend("<input type=\"text\" id=\"charitable-admin-campaign-embed-wizard-shortcode\" class=\"charitable-admin-popup-shortcode\" value=\"" + campaignEmedCode + "\" />");
6100 $( '#charitable-admin-campaign-embed-wizard-shortcode' ).prop( 'disabled', true );
6101
6102 // If this is a first time save, make sure to update the label re: campaign title.
6103 app.setCampaignTitleSet();
6104
6105 return app.addCampaignIDToURL( campaignID );
6106
6107 } else {
6108
6109 app.formSaveError( response.data );
6110
6111 }
6112
6113 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
6114
6115 app.formSaveError();
6116
6117 } ).always( function() {
6118
6119 $label.text( charitable_builder.saved );
6120 setTimeout( function() {
6121 $label.text( charitable_builder.save );
6122 }, 2500 );
6123 $saveBtn.prop( 'disabled', false );
6124 $spinner.addClass( 'charitable-hidden' );
6125 $icon.removeClass( 'charitable-hidden' );
6126
6127 } );
6128 },
6129
6130 /**
6131 * Form save error.
6132 *
6133 * @since 1.8.0
6134 *
6135 * @param {string} error Error message.
6136 */
6137 formSaveError: function( error ) {
6138
6139 // Default error message.
6140 if ( wpchar.empty( error ) ) {
6141 error = charitable_builder.error_save_form;
6142 }
6143
6144 // Display error in modal window.
6145 $.confirm( {
6146 title : charitable_builder.heads_up,
6147 content : '<p>' + error + '</p><p>' + charitable_builder.error_contact_support + '</p>',
6148 icon : 'fa fa-exclamation-circle',
6149 type : 'orange',
6150 buttons : {
6151 confirm: {
6152 text: charitable_builder.ok,
6153 btnClass: 'btn-confirm',
6154 keys: [ 'enter' ]
6155 }
6156 }
6157 } );
6158
6159 },
6160
6161 /**
6162 * Form save error.
6163 *
6164 * @since 1.8.0
6165 *
6166 * @param {string} error Error message.
6167 */
6168 formGenericError: function( error ) {
6169
6170 // Default error message.
6171 if ( wpchar.empty( error ) ) {
6172 error = charitable_builder.something_went_wrong;
6173 }
6174
6175 // Display error in modal window.
6176 $.confirm( {
6177 title : charitable_builder.heads_up,
6178 content : '<p>' + error + '</p><p>' + charitable_builder.error_contact_support + '</p>',
6179 icon : 'fa fa-exclamation-circle',
6180 type : 'orange',
6181 buttons : {
6182 confirm: {
6183 text: charitable_builder.ok,
6184 btnClass: 'btn-confirm',
6185 keys: [ 'enter' ]
6186 }
6187 }
6188 } );
6189 },
6190
6191 /**
6192 * Form save error.
6193 *
6194 * @since 1.8.0
6195 *
6196 * @param {string} error Error message.
6197 */
6198 formGenericNotice: function( error ) {
6199
6200 // Default error message.
6201 if ( wpchar.empty( error ) ) {
6202 error = charitable_builder.something_went_wrong;
6203 }
6204
6205 // Display error in modal window.
6206 $.confirm( {
6207 title : charitable_builder.heads_up,
6208 content : '<p>' + error + '</p>',
6209 icon : 'fa fa-exclamation-circle',
6210 type : 'orange',
6211 buttons : {
6212 confirm: {
6213 text: charitable_builder.ok,
6214 btnClass: 'btn-confirm',
6215 keys: [ 'enter' ]
6216 }
6217 }
6218 } );
6219
6220 },
6221
6222 /**
6223 * Appends campaign ID to the URL in the campign builder.
6224 *
6225 * @since 1.8.0
6226 *
6227 * @param {int} campaignID ID of campaign.
6228 */
6229 addCampaignIDToURL: function( campaignID ) {
6230
6231 const currentURL = window.location.href,
6232 urlHasQueryString = currentURL.includes("?");
6233
6234 let updatedURL = false;
6235
6236 if ( urlHasQueryString ) {
6237 // URL already has a query string.
6238
6239 const queryString = window.location.search,
6240 campaignIDExists = queryString.includes("campaign_id");
6241
6242 if ( campaignIDExists ) {
6243 // URL already has a campaign ID parameter.
6244 // Remove any # and text after the # from the url.
6245 updatedURL = currentURL.split('#')[0];
6246 return updatedURL;
6247 } else {
6248 // Remove any # and text after the # from the url.
6249 updatedURL = currentURL.split('#')[0];
6250 // URL does not have a campaign ID parameter, so add it.
6251 return updatedURL + "&campaign_id=" + campaignID;
6252 }
6253
6254 } else {
6255
6256 // URL does not have a query string, so add one with the campaign ID parameter.
6257 return currentURL + "?campaign_id=" + campaignID;
6258
6259 }
6260
6261 },
6262
6263 //--------------------------------------------------------------------//
6264 // Fields (Design) Panel
6265 //--------------------------------------------------------------------//
6266
6267 /**
6268 * Toggle fields tabs (Add Fields, Field Options.
6269 *
6270 * @since 1.8.0
6271 *
6272 * @param {string|integer} id Field Id or `add-fields` or `field-options`.
6273 */
6274 fieldTabToggle: function( id ) {
6275
6276 const event = CharitableUtils.triggerEvent( $builder, 'charitableFieldTabToggle', [ id ] );
6277
6278 // Allow callbacks on `charitableFieldTabToggle` to cancel tab toggle by triggering `event.preventDefault()`.
6279 if ( event.isDefaultPrevented() ) {
6280 return false;
6281 }
6282
6283 $( '.charitable-tab a' ).removeClass( 'active' );
6284 $( '.charitable-field, .charitable-preview-top-bar' ).removeClass( 'active' );
6285
6286 if ( id === 'add-layout' ) {
6287
6288 $( '#add-layout a' ).addClass( 'active' );
6289 $( '.charitable-field-options' ).hide();
6290 $( '.charitable-add-fields' ).show();
6291
6292 } else if ( id === 'layout-options' ) {
6293
6294 $( '#layout-options a' ).addClass( 'active' );
6295 $( '.charitable-add-fields' ).hide();
6296 $( '.charitable-field-options' ).show();
6297
6298 } else {
6299
6300 $( '#charitable-field-' + id ).addClass( 'active' );
6301
6302 $( '.charitable-field-option' ).hide();
6303 $( '#charitable-field-option-' + id ).show();
6304
6305 $( '.charitable-add-fields' ).hide();
6306 $( '.charitable-field-options' ).show();
6307
6308 $builder.trigger( 'charitableFieldOptionTabToggle', [ id ] );
6309 }
6310
6311 wpCookies.set( 'charitable_panel_content_section', id, 2592000 );
6312 wpCookies.set( 'charitable_panel_active_field_id', '', 2592000 ); // 1 month
6313 wpCookies.set( 'charitable_panel_design_layout_options_group', '', 2592000 ); // 1 month
6314 wpCookies.set( 'charitable_panel_tab_section_tab_id', '', 2592000 ); // 1 month
6315
6316 },
6317
6318 /**
6319 * Exit form builder.
6320 *
6321 * @since 1.8.0
6322 */
6323 formExit: function() {
6324
6325 if ( app.formIsSaved() ) {
6326
6327 window.location.href = charitable_builder.exit_url;
6328
6329 } else {
6330
6331 $.confirm( {
6332 title : false,
6333 content : '<p>' + charitable_builder.exit_confirm + '</p>',
6334 icon : 'fa fa-exclamation-circle',
6335 type : 'orange',
6336 closeIcon : true,
6337 buttons : {
6338 confirm: {
6339 text: charitable_builder.save_exit,
6340 btnClass: 'btn-confirm',
6341 keys: [ 'enter' ],
6342 action: function() {
6343 app.formSave( true );
6344 }
6345 },
6346 cancel: {
6347 text: charitable_builder.exit,
6348 keys: [ 'esc' ],
6349 action: function() {
6350 closeConfirmation = false;
6351 window.location.href = charitable_builder.exit_url;
6352 }
6353 }
6354 }
6355 } );
6356 }
6357
6358 },
6359
6360 /**
6361 * Check the field max values and set buttons in "add layout" accordingly.
6362 * This function can check things generally... or specifically if a type is passed.
6363 *
6364 * @since 1.8.0
6365 *
6366 * @param {string} type Field type.
6367 * @param {int} max Max number of fields.
6368 */
6369 checkFieldMax: function( type = '', max = 99 ) {
6370
6371 if ( type.length === 0 ) {
6372
6373 // count how many types of fields there are and if they are under the max, removeClass 'charitable-disabled' from the button.
6374 var fieldCount = elements.$preview.find('.charitable-field-' + type).length;
6375
6376 if ( fieldCount < max ) {
6377 elements.$panelDesign.find('button#charitable-add-fields-' + type).removeClass('charitable-disabled');
6378 }
6379
6380 } else {
6381
6382 elements.$preview.find( ".charitable-field[data-field-max!='']" ).each( function() {
6383 var max = parseInt( $( this ).data('field-max') ),
6384 fieldType = $( this ).data('field-type'),
6385 fieldCount = ( typeof fieldType === 'undefined' || fieldType.length === 0 ) ? 0 : elements.$preview.find('.charitable-field-' + fieldType).length;
6386
6387 // count how many of the fields there are in the preview area and see if we need to deactivate the button.
6388 if ( ! isNaN( max ) && max > 0 && fieldCount >= max ) {
6389 // disable button.
6390 elements.$panelDesign.find('button#charitable-add-fields-' + fieldType).addClass('charitable-disabled');
6391 }
6392 });
6393
6394 }
6395
6396 },
6397
6398 /**
6399 * Check the field deny list to see if any fields should be disabled.
6400 *
6401 * @since 1.8.0
6402 */
6403 checkFieldAllow: function () {
6404
6405 wpchar.debug( 'checkFieldAllow' );
6406
6407 // Assume all fields are enabled until we go through the deny list and find one instance where they would be disabled.
6408 $.each( s.denyList, function ( fieldPresent, fieldsNotAllowed ) {
6409 $.each( fieldsNotAllowed, function ( fieldsNotAllowedName, fieldsNotAllowedAmount ) { // eslint-disable-line
6410 app.enableAddFieldButton( fieldsNotAllowedName );
6411 });
6412 });
6413
6414 $.each( s.denyList, function ( fieldPresent, fieldsNotAllowed ) {
6415
6416 wpchar.debug( 'fieldPresent: ' + fieldPresent );
6417
6418 // s.denyList = {
6419 // 'donation-form': { 'donate-button' : 0, 'donation-form' : 0, 'donate-amount' : 0 },
6420 // 'donate-button': { 'donation-form' : 0 },
6421 // 'donate-amount': { 'donation-form' : 0 },
6422 // };
6423
6424 // check and see if the fieldPresent is... present.
6425 if ( app.checkFieldIsPresent( fieldPresent ) ) {
6426 $.each( fieldsNotAllowed, function ( fieldsNotAllowedName, fieldsNotAllowedAmount ) {
6427 if ( fieldsNotAllowedAmount === 0 || app.getFieldAmount( fieldsNotAllowedName ) > parseInt( fieldsNotAllowedAmount ) ) {
6428 wpchar.debug ( fieldsNotAllowedName + ' is not allowed');
6429 // fieldsNotAllowedName shouldn't be allowed at all.
6430 app.disableAddFieldButton( fieldsNotAllowedName );
6431 }
6432 });
6433 }
6434 });
6435
6436 },
6437
6438 /**
6439 * Check all recommended files on the left to see if they need to be checked or unchecked base don the fields in the preview area.
6440 *
6441 * @since 1.8.0
6442 */
6443 checkAllRecommendedFields: function () {
6444
6445 $.each( $('.charitable-add-fields-group-recommended button'), function ( ) {
6446
6447 var type = $(this).data('field-type');
6448
6449 app.checkRecommendedFields( type );
6450
6451 });
6452
6453 },
6454
6455 /**
6456 * Check to see if a single recommended field (passed) needs to be checked or unchecked base don the fields in the preview area.
6457 *
6458 * @since 1.8.0
6459 *
6460 * @param {string} type Field type.
6461 *
6462 */
6463 checkRecommendedFields: function ( type = false ) {
6464
6465 if ( ! type || type.length === 0 ) {
6466 return;
6467 }
6468
6469 var numberOfFieldsByType = elements.$preview.find( ".charitable-field[data-field-type='" + type + "']" ).length,
6470 checkForRecommended = $builder.find( '#charitable-add-fields-' + type ).parent().find('.charitable-check');
6471
6472 if ( typeof checkForRecommended === 'undefined' || checkForRecommended.length === 0 ) {
6473 return;
6474 }
6475
6476 if ( numberOfFieldsByType === 0 ) {
6477 checkForRecommended.removeClass('checked').addClass('unchecked');
6478 } else {
6479 checkForRecommended.removeClass('unchecked').addClass('checked');
6480 }
6481
6482 },
6483
6484 /**
6485 * Disable add field button.
6486 *
6487 * @since 1.8.0
6488 */
6489 disableAddFieldButton: function( fieldType ) {
6490
6491 elements.$panelDesign.find('button#charitable-add-fields-' + fieldType).addClass('charitable-disabled');
6492
6493 },
6494
6495 /**
6496 * Enable add field button.
6497 *
6498 * @since 1.8.0
6499 */
6500 enableAddFieldButton: function( fieldType = '' ) {
6501
6502 elements.$panelDesign.find('button#charitable-add-fields-' + fieldType).removeClass('charitable-disabled');
6503
6504 },
6505
6506 /**
6507 * Get field amount.
6508 *
6509 * @since 1.8.0
6510 */
6511 getFieldAmount: function ( fieldType = '' ) {
6512
6513 if ( fieldType.length === 0 ) {
6514 return;
6515 }
6516
6517 return elements.$preview.find('.charitable-field-' + fieldType).length;
6518
6519 },
6520
6521 /**
6522 * Check field is present.
6523 *
6524 * @since 1.8.0
6525 */
6526 checkFieldIsPresent: function ( fieldType = '' ) {
6527
6528 if ( fieldType.length === 0 ) {
6529 return false;
6530 }
6531
6532 if ( app.getFieldAmount( fieldType ) > 0 ) {
6533 return true;
6534 }
6535
6536 return false;
6537
6538 },
6539
6540 /**
6541 * Close confirmation setter.
6542 *
6543 * @since 1.8.0
6544 *
6545 * @param {boolean} confirm Close confirmation flag value.
6546 */
6547 setCloseConfirmation: function( confirm ) {
6548
6549 closeConfirmation = ! ! confirm;
6550
6551 },
6552
6553 /**
6554 * Check current form state.
6555 *
6556 * @since 1.8.0
6557 */
6558 formIsSaved: function() {
6559
6560 if ( $( '#charitable-builder-form input#charitable-form-saved' ).val().length === 0 && ( typeof s.formSaved !== 'undefined' ) && s.formSaved.length === 0 ) { // the hidden field exists but contains no value, both of these fields SHOULD be in sync
6561 return true;
6562 } else {
6563 return false;
6564 }
6565
6566 },
6567
6568 /**
6569 * Sets the campaign to a not saved mode.
6570 *
6571 * @since 1.8.1.12
6572 */
6573 setCampaignTitleNotSet: function () {
6574
6575 $('.charitable-edit-campaign-title-label').text( 'Name Your Campaign:' );
6576
6577 },
6578
6579 /**
6580 * Sets the campaign to a not saved mode.
6581 *
6582 * @since 1.8.1.12
6583 */
6584 setCampaignTitleSet: function () {
6585
6586 $('.charitable-edit-campaign-title-label').text( 'Now Editing:' );
6587
6588 },
6589
6590 /**
6591 * Sets the campaign to a not saved mode.
6592 *
6593 * @since 1.8.0
6594 */
6595 setCampaignNotSaved: function( enablePreviewButton = false ) { // eslint-disable-line
6596
6597 var the_time = Date.now();
6598
6599 $( '#charitable-builder-form input#charitable-form-saved' ).val( the_time );
6600 s.formSaved = the_time;
6601
6602 // if the user has done something to the form, then we need to update the preview button to reflect that (enable the preview button).
6603 elements.$previewButton.removeClass('charitable-disabled');
6604
6605 },
6606
6607 /**
6608 * Sets the campaign to a saved mode.
6609 *
6610 * @since 1.8.0
6611 */
6612 setCampaignSaved: function() {
6613
6614 $( '#charitable-builder-form input#charitable-form-saved' ).val( '' );
6615 s.formSaved = '';
6616
6617 // For published campaigns disable preview button because there's nothing to currently preview untit the user makes another change...
6618 // ...otherwise if it's saved in draft, user should be able to click the preview buton because there's no other way to preview it.
6619 if ( typeof s.formStatus !== 'undefined' && s.formStatus === 'publish' ) {
6620 elements.$previewButton.addClass('charitable-disabled');
6621 } else {
6622 elements.$previewButton.removeClass('charitable-disabled');
6623 }
6624
6625 },
6626
6627 /**
6628 * Bindings that prevent a user from entering anything but numbers, comma, and decimal point into a text field that represents a money value.
6629 *
6630 * @since 1.8.0
6631 */
6632 bindUIMoneyTextFields: function () {
6633
6634 // we will be monitoring:
6635 // #charitable-panel-field-settings-campaign_minimum_donation_amount
6636 // #charitable-panel-field-settings-campaign_goal
6637 $builder.on( 'keydown', 'input#charitable-panel-field-settings-campaign_minimum_donation_amount, input#charitable-panel-field-settings-campaign_goal', function( e ) { // eslint-disable-line
6638 var k = e.keyCode || e.which,
6639 // show keycode for decimal point
6640 ok = k == 190 || // period
6641 k == 188 || // comma
6642 k == 32 || // space
6643 k == 9 || // tab
6644 // k == 173 || // dash
6645 k == 8 || // backspaces
6646 ( e.ctrlKey && k == 65 ) || // Ctrl + A
6647 ( e.ctrlKey && k == 67 ) || // Ctrl + C
6648 ( e.ctrlKey && k == 88 ) || // Ctrl + X
6649 ( e.ctrlKey && k == 86 ) || // Ctrl + V
6650 ( e.metaKey && k == 65 ) || // command + a
6651 ( e.metaKey && k == 67 ) || // command + c
6652 ( e.metaKey && k == 88 ) || // command + x
6653 ( e.metaKey && k == 86 ) || // command + v
6654 ( k >= 96 && k <= 105 ) || // allow numbers entered from a number pad
6655 ( k == 110 || k == 190 ) || // period or decimal point
6656 ( k >= 37 && k <= 40 ) || // allow arrow keys
6657 ( k == 46 ) || // allow delete key
6658 ( k >= 48 && k <= 57 ); // only 0-9 (ok SHIFT options)
6659
6660 if ( !ok ) {
6661 e.preventDefault();
6662 }
6663
6664 } );
6665
6666 $builder.on( 'focusout', 'input#charitable-panel-field-settings-campaign_minimum_donation_amount, input#charitable-panel-field-settings-campaign_maximum_donation_amount, input#charitable-panel-field-settings-campaign_goal', function( e ) { // eslint-disable-line
6667 // add a decimal point followed by two zeros (.00) if the user clicks out of the input box and the money value is a whole number.
6668 var $this = $( this ),
6669 val = $this.val(),
6670 decimal_separator = charitable_builder.currency_decimal_separator,
6671 thousands_separator = charitable_builder.currency_thousands_separator;
6672
6673 // if undefined or empty, return.
6674 if ( typeof decimal_separator === 'undefined' || decimal_separator.length === 0 ) {
6675 return;
6676 }
6677
6678 if ( typeof thousands_separator === 'undefined' || thousands_separator.length === 0 ) {
6679 return;
6680 }
6681
6682 if ( val.length > 0 && val.indexOf( decimal_separator ) === -1 ) {
6683 $this.val( val + decimal_separator + '00' );
6684 }
6685
6686 } );
6687
6688 },
6689
6690 /**
6691 * Element bindings for general panel tasks.
6692 *
6693 * @since 1.8.0
6694 */
6695 bindUISettingsRevealGroups: function() {
6696
6697 $.each( elements.$settingsPanel.find( '.charitable-panel-fields-group.unfoldable' ), function() {
6698 // we found a reveal / foldable, see if we need to show it... find the id and see if the cookies exists.
6699 var $this = $( this ),
6700 dataGroup = $this.attr('data-group'),
6701 dataGroupCookie = wpCookies.get( 'charitable_fold_' + dataGroup );
6702
6703 if ( dataGroupCookie === 'true' && ! $this.hasClass('opened') ) {
6704 // open the section
6705 $this.addClass('opened');
6706 $this.find('.charitable-panel-fields-group-inner').show();
6707 }
6708
6709 });
6710
6711 elements.$settingsPanel.on( 'click', '.charitable-track-cookie', function( e ) { // eslint-disable-line
6712
6713 var $this = $( this ),
6714 dataGroup = $this.closest('.unfoldable').attr('data-group');
6715
6716 if ( ! $this.closest('.unfoldable').hasClass('opened') ) {
6717
6718 wpCookies.set( 'charitable_fold_' + dataGroup, true, 2592000 ); // 1 month.
6719
6720 } else {
6721
6722 wpCookies.remove( 'charitable_fold_' + dataGroup );
6723 }
6724
6725 } );
6726
6727 },
6728
6729 /**
6730 * Checks fields to see if OTHER fields can be shown/hidden. Logical conditional showings.
6731 *
6732 * @since 1.8.0
6733 */
6734 checkFieldConditionals: function() {
6735
6736 $.each( charitable_campaign_builder_field_conditionals, function( mainUIKey, mainUIValue) {
6737
6738 var theUIElement = elements.$settingsPanel.find( mainUIKey ),
6739 changeArray = mainUIValue;
6740
6741 if ( theUIElement.is('input[type="checkbox"]') ) {
6742
6743 elements.$settingsPanel.on( 'click', mainUIKey, function( e ) { // eslint-disable-line
6744
6745 var thisUIElement = $( this ), // eslint-disable-line
6746 isChecked = $(this).is(':checked'),
6747 checkedFields = changeArray['checked'],
6748 uncheckedFields = changeArray['unchecked'];
6749
6750 if ( isChecked ) {
6751
6752 $.each( checkedFields, function( checkedFieldKey, checkedFieldValue ) { // checkedFieldValue is the string selector for $
6753 elements.$settingsPanel.find( checkedFieldValue + '' ).removeClass('charitable-hidden');
6754 });
6755
6756 } else {
6757
6758 $.each( uncheckedFields, function( checkedFieldKey, checkedFieldValue ) { // checkedFieldValue is the string selector for $
6759 $.each( checkedFieldValue, function( hidingfieldKey, hidingfieldValue ) {
6760 elements.$settingsPanel.find( hidingfieldValue + '' ).addClass('charitable-hidden');
6761 });
6762 });
6763
6764 }
6765
6766 } );
6767
6768 }
6769
6770 if ( theUIElement.is('input[type="radio"]') ) {
6771
6772 elements.$settingsPanel.on( 'click', theUIElement.closest('.charitable-panel-field-radio-options').find('input[type="radio"]'), function( e ) { // eslint-disable-line
6773
6774 var thisUIElement = $( theUIElement ),
6775 isChecked = thisUIElement.is(':checked'),
6776 checkedFields = changeArray['checked'],
6777 uncheckedFields = changeArray['unchecked'],
6778 abort = false;
6779
6780 if ( isChecked ) {
6781
6782 $.each( checkedFields, function( checkedFieldKey, checkedFieldValue ) { // checkedFieldValue is the string selector for $
6783 if ( checkedFieldKey === 'if' ) {
6784 $.each( checkedFieldValue, function( checkedIfFieldKey, checkedIfFieldValue ) { // checkedFieldValue is the string selector for $
6785 if ( checkedIfFieldValue === 'checked' && false === elements.$settingsPanel.find( checkedIfFieldKey + '' ).is(':checked') ) {
6786 abort = true;
6787 }
6788 });
6789 }
6790
6791 if ( checkedFieldKey === 'show' && abort === false ) {
6792 elements.$settingsPanel.find( checkedFieldValue + '' ).removeClass('charitable-hidden');
6793 } else if ( checkedFieldKey === 'show' && abort === true ) {
6794 elements.$settingsPanel.find( checkedFieldValue + '' ).addClass('charitable-hidden');
6795 }
6796
6797 });
6798
6799 } else {
6800
6801 $.each( uncheckedFields, function( checkedFieldKey, checkedFieldValue ) { // checkedFieldValue is the string selector for $
6802 elements.$settingsPanel.find( checkedFieldValue + '' ).addClass('charitable-hidden');
6803 });
6804
6805 }
6806
6807 } );
6808
6809 }
6810
6811 });
6812
6813 },
6814
6815 /**
6816 * Check if the builder opened in the popup (iframe).
6817 *
6818 * @since 1.8.0
6819 *
6820 * @returns {boolean} True if builder opened in the popup.
6821 */
6822 isBuilderInPopup: function() {
6823
6824 return window.self !== window.parent && window.self.frameElement.id === 'charitable-builder-iframe';
6825 },
6826
6827 //--------------------------------------------------------------------//
6828 // General / global
6829 //--------------------------------------------------------------------//
6830
6831 /**
6832 * Element bindings for general and global items
6833 *
6834 * @since 1.8.0
6835 */
6836 bindUIActionsGeneral: function() {
6837
6838 // Toggle unfoldable group of fields.
6839 $builder.on( 'click', '.charitable-panel-fields-group.unfoldable .charitable-panel-fields-group-title', app.toggleUnfoldableGroup );
6840
6841 $builder.on( 'click', '.go-to-settings-button', function( e ) {
6842 e.preventDefault();
6843 app.panelSwitch( 'settings' );
6844 var $this = $( this ),
6845 section = ( $this ).data('settings-section'),
6846 $panel = $( '.charitable-panel-sidebar-content' ),
6847 $sectionButtons = $panel.find( '.charitable-panel-sidebar-section' ),
6848 $sectionButton = $panel.find( '.charitable-panel-sidebar-section-' + section ),
6849 cookieName = 'charitable_panel_sidebar_section';
6850
6851 if ( ! $sectionButton.hasClass( 'active' ) ) {
6852 app.panelSectionSwitchTo( section, $panel, $sectionButtons, $sectionButton );
6853 wpCookies.set( cookieName, section, 2592000 ); // 1 month.
6854 }
6855
6856 } );
6857
6858 // Mobile notice primary button / close icon click.
6859 $builder.on( 'click', '#charitable-builder-mobile-notice .charitable-fullscreen-notice-button-primary, #charitable-builder-mobile-notice .close', function() {
6860 window.location.href = charitable_builder.exit_url;
6861 } );
6862
6863 // Mobile notice secondary button click.
6864 $builder.on( 'click', '#charitable-builder-mobile-notice .charitable-fullscreen-notice-button-secondary', function() {
6865 window.location.href = wpchar.updateQueryString( 'force_desktop_view', 1, window.location.href );
6866 } );
6867
6868 },
6869
6870
6871 /**
6872 * Element bindings for general and global items
6873 *
6874 * @since 1.8.0
6875 */
6876 initFeedbackForms: function( $element ) {
6877
6878 var theForm = $element.closest('.charitable-form'),
6879 theConfirmation = theForm.find('.charitable-feedback-form-interior-confirmation'),
6880 data = {
6881 name : theForm.find('.charitable-feedback-form-name').val(),
6882 email : theForm.find('.charitable-feedback-form-email').val(),
6883 feedback : theForm.find('.charitable-feedback-form-feedback').val(),
6884 type : theForm.find('.charitable-feedback-form-type').val()
6885 };
6886
6887 // required fields
6888 if ( '' === data.name || '' === data.email || '' === data.feedback ) {
6889
6890 $.alert( {
6891 title: false,
6892 content : charitable_builder.feedback_form_fields_required,
6893 icon: 'fa fa-info-circle',
6894 type: 'red',
6895 buttons: {
6896 confirm: {
6897 text : charitable_builder.ok,
6898 btnClass : 'btn-confirm',
6899 keys : [ 'esc' ],
6900 action: function() {
6901 // delete the campaign.
6902 }
6903 }
6904 }
6905 } );
6906
6907
6908 } else {
6909
6910 $element.addClass('charitable-disabled');
6911 theForm.addClass('charitable-processing');
6912 theForm.find('.charitable-loading-spinner').removeClass('charitable-hidden');
6913
6914 var ajaxData = {
6915 action : 'charitable_campaign_builder_send_feedback_ajax',
6916 dataType : 'json',
6917 data : data,
6918 nonce : charitable_builder.nonce
6919 };
6920
6921 $.post( charitable_builder.ajax_url, ajaxData, function( response ) {
6922
6923 if ( response.success ) {
6924
6925 $element.removeClass('charitable-disabled');
6926 theForm.removeClass('charitable-processing');
6927 theForm.find('.charitable-feedback-form-interior').addClass('charitable-hidden');
6928 theConfirmation.removeClass('charitable-hidden');
6929 theConfirmation.find('.charitable-form-confirmation').removeClass('charitable-hidden');
6930
6931 }
6932
6933 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
6934
6935
6936 } ).always( function() {
6937
6938
6939 } );
6940
6941 }
6942
6943
6944 },
6945
6946 /**
6947 * Toggle unfoldable group of fields.
6948 *
6949 * @since 1.8.0
6950 *
6951 * @param {object} e Event object.
6952 */
6953 toggleUnfoldableGroup: function( e ) {
6954
6955 e.preventDefault();
6956
6957 var $title = $( e.target ),
6958 $group = $title.closest( '.charitable-panel-fields-group' ),
6959 $inner = $group.find( '.charitable-panel-fields-group-inner' ),
6960 cookieName = 'charitable_fields_group_' + $group.data( 'group' );
6961
6962 if ( $group.hasClass( 'opened' ) ) {
6963 wpCookies.remove( cookieName );
6964 $inner.stop().slideUp( 150, function() {
6965
6966 $group.removeClass( 'opened' );
6967 } );
6968 } else {
6969 wpCookies.set( cookieName, 'true', 2592000 ); // 1 month.
6970 $group.addClass( 'opened' );
6971 $inner.stop().slideDown( 150 );
6972 }
6973 },
6974
6975 /**
6976 * Update via ajax the debug window.
6977 *
6978 * @since 1.8.0
6979 *
6980 * @param {string} data Data.
6981 * @param {int} campaignID Camapign ID.
6982 */
6983 updateDebugWindow: function( data, campaignID = 0 ) {
6984
6985 var ajaxData = {
6986 action : 'charitable_update_debug_window_ajax',
6987 dataType : 'json',
6988 data : data,
6989 id : campaignID,
6990 nonce : charitable_builder.nonce
6991 };
6992
6993 $.post( charitable_builder.ajax_url, ajaxData, function( response ) {
6994
6995 if ( response.success ) {
6996 $('.charitable-debug').html( response.data );
6997 }
6998
6999 } ).fail( function( xhr, textStatus, e ) { // eslint-disable-line
7000
7001
7002 } ).always( function() {
7003
7004 // $label.text( text );
7005 // $saveBtn.prop( 'disabled', false );
7006 // $spinner.addClass( 'charitable-hidden' );
7007 // $icon.removeClass( 'charitable-hidden' );
7008
7009 } );
7010 },
7011
7012 //--------------------------------------------------------------------//
7013 // Alerts (notices).
7014 //--------------------------------------------------------------------//
7015
7016 /**
7017 * Click on the Dismiss notice button.
7018 *
7019 * @since 1.8.0
7020 */
7021 dismissNotice: function() {
7022
7023 $builder.on( 'click', '.charitable-alert-field-not-available .charitable-dismiss-button', function( e ) {
7024
7025 e.preventDefault();
7026
7027 var $button = $( this ),
7028 $alert = $button.closest( '.charitable-alert' ),
7029 fieldId = $button.data( 'field-id' );
7030
7031 $alert.addClass( 'out' );
7032 setTimeout( function() {
7033 $alert.remove();
7034 }, 250 );
7035
7036 if ( fieldId ) {
7037 $( '#charitable-field-option-' + fieldId ).remove();
7038 }
7039 } );
7040
7041 },
7042
7043 //--------------------------------------------------------------------//
7044 // Other functions.
7045 //--------------------------------------------------------------------//
7046
7047 /**
7048 * Init all the HTML textarea fields so that Quill, etc. runs in them.
7049 *
7050 * @since 1.8.0
7051 *
7052 * @param {object} $element Data.
7053 * @param {boolean} minmum Show or not show min interface for quill.
7054 */
7055 initHTMLEditorFields: function( $element, minmum = false ) {
7056
7057 if ( $.inArray( $element.attr('id'), s.quilled) == -1 ) {
7058
7059 var $container_id = '#' + $element.attr('id'),
7060 div_height = 200,
7061 textarea_name = $element.data('textarea-name'); // ql-editor
7062
7063 if ( minmum !== false ) {
7064 var toolbarOptions = ['bold', 'italic', 'underline', 'strike'];
7065
7066 var quill = new Quill( $container_id, { // eslint-disable-line
7067 debug: 'info',
7068 theme: 'snow',
7069 modules: {
7070 toolbar: toolbarOptions
7071 }
7072 });
7073
7074 $( $container_id ).css('height', div_height+'px');
7075 $( $container_id ).css('min-height', div_height+'px');
7076
7077 quill.focus;
7078
7079 s.quilled.push ( $element.attr('id') );
7080
7081 } else {
7082
7083 var quill = new Quill( $container_id, { // eslint-disable-line
7084 // debug: 'info',
7085 theme: 'snow'
7086 });
7087
7088 quill.focus;
7089
7090 s.quilled.push ( $element.attr('id') );
7091
7092 }
7093
7094 $( $container_id ).on( 'focus', '.ql-editor', function() { // eslint-disable-line
7095
7096 var contents = $element.find('.ql-editor').html();
7097
7098 if ( $element.closest('.charitable-panel-field-textarea').attr('data-special-type') === 'campaign_description' ) {
7099
7100 $('#charitable-panel-field-settings-campaign_description .ql-editor').html( contents );
7101 elements.$preview.find('.charitable-field-campaign-description .charitable-campaign-builder-placeholder-preview-text').html( '<div>' + contents + '</div>' );
7102 s.campaignDescription = contents;
7103 }
7104
7105 });
7106
7107 quill.on( 'text-change', function( delta, oldDelta, source ) { // eslint-disable-line
7108
7109 var contents = $element.find('.ql-editor').html(),
7110 tab_id = $element.closest('.charitable-group-row').data('tab-id'),
7111 field_id = $element.closest('.charitable-panel-field').data('field-id');
7112
7113 app.setCampaignNotSaved(); // this is a trigger.
7114
7115 // fill the form field as the user types
7116 $('input[name="' + textarea_name + '"]').val( contents );
7117
7118 // if this is the CAMPAIGN DESCRIPTION in a DESCRIPTION FIELD we need to update any campaign description field in the settings and other design/preview areas too.
7119 if ( $element.closest('.charitable-panel-field-textarea').attr('data-special-type') === 'campaign_description' ) {
7120
7121 if ( $.trim( app.removeTags( contents ) ) === '' ) {
7122 elements.$preview.find('.charitable-campaign-builder-no-description-preview').removeClass('charitable-hidden');
7123 elements.$preview.find('.charitable-field-campaign-description .charitable-campaign-builder-placeholder-preview-text').html( '<div></div>' );
7124 // elements.$fieldOptions.find('.campaign-builder-htmleditor .ql-editor').html( '<div></div>' );
7125 s.campaignDescription = '';
7126 } else {
7127 // elements.$preview.find('.charitable-campaign-builder-no-description-preview').addClass('charitable-hidden');
7128 // elements.$preview.find('.charitable-field-campaign-description .charitable-campaign-builder-placeholder-preview-text').html( app.removeTags( contents ) );
7129 elements.$preview.find('.charitable-field-campaign-description .charitable-campaign-builder-placeholder-preview-text').html( '<div>' + contents + '</div>' );
7130 // elements.$fieldOptions.find('.charitable-panel-field').not('[data-field-id="' + field_id + '"]').find('.campaign-builder-htmleditor').pasteHTML([{ insert: contents }]);
7131 s.campaignDescription = contents;
7132 }
7133
7134 $('#charitable-panel-field-settings-campaign_description .ql-editor').html( contents );
7135 }
7136
7137 if ( $element.closest('.charitable-panel-field-textarea').attr('data-special-type') === 'campaign_overview' ) {
7138 if ( $.trim( app.removeTags( contents ) ) === '' ) {
7139 elements.$preview.find('.charitable-campaign-builder-no-overview-preview').removeClass('charitable-hidden')
7140 elements.$preview.find('.charitable-field-campaign-overview .charitable-campaign-builder-placeholder-preview-text').html( '<div></div>' );
7141 s.campaignDescription = '';
7142 } else {
7143 elements.$preview.find('.charitable-field-campaign-overview .charitable-campaign-builder-placeholder-preview-text').html( '<div>' + contents + '</div>' );
7144 s.campaignDescription = contents;
7145 }
7146
7147 $('#charitable-panel-field-settings-campaign_overview .ql-editor').html( contents );
7148 }
7149
7150 if ( $element.closest('.charitable-panel-field-textarea').attr('data-special-type') === 'organizer_content' ) {
7151
7152 field_id = parseInt( $element.closest('.charitable-panel-field-textarea').data('field-id') );
7153
7154 if ( $.trim( app.removeTags( contents ) ) === '' ) {
7155 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-organizer-description').html( '<div></div>' );
7156 } else {
7157 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-organizer-description').html( '<div>' + contents + '</div>' );
7158 }
7159
7160 }
7161
7162 if ( $element.closest('.charitable-panel-field-textarea').attr('data-special-type') === 'text' ) {
7163
7164 field_id = parseInt( $element.closest('.charitable-panel-field-textarea').data('field-id') );
7165
7166 if ( $.trim( app.removeTags( contents ) ) === '' ) {
7167 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-campaign-builder-placeholder-preview-text').html( '<div></div>' );
7168 } else {
7169 elements.$preview.find('#charitable-field-' + field_id + ' .charitable-campaign-builder-placeholder-preview-text').html( '<div>' + contents + '</div>' );
7170 }
7171
7172 // $('#charitable-panel-field-settings-campaign_overview .ql-editor').html( contents );
7173 }
7174
7175 if ( minmum !== false ) {
7176 var div_height = parseInt( $element.find('.ql-editor').height() / 2 );
7177 $('#tab_' + tab_id + '_content .placeholder.big').css('min-height', div_height+'px');
7178 }
7179
7180 });
7181
7182 } else {
7183
7184 /* already on the list */
7185
7186 }
7187
7188 },
7189
7190 /**
7191 * Init the suggested donations area in campaign settings.
7192 *
7193 * @since 1.8.0
7194 *
7195 * @param {object} $element Data.
7196 */
7197 initSuggestedDonations: function ( $element ) {
7198
7199 wpchar.debug( $element, 'initSuggestedDonations' );
7200
7201 var $table = $element.closest( '.charitable-campaign-suggested-donations' ),
7202 $add_row_button = $element.find('[data-charitable-add-row]'),
7203 donation_type = $add_row_button.data( 'charitable-add-row' ), // eslint-disable-line
7204 $delete_row_button = $element.find('.charitable-delete-row');
7205
7206 $add_row_button.on( 'click', function( event ) {
7207
7208 event.preventDefault();
7209 event.stopImmediatePropagation();
7210
7211 var type = $( this ).data( 'charitable-add-row' );
7212
7213 if ( 'suggested-amount' === type ) {
7214 app.add_suggested_amount_row( $( this ) );
7215 } else if ( 'suggested-recurring-amount' === type ) {
7216 app.add_suggested_amount_row( $( this ), 'recurring' );
7217 }
7218
7219 app.initSuggestedDonations( $element );
7220 return false;
7221 });
7222
7223 $delete_row_button.on( 'click', function( event ) {
7224
7225 event.preventDefault();
7226 event.stopImmediatePropagation();
7227
7228 var donation_type = $( this ).closest( '.charitable-campaign-suggested-donations' ).find('[data-charitable-add-row]').data( 'charitable-add-row' );
7229
7230 if ( 'suggested-amount' === donation_type ) {
7231 app.delete_suggested_amount_row( $( this ) );
7232 } else if ( 'suggested-recurring-amount' === donation_type ) {
7233 app.delete_suggested_amount_row( $( this ), 'recurring' );
7234 }
7235
7236 app.initSuggestedDonations( $element );
7237 return false;
7238 });
7239
7240 $('.charitable-campaign-suggested-donations tbody').sortable({
7241 items: "tr:not(.to-copy)",
7242 handle: ".handle",
7243 stop: function( event, ui ) { // eslint-disable-line
7244 app.reindex_rows();
7245 // app.redrawDonationAmountsPreview( $table );
7246 $( '.charitable-campaign-suggested-donations-mini' ).each( function() {
7247 // app.initSuggestedDonationsMini( $( this ) );
7248 app.updateSuggestdDonationsMiniRowsFromSettings( $( this ) );
7249 app.redrawDonationAmountsPreview( $( this ) );
7250 });
7251 }
7252
7253 });
7254
7255 $table.on( 'click', 'th.default_amount-col a, a.charitable-clear-defaults', function() {
7256 $table.find('.default_amount-col input').prop('checked', false);
7257 $( this ).blur();
7258 return false;
7259 });
7260
7261 // if there is any change in the radio button update with redrawDonationAmountsPreview.
7262 $table.on( 'change', 'input[type="radio"]', function() {
7263 app.redrawDonationAmountsPreview( $table );
7264 });
7265
7266 },
7267
7268 /**
7269 * Just update the mini rows of a suggested donations table.
7270 *
7271 * @since 1.8.0
7272 *
7273 * @param {object} $element Data.
7274 * @param {int} field_id Field ID.
7275 */
7276 updateSuggestdDonationsMiniRowsFromSettings: function( $element, field_id = 0 ) {
7277
7278 if ( 0 === field_id ) {
7279 field_id = $element.closest('.charitable-panel-field').data('field-id');
7280 }
7281
7282 // Remove all rows in this table except the table with the class 'to-copy'.
7283 $element.find('tbody').children('tr').not('.to-copy').remove();
7284
7285 // Get the values from the donation suggestions table in settings and update them for this table.
7286 $('#campaign_donation_amounts tbody').children('tr').not('.no-suggested-amounts').not('.to-copy').each( function( index ) { // eslint-disable-line
7287 var row_to_add = '',
7288 updatedIndex = index + 1,
7289 updatedDonationValue = $( this ).find('.amount-col input').val(),
7290 isChecked = $( this ).find('.default_amount-col input').is(':checked') ? 'checked' : '';
7291
7292 row_to_add = row_to_add + '<tr class="" data-index="' + updatedIndex + '">';
7293 row_to_add = row_to_add + '<td class="reorder-col"><span class="charitable-icon charitable-icon-donations-grab handle ui-sortable-handle"></span></td>';
7294 row_to_add = row_to_add + '<td class="default_amount-col"><input ' + isChecked + ' type="radio" class="campaign_suggested_donations" name="_fields][' + field_id + '][donation_amounts][campaign_suggested_donations_default][]" value="' + updatedIndex + '"></td>';
7295 row_to_add = row_to_add + '<td class="amount-col"><input autocomplete="false" type="text" class="campaign_suggested_donations" name="_fields][' + field_id + '][donation_amounts][' + updatedIndex + '][amount]" value="' + updatedDonationValue + '" placeholder="Amount"></td>';
7296 row_to_add = row_to_add + '<td class="description-col"><input type="text" class="campaign_suggested_donations" name="_fields][' + field_id + '][donation_amounts][' + updatedIndex + '][description]" value="This is a small donation." placeholder="Optional Description">';
7297 row_to_add = row_to_add + '</td>';
7298 row_to_add = row_to_add + '<td class="remove-col"><span class="dashicons-before dashicons-dismiss charitable-delete-row"></span></td>';
7299 row_to_add = row_to_add + '</tr>';
7300
7301 $element.find('tbody').append( row_to_add );
7302
7303 });
7304
7305 return $element;
7306
7307 },
7308
7309 /**
7310 * Just update the mini rows of a suggested donations table.
7311 *
7312 * @since 1.8.0
7313 *
7314 * @param {object} $element Data.
7315 * @param {int} field_id Field ID.
7316 */
7317 updateSuggestdDonationsRowsFromSettings: function( $miniTableTBody, $element ) {
7318
7319 // Provide a default value.
7320 if ( typeof $element === 'undefined' || $element.length === 0 ) {
7321 $element = $('#campaign_donation_amounts');
7322 }
7323
7324 // Remove all rows in this table except the table with the class 'to-copy'.
7325 $element.find('tbody').children('tr').not('.to-copy').remove();
7326
7327 // Get the values from the donation suggestions table in settings and update them for this table.
7328 $miniTableTBody.children('tr').not('.no-suggested-amounts').not('.to-copy').each( function( index ) { // eslint-disable-line
7329 var row_to_add = '',
7330 updatedIndex = index + 1,
7331 updatedDonationValue = $( this ).find('.amount-col input').val(),
7332 isChecked = $( this ).find('.default_amount-col input').is(':checked') ? 'checked' : '';
7333
7334 row_to_add = row_to_add + '<tr class="" data-index="' + updatedIndex + '">';
7335 row_to_add = row_to_add + '<td class="reorder-col"><span class="charitable-icon charitable-icon-donations-grab handle ui-sortable-handle"></span></td>';
7336 row_to_add = row_to_add + '<td class="default_amount-col"><input ' + isChecked + ' type="radio" class="campaign_suggested_donations" name="settings][donation-options][donation_amounts][campaign_suggested_donations_default][]" value="' + updatedIndex + '">';
7337 row_to_add = row_to_add + '<td class="amount-col"><input autocomplete="off" type="text" class="campaign_suggested_donations" name="settings][donation-options][donation_amounts][' + updatedIndex + '][amount]" value="' + updatedDonationValue + '" placeholder="Amount">';
7338 row_to_add = row_to_add + '<td class="description-col"><input type="text" class="campaign_suggested_donations" name="settings][donation-options][donation_amounts][' + updatedIndex + '][description]" value="This is a small donation." placeholder="Optional Description">';
7339 row_to_add = row_to_add + '</td>';
7340 row_to_add = row_to_add + '<td class="remove-col"><span class="dashicons-before dashicons-dismiss charitable-delete-row"></span></td>';
7341 row_to_add = row_to_add + '</tr>';
7342
7343 $element.find('tbody').append( row_to_add );
7344
7345 });
7346
7347 return $element;
7348
7349 },
7350
7351 /**
7352 * Init the suggested donations area in campaign settings.
7353 *
7354 * @since 1.8.0
7355 *
7356 * @param {object} $element Data.
7357 */
7358 initSuggestedDonationsMini: function ( $element ) {
7359
7360 wpchar.debug( $element, 'initSuggestedDonationsMini' );
7361 wpchar.debug( 'initSuggestedDonationsMini' );
7362
7363 var field_id = $element.closest('.charitable-panel-field').data('field-id');
7364
7365 wpchar.debug( $element, 'initSuggestedDonationsMini' );
7366
7367 app.updateSuggestdDonationsMiniRowsFromSettings( $element, field_id );
7368
7369 wpchar.debug( $element, 'initSuggestedDonationsMini2' );
7370
7371 app.redrawDonationAmountsPreview( $element );
7372
7373 wpchar.debug( $element, 'initSuggestedDonationsMini3' );
7374
7375 var $table = $element.closest( '.charitable-campaign-suggested-donations-mini' ),
7376 $add_row_button = $element.find('[data-charitable-add-row]');
7377
7378 $add_row_button.on( 'click', function() {
7379 var type = $( this ).data( 'charitable-add-row' );
7380
7381 if ( 'suggested-amount' === type ) {
7382 wpchar.debug ('type is suggesetd amount');
7383 wpchar.debug( $ ( this ) );
7384 app.add_suggested_amount_row( $( this ), 'mini' );
7385 }
7386 return false;
7387 });
7388
7389 $('.charitable-campaign-suggested-donations-mini tbody').sortable({
7390 items: "tr:not(.to-copy)",
7391 handle: ".handle",
7392 stop: function( event, ui ) { // eslint-disable-line
7393 wpchar.debug( 'sortable test mini ');
7394 app.reindex_rows( 'mini' );
7395 app.updateSuggestdDonationsRowsFromSettings( $( this ) );
7396 app.redrawDonationAmountsPreview( $( this ).parent() );
7397 }
7398
7399 });
7400
7401 $table.on( 'click', '.charitable-delete-row', function() {
7402 app.delete_suggested_amount_row( $( this ), 'mini' );
7403 return false;
7404 });
7405
7406 $table.on( 'click', 'th.default_amount-col a', function() {
7407 $table.find('.default_amount-col input').prop('checked', false);
7408 $( this ).blur();
7409 return false;
7410 });
7411
7412 // if there is any change in the radio button update with redrawDonationAmountsPreview.
7413 $table.on( 'change', 'input[type="radio"]', function() {
7414 app.redrawDonationAmountsPreview( $table );
7415 // If there is a change to the radio buttons, make sure to sync this change so that everyone ahs the same default donation.
7416 // Get the value of this radio button.
7417 var radioButtonValue = $(this).val();
7418 $builder.find( '.charitable-campaign-suggested-donations-table' ).find('input[type="radio"]').prop('checked', false );
7419 $builder.find( '.charitable-campaign-suggested-donations-table' ).each( function() {
7420 $(this).find('input[type="radio"][value="' + radioButtonValue + '"]').prop('checked', true );
7421 });
7422 });
7423
7424 },
7425
7426 /**
7427 * Adds a row in the suggestion dations area.
7428 *
7429 * @since 1.8.0
7430 *
7431 * @param {object} $button Button, clicked.
7432 * @param {string} type The type of table to reindex.
7433 */
7434 add_suggested_amount_row: function( $button, type = '' ) { // eslint-disable-line
7435
7436 wpchar.debug('add_suggested_amount_row');
7437 wpchar.debug('.charitable-campaign-suggested-donations' + type );
7438
7439 const $donations_table = type === 'recurring' ? $builder.find( '.charitable-campaign-suggested-recurring-donations-table' ) : $builder.find( '.charitable-campaign-suggested-donations-table' );
7440
7441 // Go through the donations table instances, in settings or not.
7442 $donations_table.each( function() {
7443
7444 wpchar.debug( $( this ) );
7445
7446 var $table = $(this).closest('table').find('tbody'),
7447 $clone = $table.find('tr.to-copy').clone().removeClass('to-copy hidden');
7448
7449 // find the data-index in the tr in the clone and update it to the number of rows that already exist in the table.
7450 var newIndex = $table.find('tr:not(.to-copy)').length + 1;
7451 $clone.attr('data-index', newIndex );
7452
7453 // Replace the [0] in the input name with the data-index number.
7454 var inputFieldName = $clone.find('.amount-col input[type="text"].campaign_suggested_donations').attr('name'),
7455 newInputFieldName = inputFieldName.replace('[0]', '[' + newIndex + ']'),
7456 inputFieldNameDescription = $clone.find('.description-col input[type="text"].campaign_suggested_donations').attr('name'),
7457 newInputFieldNameDescription = inputFieldNameDescription.replace('[0]', '[' + newIndex + ']');
7458 wpchar.debug( $clone.find('.amount-col input[type="text"].campaign_suggested_donations') );
7459 wpchar.debug( $clone.find('.amount-col input[type="text"].campaign_suggested_donations').attr('name') );
7460 $clone.find('.amount-col input[type="text"].campaign_suggested_donations').attr('name', newInputFieldName );
7461 $clone.find('.description-col input[type="text"].campaign_suggested_donations').attr('name', newInputFieldNameDescription );
7462 $clone.find('.default_amount-col input[type="radio"]').val( newIndex );
7463
7464 wpchar.debug( $table, 'table' );
7465 wpchar.debug( $clone, 'clone' );
7466
7467 $table.find( '.no-suggested-amounts' ).hide();
7468 $table.append( $clone );
7469 $clone.on( 'click', '.charitable-delete-row', function() {
7470 app.delete_suggested_amount_row( $(this), type );
7471 return false;
7472 });
7473
7474 });
7475
7476 app.redrawDonationAmountsPreview( $builder.find( '.charitable-campaign-suggested-donations-table' ).first() );
7477
7478 app.reindex_rows( type );
7479 if ( type === '' ) {
7480 app.toggle_custom_donations_checkbox();
7481 }
7482
7483 },
7484
7485 /**
7486 * Removes a row in the suggestion dontions area.
7487 *
7488 * @since 1.8.0
7489 *
7490 * @param {object} $button Button, clicked.
7491 * @param {string} type The type of table to reindex.
7492 */
7493 delete_suggested_amount_row: function( $button, type = '' ) {
7494
7495 wpchar.debug('delete_suggested_amount_row');
7496 wpchar.debug( $button );
7497 wpchar.debug( type );
7498
7499 var $row_to_delete = $button.closest( 'tr '),
7500 row_to_delete_index = parseInt( $row_to_delete.attr('data-index') );
7501
7502 wpchar.debug( $row_to_delete );
7503 wpchar.debug( row_to_delete_index, 'row_to_delete_index' );
7504
7505 const $donations_table = type === 'recurring' ? $builder.find( '.charitable-campaign-suggested-recurring-donations-table' ) : $builder.find( '.charitable-campaign-suggested-donations-table' ),
7506 donations_table_class = type === 'recurring' ? '.charitable-campaign-suggested-recurring-donations-table' : '.charitable-campaign-suggested-donations-table';
7507
7508 if ( row_to_delete_index > 0 ) {
7509
7510 wpchar.debug( $donations_table.find(' tbody tr[data-index="' + row_to_delete_index + '"]') );
7511 wpchar.debug( donations_table_class + ' tbody tr[data-index="' + row_to_delete_index + '"]' );
7512
7513 // remove the row in all instances of the donations table
7514 $donations_table.find(' tbody tr[data-index="' + row_to_delete_index + '"]').remove();
7515
7516 }
7517
7518 app.redrawDonationAmountsPreview( $builder.find( '.charitable-campaign-suggested-donations-table' ).first() );
7519
7520 var $table = $button.closest('table').find('tbody');
7521
7522 if ( $table.find( 'tr:not(.to-copy)' ).length == 1 ){
7523 $table.find( '.no-suggested-amounts' ).removeClass('hidden').show();
7524 }
7525
7526 // Reindex the rows.
7527 app.reindex_rows( type );
7528 if ( type === '' ) {
7529 app.reindex_rows( 'mini' );
7530 } else if ( type === 'mini' ) {
7531 app.reindex_rows();
7532 }
7533 if ( type === '' ) {
7534 app.toggle_custom_donations_checkbox();
7535 }
7536
7537 },
7538
7539 /**
7540 * Reindexes the rows in the suggestion dontions area.
7541 *
7542 * @since 1.8.0
7543 *
7544 * @param {string} type The type of table to reindex.
7545 *
7546 */
7547 reindex_rows: function( type = '' ) {
7548
7549 wpchar.debug('reindex rows with type: ' + type );
7550
7551 if ( type !== '' ) {
7552 type = '-' + type;
7553 }
7554
7555 $('.charitable-campaign-suggested-donations' + type + ' tbody').each(function(){
7556 $(this).children('tr').not('.no-suggested-amounts .to-copy').each( function( index ) {
7557 $(this).find('input[type="radio"]').val( index );
7558 $(this).attr('data-index', index );
7559 $(this).find('input').each(function(i) { // eslint-disable-line
7560 this.name = this.name.replace(/(\[\d\])/, '[' + index + ']');
7561 });
7562 });
7563 });
7564
7565 },
7566
7567 /**
7568 * Redraws the donation amounts preview based on the values in the suggested donations table.
7569 *
7570 * @since 1.8.0
7571 *
7572 * @param {object} targetTable The table to redraw the preview for.
7573 *
7574 */
7575 redrawDonationAmountsPreview: function( targetTable ) {
7576
7577 // for each ul element called 'charitable-preview-donation-amounts'...
7578 elements.$preview.find('ul.charitable-preview-donation-amounts').each( function( index ) { // eslint-disable-line
7579 var $theDontionAmountsList = $( this );
7580
7581 // remove all the li.
7582 $theDontionAmountsList.find('li').remove();
7583 // look at targetTable - a suggested donations table - and read each row and repopulate the amounts unordered list.
7584 $( targetTable ).find('tbody').children('tr').not('.no-suggested-amounts').not('.to-copy').not('hidden').each( function( index ) { // eslint-disable-line
7585 var theInputTextValue = $( this ).find('input[type="text"].campaign_suggested_donations').first().val();
7586
7587 // If the radio button is checked, add the class 'selected' to the li.
7588 var selected = '';
7589 wpchar.debug($( this ).find('input[type="radio"]'));
7590 if ( $( this ).find('input[type="radio"]').is(':checked') ) {
7591 selected = ' selected';
7592 }
7593 $theDontionAmountsList.append( '<li class="charitable-preview-donation-amount suggested-donation-amount' + selected + '"><label><input type="radio" name="donation_amount" value="' + index + '"><span class="amount">' + theInputTextValue + '</span></label></li>' );
7594 });
7595
7596 // Add the custom donation HTML if the checkbox is checked.
7597 if ( $( '#charitable-panel-field-settings-campaign_allow_custom_donations').is(':checked') ) {
7598 $theDontionAmountsList.append( '<li class="charitable-preview-donation-amount custom-donation-amount "><span class="custom-donation-amount-wrapper"><label><input type="radio" name="donation_amount" value="custom"><span class="description">Custom amount</span></label><input type="text" disabled="&quot;true&quot;" class="custom-donation-input" name="custom_donation_amount" placeholder="Custom Donation Amount" value=""></span></li>' );
7599 }
7600
7601 });
7602
7603 },
7604
7605 /**
7606 * Add handler for the toggle that allows/disallows custom donations.
7607 *
7608 * @since 1.8.0
7609 *
7610 */
7611 toggle_custom_donations_checkbox: function() {
7612 var $custom = $('#campaign_allow_custom_donations'),
7613 $suggestions = $('.charitable-campaign-suggested-donations tbody tr:not(.to-copy)'),
7614 has_suggestions = $suggestions.length > 1 || false === $suggestions.first().hasClass('no-suggested-amounts');
7615
7616 $custom.prop( 'disabled', ! has_suggestions );
7617
7618 if ( ! has_suggestions ) {
7619 $custom.prop( 'checked', true );
7620 }
7621 },
7622
7623 /**
7624 * Init the tag field in campaign settings.
7625 *
7626 * @since 1.8.0
7627 *
7628 * @param {object} $element Data.
7629 */
7630 initTagField: function( $element ) {
7631
7632 $element.select2();
7633
7634 },
7635
7636 /**
7637 * Init color picker fields in campaign settings.
7638 *
7639 * @since 1.8.0
7640 *
7641 * @param {object} $element Data.
7642 */
7643 initColorPicker: function() {
7644
7645 Coloris({ // eslint-disable-line
7646 el: '.coloris'
7647 });
7648
7649 Coloris.setInstance('.instance2.primary', { // eslint-disable-line
7650 defaultColor: s.primaryThemeColor,
7651 onChange : function ( color ) {
7652 if ( '' === color ) {
7653 $('input[name="layout__advanced__theme_color_primary"]').val( s.primaryThemeColorBase );
7654 s.primaryThemeColor = s.primaryThemeColorBase;
7655 document.querySelector('input[name="layout__advanced__theme_color_primary"]').dispatchEvent(new Event('input', { bubbles: true }));
7656 } else {
7657 s.primaryThemeColor = color;
7658 }
7659 app.updateThemeCSS( 'primary', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7660 },
7661 theme: 'polaroid',
7662 themeMode: 'light',
7663 alpha: false,
7664 formatToggle: true,
7665 closeButton: true,
7666 clearButton: true,
7667 clearLabel: 'Reset',
7668 swatches: [
7669 '#067bc2',
7670 '#84bcda',
7671 '#80e377',
7672 '#ecc30b',
7673 '#f37748',
7674 '#d56062'
7675 ]
7676 });
7677
7678 Coloris.setInstance('.instance2.secondary', { // eslint-disable-line
7679 defaultColor: s.secondaryThemeColor,
7680 onChange : function ( color ) {
7681 if ( '' === color ) {
7682 $('input[name="layout__advanced__theme_color_secondary"]').val( s.secondaryThemeColorBase );
7683 s.secondaryThemeColor = s.secondaryThemeColorBase;
7684 app.updateThemeCSS( 'secondary', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7685 document.querySelector('input[name="layout__advanced__theme_color_secondary"]').dispatchEvent(new Event('input', { bubbles: true }));
7686 } else {
7687 s.secondaryThemeColor = color;
7688 app.updateThemeCSS( 'secondary', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7689 }
7690 },
7691 theme: 'polaroid',
7692 themeMode: 'light',
7693 alpha: false,
7694 formatToggle: true,
7695 closeButton: true,
7696 clearButton: true,
7697 clearLabel: 'Reset',
7698 swatches: [
7699 '#067bc2',
7700 '#84bcda',
7701 '#80e377',
7702 '#ecc30b',
7703 '#f37748',
7704 '#d56062'
7705 ]
7706 });
7707
7708 Coloris.setInstance('.instance2.tertiary', { // eslint-disable-line
7709 defaultColor: s.tertiaryThemeColor,
7710 onChange : function ( color ) {
7711 if ( '' === color ) {
7712 $('input[name="layout__advanced__theme_color_tertiary"]').val( s.tertiaryThemeColorBase );
7713 s.tertiaryThemeColor = s.tertiaryThemeColorBase;
7714 app.updateThemeCSS( 'teritary', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7715 document.querySelector('input[name="layout__advanced__theme_color_tertiary"]').dispatchEvent(new Event('input', { bubbles: true }));
7716 } else {
7717 s.tertiaryThemeColor = color;
7718 app.updateThemeCSS( 'teritary', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7719 }
7720 },
7721 theme: 'polaroid',
7722 themeMode: 'light',
7723 alpha: false,
7724 formatToggle: true,
7725 closeButton: true,
7726 clearButton: true,
7727 clearLabel: 'Reset',
7728 swatches: [
7729 '#067bc2',
7730 '#84bcda',
7731 '#80e377',
7732 '#ecc30b',
7733 '#f37748',
7734 '#d56062'
7735 ]
7736 });
7737
7738 Coloris.setInstance('.instance2.button-color', { // eslint-disable-line
7739 defaultColor: s.buttonThemeColor,
7740 onChange : function ( color ) {
7741 if ( '' === color ) {
7742 $('input[name="layout__advanced__theme_color_button"]').val( s.buttonThemeColorBase );
7743 s.buttonThemeColor = s.buttonThemeColorBase;
7744 app.updateThemeCSS( 'button', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7745 document.querySelector('input[name="layout__advanced__theme_color_button"]').dispatchEvent(new Event('input', { bubbles: true }));
7746 } else {
7747 s.buttonThemeColor = color;
7748 app.updateThemeCSS( 'button', s.templateID, s.primaryThemeColor, s.secondaryThemeColor, s.tertiaryThemeColor, s.buttonThemeColor, true );
7749 }
7750 },
7751 theme: 'polaroid',
7752 themeMode: 'light',
7753 alpha: false,
7754 formatToggle: true,
7755 closeButton: true,
7756 clearButton: true,
7757 clearLabel: 'Reset',
7758 swatches: [
7759 '#067bc2',
7760 '#84bcda',
7761 '#80e377',
7762 '#ecc30b',
7763 '#f37748',
7764 '#d56062'
7765 ]
7766 });
7767
7768 /* make it so clicking the badge triggers the popup */
7769 elements.$fieldOptions.on( 'click', '.clr-field button', function( e ) {
7770 e.preventDefault();
7771 $( this ).parent().find('input[type="text"]').trigger('click');
7772 });
7773
7774 },
7775
7776 /**
7777 * Init the date pickers in campaign settings.
7778 *
7779 * @since 1.8.0
7780 *
7781 * @param {object} $element Data.
7782 */
7783 initDatePicker: function( $element ) {
7784
7785 var $the_element = $element,
7786 options = {
7787 dateFormat : $the_element.data('format') || 'MM d, yy',
7788 minDate : $the_element.data('min-date') || '',
7789 beforeShow : function( input, inst ) { // eslint-disable-line
7790 setTimeout( function() {
7791 $('.ui-datepicker').css('z-index', 99999999999999);
7792 }, 0);
7793 }
7794 };
7795
7796 if ( $.isFunction( $the_element.datepicker ) ) {
7797
7798 $the_element.datepicker( options );
7799
7800 if ( $the_element.data('date') ) {
7801 $the_element.datepicker( 'setDate', this.$el.data('date') );
7802 }
7803
7804 if ( $the_element.data('min-date') ) {
7805 $the_element.datepicker( 'option', 'minDate', this.$el.data('min-date') );
7806 }
7807
7808 }
7809 },
7810
7811 /**
7812 * Trim long form titles.
7813 *
7814 * @since 1.8.0
7815 */
7816 trimFormTitle: function() {
7817
7818 var $title = $( '.charitable-center-form-name' );
7819
7820 if ( $title.text().length > 38 ) {
7821 var shortTitle = $title.text().trim().substring( 0, 38 ).split( ' ' ).slice( 0, -1 ).join( ' ' ) + '...';
7822 $title.text( shortTitle );
7823 }
7824
7825 },
7826
7827 /**
7828 * Hotkeys:
7829 * Ctrl+t - Templates.
7830 *
7831 * @since 1.8.0
7832 * @since 1.8.0.4 Added ctrl+x to exit builder.
7833 */
7834 builderHotkeys: function() {
7835
7836 hotkeys('esc,ctrl+1,ctrl+2,ctrl+3,ctrl+4,ctrl+5,ctrl+s,ctrl+p,ctrl+x,ctrl+v', function ( event, handler ){ // eslint-disable-line
7837 switch (handler.key) {
7838 case 'esc':
7839 // this is a non-standard modal popup, so we want to close it manually with the esc key.
7840 if ( $('.charitable-builder-modal.charitable-builder-modal-template-preview').hasClass('active') ) {
7841 $('.charitable-template-list-container').removeClass('disabled');
7842 $('#charitable-builder-underlay').remove();
7843 $('.charitable-builder-modal.charitable-builder-modal-template-preview').removeClass('active');
7844 }
7845 break;
7846 case 'ctrl+1':
7847 $( elements.$templateButton, $builder ).trigger( 'click' );
7848 break;
7849 case 'ctrl+2':
7850 $( elements.$designButton, $builder ).trigger( 'click' );
7851 break;
7852 case 'ctrl+3':
7853 $( elements.$settingsButton, $builder ).trigger( 'click' );
7854 break;
7855 case 'ctrl+4':
7856 $( elements.$marketingButton, $builder ).trigger( 'click' );
7857 break;
7858 case 'ctrl+5':
7859 $( elements.$paymentButton, $builder ).trigger( 'click' );
7860 break;
7861 case 'ctrl+s': // Save Campaign
7862 $( elements.$saveButton, $builder ).trigger( 'click' );
7863 break;
7864 case 'ctrl+p': // Preview
7865 $( elements.$previewButton, $builder ).trigger( 'click' );
7866 break;
7867 case 'ctrl+x':
7868 $( elements.$exitButton, $builder ).trigger( 'click' );
7869 break;
7870 case 'ctrl+v':
7871 $( elements.$viewCampaignButton, $builder ).trigger( 'click' );
7872 break;
7873 }
7874 });
7875
7876 },
7877
7878 /* LITE OR UPDATE/INSTALL SPECIFIC */
7879
7880 /**
7881 * Registers click events that should open upgrade modal.
7882 *
7883 * @since 1.8.0
7884 */
7885 openModalButtonClick: function() {
7886
7887 $( document )
7888 .on( 'click', '.charitable-not-available:not(.charitable-add-fields-button)', app.openModalButtonHandler )
7889 .on( 'mousedown', '.charitable-not-available.charitable-add-fields-button', app.openModalButtonHandler );
7890
7891 $( document )
7892 .on( 'click', '.charitable-disabled-modal:not(.charitable-add-fields-button)', app.openModalWarningModal )
7893 .on( 'mousedown', '.charitable-add-fields-button.charitable-disabled-modal', app.openModalWarningModal );
7894
7895 $( document )
7896 .on( 'click', '.charitable-disabled-same_page:not(.charitable-add-fields-button)', app.openModalWarningModal )
7897 .on( 'mousedown', '.charitable-add-fields-button.charitable-disabled-same_page', app.openModalWarningModal );
7898
7899 $( document )
7900 .on( 'click', '.charitable-not-installed:not(.charitable-add-fields-button)', app.openModalButtonHandlerInstall )
7901 .on( 'mousedown', '.charitable-not-installed.charitable-add-fields-button', app.openModalButtonHandlerInstall );
7902
7903 $( document )
7904 .on( 'click', '.charitable-not-activated:not(.charitable-add-fields-button)', app.openModalButtonHandlerActivate )
7905 .on( 'mousedown', '.charitable-not-activated.charitable-add-fields-button', app.openModalButtonHandlerActivate );
7906
7907 $( document )
7908 .on( 'click', '.charitable-addon-file-missing:not(.charitable-add-fields-button)', app.openModalButtonHandlerInstall )
7909 .on( 'mousedown', '.charitable-addon-file-missing.charitable-add-fields-button', app.openModalButtonHandlerInstall );
7910
7911 $( document )
7912 .on( 'click', '.charitable-not-available:not(.charitable-setting-panel-upgrade-to-pro)', app.openModalButtonHandler )
7913 .on( 'mousedown', '.charitable-not-available.charitable-setting-panel-upgrade-to-pro', app.openModalButtonHandler );
7914
7915 $( document )
7916 .on( 'click', '.charitable-need-upgrade:not(.charitable-setting-panel-upgrade-to-pro)', app.openModalButtonHandler )
7917 .on( 'mousedown', '.charitable-need-upgrade.charitable-setting-panel-upgrade-to-pro', app.openModalButtonHandler );
7918
7919 $( document )
7920 .on( 'click', '.charitable-installed-refresh:not(.charitable-add-fields-button)', app.openModalButtonHandlerActivatedRefresh )
7921 .on( 'mousedown', '.charitable-installed-refresh.charitable-add-fields-button', app.openModalButtonHandlerActivatedRefresh );
7922
7923
7924 /* the below represent buttons inside the preview area in the design tab */
7925
7926 $( document )
7927 .on( 'click', 'a.button-link.charitable-not-activated', app.openModalButtonHandlerActivate )
7928 .on( 'mousedown', 'a.button-link.charitable-not-activated', app.openModalButtonHandlerActivate );
7929
7930 $( document )
7931 .on( 'click', 'a.button-link.charitable-not-installed', app.openModalButtonHandlerInstall )
7932 .on( 'mousedown', 'a.button-link.charitable-not-installed', app.openModalButtonHandlerInstall );
7933
7934 /* the below represent buttons inside the marketing area */
7935
7936 $( document )
7937 .on( 'click', 'a.button-link.charitable-not-activated-button', app.activateFromButton )
7938 .on( 'mousedown', 'a.button-link.charitable-not-activated-button', app.activateFromButton );
7939
7940 $( document )
7941 .on( 'click', 'a.button-link.charitable-not-installed-button', app.installFromButton )
7942 .on( 'mousedown', 'a.button-link.charitable-not-installed-button', app.installFromButton );
7943
7944 },
7945
7946 /**
7947 * Open the upgrade modal to activate an extension/plugin from a sepcific button click.
7948 *
7949 * @since 1.8.1.12
7950 *
7951 * @param {object} e Event.
7952 *
7953 * @return {void}
7954 *
7955 */
7956 activateFromButton: function( e ) {
7957 e.preventDefault();
7958
7959 const $button = $( this ),
7960 plugin_url = $( this ).data( 'plugin-url' ),
7961 plugin_name = $( this ).data( 'name' ),
7962 settings_url = $( this ).data( 'settings-url' ),
7963 plugin_slug = $( this ).data( 'plugin-slug' ),
7964 enable_url = $( this ).data( 'enable-url' ).length > 0 ? $( this ).data( 'enable-url' ) : '';
7965
7966 // check to makes sure all the variables above are set.
7967 if ( ! plugin_url || ! plugin_name ) {
7968 return;
7969 }
7970
7971 app.installFromButtonAjax( plugin_url, plugin_name, settings_url, plugin_slug, enable_url, 'activate', 'addon', $button );
7972 },
7973
7974 /**
7975 * Open the upgrade modal to install an extension/plugin from a sepcific button click.
7976 *
7977 * @since 1.8.1.12
7978 *
7979 * @param {object} e Event.
7980 *
7981 * @return {void}
7982 *
7983 */
7984 installFromButton: function( e ) {
7985 e.preventDefault();
7986
7987 const $button = $( this ),
7988 plugin_url = $( this ).data( 'plugin-url' ),
7989 plugin_name = $( this ).data( 'name' ),
7990 settings_url = $( this ).data( 'settings-url' ),
7991 plugin_slug = $( this ).data( 'plugin-slug' ),
7992 enable_url = $( this ).data( 'enable-url' ).length > 0 ? $( this ).data( 'enable-url' ) : '';
7993
7994 // check to makes sure all the variables above are set.
7995 if ( ! plugin_url || ! plugin_name ) {
7996 return;
7997 }
7998
7999 app.installFromButtonAjax( plugin_url, plugin_name, settings_url, plugin_slug, enable_url, 'install', 'addon', $button );
8000 },
8001
8002
8003 /**
8004 * Change plugin/addon state.
8005 *
8006 * @since 1.8.0
8007 *
8008 * @param {string} plugin_url Plugin URL.
8009 * @param {string} plugin_name Plugin name.
8010 */
8011 installFromButtonAjax: function( plugin_url, plugin_name, settings_url, plugin_slug, enable_url, state, pluginType, $button ) {
8012
8013 wpchar.debug ( 'setAddonState' );
8014 wpchar.debug ( plugin_url );
8015 wpchar.debug ( plugin_name );
8016 wpchar.debug ( state );
8017
8018 var actions = {
8019 'activate': 'charitable_activate_addon',
8020 'install': 'charitable_install_addon',
8021 'deactivate': 'charitable_deactivate_addon'
8022 },
8023 action = actions[ state ];
8024
8025 if ( ! action ) {
8026 return;
8027 }
8028
8029 var plugin_ajax = plugin_url;
8030
8031 // update the text of the button.
8032 if ( 'install' === state ) {
8033 if ( enable_url.length > 0 ) { // this is a payment extension
8034 $button.text( 'Installing and activating gateway...' );
8035 } else {
8036 $button.text( 'Installing and activating...' );
8037 }
8038 $button.removeClass( 'charitable-not-installed-button' );
8039 $button.addClass( 'charitable-view-settings-button' );
8040 } else if ( 'activate' === state ) {
8041 if ( enable_url.length > 0 ) { // this is a payment extension
8042 $button.text( 'Activating gateway...' );
8043 } else {
8044 $button.text( 'Activating...' );
8045 }
8046 $button.removeClass( 'charitable-not-activated-button' );
8047 $button.addClass( 'charitable-view-settings-button' );
8048 plugin_ajax = plugin_slug;
8049 }
8050
8051 // Override the plugin var in the ajax if this is activation.
8052 var data = {
8053 action: action,
8054 nonce: charitable_admin.nonce, // eslint-disable-line
8055 plugin: plugin_ajax,
8056 type: pluginType
8057 };
8058
8059 wpchar.debug ( data );
8060
8061 $.post( charitable_admin.ajax_url, data, function( res ) { // eslint-disable-line
8062
8063 // update the text of the button.
8064 if ( 'install' === state ) {
8065 // all buttons that have a data attribute of data-plugin-slug that matches "test".
8066 $( 'a.button-link[data-plugin-slug="' + plugin_slug + '"]' ).each( function() {
8067 $( this ).text( 'View Settings' );
8068 $( this ).removeClass( 'charitable-not-installed-button' );
8069 $( this ).addClass( 'charitable-view-settings-button' );
8070 if ( enable_url.length > 0 ) { // this is a payment extension
8071 $( this ).attr( 'href', enable_url );
8072 } else {
8073 $( this ).attr( 'href', settings_url );
8074 }
8075 $( this ).attr( 'target', '_blank' );
8076 });
8077
8078 $( 'section.header-content h2' ).addClass('charitable-hidden');
8079 $( 'section.header-content h2.charitable-header-content-activated' ).removeClass('charitable-hidden');
8080
8081
8082 } else if ( 'activate' === state ) {
8083 // all buttons that have a data attribute of data-plugin-slug that matches "test".
8084 $( 'a.button-link[data-plugin-slug="' + plugin_slug + '"]' ).each( function() {
8085 $( this ).text( 'View Settings' );
8086 $( this ).removeClass( 'charitable-not-installed-button' );
8087 $( this ).addClass( 'charitable-view-settings-button' );
8088 if ( enable_url.length > 0 ) { // this is a payment extension
8089 $( this ).attr( 'href', enable_url );
8090 } else {
8091 $( this ).attr( 'href', settings_url );
8092 }
8093 $( this ).attr( 'target', '_blank' );
8094 });
8095
8096 $( 'section.header-content h2' ).addClass('charitable-hidden');
8097 $( 'section.header-content h2.charitable-header-content-activated' ).removeClass('charitable-hidden');
8098 }
8099
8100 } ).fail( function( xhr ) {
8101
8102 wpchar.debug( xhr.responseText );
8103
8104 } );
8105 },
8106
8107 /**
8108 * Open "you're using modal" modal handler.
8109 *
8110 * @since 1.8.0
8111 *
8112 * @param {Event} event Event.
8113 */
8114 openModalWarningModal: function( event ) {
8115
8116 const $this = $( this );
8117
8118 event.preventDefault();
8119 event.stopImmediatePropagation();
8120
8121 let name = $this.data( 'name' ),
8122 icon = '',
8123 reason = '';
8124
8125 if ( $this.hasClass( 'charitable-add-fields-button' ) ) {
8126 name = $this.text();
8127 name += name.indexOf( charitable_builder.field ) < 0 ? ' ' + charitable_builder.field : '';
8128 // if the button has an attribute of data-icon, we want to use that icon instead of the default one.
8129 if ( $this.data('field-icon') ) {
8130 icon = $this.data('field-icon');
8131 }
8132 }
8133
8134 if ( $this.hasClass( 'charitable-disabled-modal' ) ) {
8135 reason = 'modal';
8136 } else if ( $this.hasClass( 'charitable-disabled-same_page' ) ) {
8137 reason = 'same_page';
8138 }
8139
8140 app.modalWarningModal( name, reason, icon );
8141 },
8142
8143 /**
8144 * Open education modal handler.
8145 *
8146 * @since 1.8.0
8147 *
8148 * @param {Event} event Event.
8149 */
8150 openModalButtonHandlerInstall: function( event ) {
8151
8152 const $this = $( this );
8153
8154 var icon = '',
8155 plugin_url = '',
8156 video = '',
8157 license = '',
8158 elementType = false;
8159
8160 if ( $this.data( 'action' ) && [ 'activate', 'install' ].includes( $this.data( 'action' ) ) ) {
8161 return;
8162 }
8163
8164 event.preventDefault();
8165 event.stopImmediatePropagation();
8166
8167 let name = $this.data( 'name' );
8168
8169 if ( $this.hasClass( 'charitable-add-fields-button' ) ) {
8170 name = $this.text();
8171 name += name.indexOf( charitable_builder.field ) < 0 ? ' ' + charitable_builder.field : '';
8172 // if the button has an attribute of data-icon, we want to use that icon instead of the default one.
8173 if ( $this.data('field-icon') ) {
8174 icon = $this.data('field-icon');
8175 }
8176 }
8177
8178 if ( $this.data( 'install' ) ) {
8179 plugin_url = $this.data( 'install' );
8180 } else if ( $this.data( 'plugin-url' ) ) {
8181 plugin_url = $this.data('plugin-url');
8182 }
8183
8184 if ( $this.data( 'video' ) ) {
8185 video = $this.data('video');
8186 }
8187
8188 if ( $this.data( 'license' ) ) {
8189 license = $this.data('license');
8190 }
8191
8192 app.installModal( name, plugin_url, license, video, $this, elementType, icon );
8193 },
8194
8195 /**
8196 * Open education modal handler.
8197 *
8198 * @since 1.8.0
8199 *
8200 * @param {Event} event Event.
8201 */
8202 openModalButtonHandlerActivatedRefresh: function( event ) {
8203
8204 const $this = $( this );
8205
8206 event.preventDefault();
8207 event.stopImmediatePropagation();
8208
8209 let name = $this.data( 'name' );
8210
8211 app.activateRefreshModal( name, $this.data( 'video' ) );
8212 },
8213
8214 /**
8215 * Open education modal handler.
8216 *
8217 * @since 1.8.0
8218 *
8219 * @param {Event} event Event.
8220 */
8221 openModalButtonHandlerActivate: function( event ) {
8222
8223 const $this = $( this );
8224
8225 var icon = '',
8226 plugin_url = '',
8227 video = '',
8228 license = '',
8229 elementType = false;
8230
8231 if ( $this.data( 'action' ) && [ 'activate', 'install' ].includes( $this.data( 'action' ) ) ) {
8232 return;
8233 }
8234
8235 event.preventDefault();
8236 event.stopImmediatePropagation();
8237
8238 let name = $this.data( 'name' );
8239
8240 if ( $this.data('plugin-url') ) {
8241 plugin_url = $this.data('plugin-url');
8242 }
8243
8244 if ( $this.data('video') ) {
8245 video = $this.data('video');
8246 }
8247
8248 if ( $this.data('license') ) {
8249 license = $this.data('license');
8250 }
8251
8252 app.activateModal( name, plugin_url, license, video, $this, elementType, icon );
8253 },
8254
8255
8256 /**
8257 * Open education modal handler.
8258 *
8259 * @since 1.8.0
8260 *
8261 * @param {Event} event Event.
8262 */
8263 openModalButtonHandler: function( event ) {
8264
8265 const $this = $( this );
8266
8267 if ( $this.data( 'action' ) && [ 'activate', 'install' ].includes( $this.data( 'action' ) ) ) {
8268 return;
8269 }
8270
8271 event.preventDefault();
8272 event.stopImmediatePropagation();
8273
8274 let icon = '',
8275 name = $this.data( 'name' ),
8276 elementType = $this.data( 'type' );
8277
8278 if ( $this.hasClass( 'charitable-add-fields-button' ) ) {
8279 name = $this.text();
8280 name += name.indexOf( charitable_builder.field ) < 0 ? ' ' + charitable_builder.field : '';
8281 // if the button has an attribute of data-icon, we want to use that icon instead of the default one.
8282 if ( $this.data('field-icon') ) {
8283 icon = $this.data('field-icon');
8284 }
8285 }
8286
8287 const utmContent = 'utmValue'; // charitableEducation.core.getUTMContentValue( $this );
8288
8289 app.upgradeModal( name, utmContent, $this.data( 'license' ), $this.data( 'video' ), elementType, icon );
8290 },
8291
8292 /**
8293 * Upgrade modal.
8294 *
8295 * @since 1.8.0
8296 *
8297 * @param {string} feature Field name or feature.
8298 * @param {string} reason slug for the why reason.
8299 * @param {string} icon icon slug.
8300 */
8301 modalWarningModal: function( feature, reason = '', icon = '' ) {
8302
8303 var message = '',
8304 modalWidth = app.getUpgradeModalWidth( false ),
8305 title = '';
8306
8307 if ( reason === 'modal' ) {
8308 message = charitable_builder.field_disabled_due_to_modal.replace( /%name%/g, feature )
8309 } else if ( reason === 'same_page' ) {
8310 message = charitable_builder.field_disabled_due_to_same_page.replace( /%name%/g, feature )
8311 }
8312
8313 var modal = $.alert( { // eslint-disable-line
8314 backgroundDismiss: true,
8315 title : title,
8316 icon : icon !== '' ? 'fa ' + icon : 'fa fa-thumbs-up',
8317 content : message,
8318 boxWidth : modalWidth,
8319 theme : 'modern,charitable-install-form',
8320 closeIcon : true,
8321 buttons : {
8322 confirm: {
8323 text : charitable_builder.ok,
8324 btnClass: 'btn-confirm',
8325 keys : [ 'enter' ],
8326 action: function() {
8327
8328 }
8329 },
8330 cancel: {
8331 text : charitable_builder.go_to_settings,
8332 btnClass: 'btn-confirm',
8333 action: function() {
8334 window.open( charitable_builder.settings_page_url );
8335 }
8336 }
8337 }
8338 } );
8339
8340 },
8341
8342 /**
8343 * Change plugin/addon state.
8344 *
8345 * @since 1.8.0
8346 *
8347 * @param {string} plugin Plugin slug or URL for download (example: charitable-geolocation/charitable-geolocation.php)
8348 * @param {string} state State status activate|deactivate|install.
8349 * @param {string} pluginType Plugin type addon or plugin.
8350 * @param {Function} callback Callback for get result from AJAX.
8351 */
8352 setAddonState: function( plugin_url, plugin_name, state, pluginType, modal, clickedObject, callback ) {
8353
8354 wpchar.debug ( 'setAddonState' );
8355 wpchar.debug ( plugin_url );
8356 wpchar.debug ( plugin_name );
8357 wpchar.debug ( state );
8358
8359 var actions = {
8360 'activate': 'charitable_activate_addon',
8361 'install': 'charitable_install_addon',
8362 'deactivate': 'charitable_deactivate_addon'
8363 },
8364 action = actions[ state ];
8365
8366 if ( ! action ) {
8367 return;
8368 }
8369
8370 var data = {
8371 action: action,
8372 nonce: charitable_admin.nonce, // eslint-disable-line
8373 plugin: plugin_url,
8374 type: pluginType
8375 };
8376
8377 wpchar.debug ( data );
8378
8379 $.post( charitable_admin.ajax_url, data, function( res ) { // eslint-disable-line
8380
8381 callback( res, plugin_url, plugin_name, state, modal, clickedObject );
8382
8383 } ).fail( function( xhr ) {
8384
8385 wpchar.debug( xhr.responseText );
8386
8387 } );
8388 },
8389
8390 /**
8391 * Respond for the plugin/addon state.
8392 *
8393 * @since 1.8.0
8394 *
8395 */
8396 callBackAddonState: function( res, plugin_url = '', plugin_name = '', state = '', modal = false, clickedObject ) {
8397
8398 wpchar.debug ( 'callBackAddonState' );
8399 wpchar.debug ( res );
8400 wpchar.debug ( state );
8401 wpchar.debug ( modal );
8402
8403 var successText = '';
8404
8405 if ( res.success ) {
8406
8407 if ( 'install' === state ) {
8408
8409 wpchar.debug('installed');
8410
8411 // close the current modal window, if you can.
8412 if ( s.currentModal !== false && 'object' === typeof s.currentModal ) {
8413 s.currentModal.close();
8414 }
8415
8416 if ( res.data.is_activated ) {
8417 successText = res.data.msg;
8418 app.upgradeModalAddonInstalledAndActivated( plugin_url, plugin_name, successText );
8419 } else {
8420 app.upgradeModalAddonInstalledAndActivatedFailed( plugin_url, plugin_name, successText );
8421 }
8422
8423 } else if ( 'activate' === state ) {
8424
8425 wpchar.debug('activated');
8426
8427 successText = res.data;
8428
8429 // close the current modal window, if you can.
8430 if ( s.currentModal !== false && 'object' === typeof s.currentModal ) {
8431 s.currentModal.close();
8432 }
8433
8434 app.upgradeModalAddonActivated( plugin_url, plugin_name, successText );
8435
8436 // Add class class to clickedObject, which should be the button they clicked.
8437 clickedObject.addClass( 'charitable-installed-refresh' ).removeClass( 'charitable-not-activated' );
8438 app.openModalButtonClick();
8439
8440 } else {
8441
8442 wpchar.debug('some other success');
8443
8444 successText = res.data;
8445
8446 // close the current modal window, if you can.
8447 if ( s.currentModal !== false && 'object' === typeof s.currentModal ) {
8448 s.currentModal.close();
8449 }
8450
8451 app.upgradeModalAddonActivated( plugin_url, plugin_name, successText );
8452
8453 // Add class class to clickedObject, which should be the button they clicked.
8454 clickedObject.addClass( 'charitable-installed-refresh' ).removeClass( 'charitable-not-activated' );
8455 app.openModalButtonClick();
8456
8457 }
8458
8459 } else {
8460
8461 // close the current modal window, if you can.
8462 if ( s.currentModal !== false && 'object' === typeof s.currentModal ) {
8463 s.currentModal.close();
8464 }
8465
8466 app.upgradeModalAddonActivatedFailed( plugin_url, plugin_name, successText );
8467
8468 }
8469
8470 },
8471
8472 /**
8473 * Refresh notice modal.
8474 *
8475 * @since 1.8.0
8476 *
8477 * @param {string} feature Feature name.
8478 * @param {string} type Feature license type: 'pro', 'basic', 'plus', 'agency', 'elite'.
8479 * @param {string} video Feature video URL.
8480 * @param {string} elementType Element type.
8481 */
8482 activateRefreshModal: function( name, video, icon = '' ) {
8483
8484 wpchar.debug('activateRefreshModal');
8485 wpchar.debug( name );
8486 wpchar.debug( video );
8487
8488 var message = charitable_builder.activated_refresh.replace( /%addon%/g, name ),
8489 isVideoModal = ! _.isEmpty( video ), // eslint-disable-line
8490 modalWidth = app.getUpgradeModalWidth( isVideoModal ),
8491 title = '<span class="charitable-upgrade-pro-title">' + name + ' ' + charitable_builder.activated_refresh_title + '</span>';
8492
8493 var modal = $.alert( {
8494 backgroundDismiss: true,
8495 title : title,
8496 icon : icon !== '' ? 'fa ' + icon : 'fa fa-thumbs-up',
8497 content : message,
8498 boxWidth : modalWidth,
8499 theme : 'modern,charitable-activate-refresh',
8500 closeIcon : true,
8501 buttons : {
8502 confirm: {
8503 text : charitable_builder.ok,
8504 btnClass: 'btn-confirm',
8505 keys : [ 'enter' ]
8506 }
8507 }
8508 } );
8509
8510 $( window ).on( 'resize', function() {
8511
8512 modalWidth = app.getUpgradeModalWidth( isVideoModal );
8513
8514 if ( modal.isOpen() ) {
8515 modal.setBoxWidth( modalWidth );
8516 }
8517 } );
8518 },
8519
8520 /**
8521 * Activate addon modal.
8522 *
8523 * @since 1.8.0
8524 *
8525 * @param {string} feature Feature name.
8526 * @param {string} plugin_url Likely the url that allows a download.
8527 * @param {string} type Feature license type: 'pro', 'basic', 'plus', 'agency', 'elite'.
8528 * @param {string} video Feature video URL.
8529 * @param {string} clickedObject What was clicked on.
8530 * @param {string} elementType Element type.
8531 * @param {string} icon Icon slug.
8532 */
8533 activateModal: function( feature, plugin_url, type, video, clickedObject, elementType = false, icon = '' ) {
8534
8535 wpchar.debug('activatelModal');
8536 wpchar.debug( feature );
8537 wpchar.debug( plugin_url );
8538 wpchar.debug( type );
8539 wpchar.debug( video );
8540 wpchar.debug( elementType );
8541
8542 // Provide a default value.
8543 if ( typeof type === 'undefined' || type.length === 0 ) {
8544 type = 'pro';
8545 }
8546
8547 var message = charitable_builder.activate.message.replace( /%addon%/g, feature ),
8548 isVideoModal = ! _.isEmpty( video ), // eslint-disable-line
8549 modalWidth = app.getUpgradeModalWidth( isVideoModal ),
8550 title = '<span class="charitable-upgrade-pro-title">' + feature + ' ' + charitable_builder.activate.title + '</span>';
8551
8552 var modal = $.alert( {
8553 backgroundDismiss: true,
8554 title : title,
8555 icon : icon !== '' ? 'fa ' + icon : 'fa fa-thumbs-up',
8556 content : message,
8557 boxWidth : modalWidth,
8558 theme : 'modern,charitable-activate-addon',
8559 closeIcon : true,
8560 buttons : {
8561 cancel: {
8562 btnClass : 'btn-confirm',
8563 keys : [ 'esc' ],
8564 isHidden : false, // initially not hidden
8565 isDisabled : false, // initially not disabled
8566 action: function() {
8567 }
8568 },
8569 install: {
8570 text : charitable_builder.activate.button,
8571 btnClass: 'btn-confirm',
8572 keys : [ 'enter' ],
8573 isHidden : false, // initially not hidden
8574 isDisabled : false, // initially not disabled
8575 action: function( activateButton ) {
8576 // change the name of the button to let the user know it's installing, or at least trying.
8577 activateButton.setText( charitable_builder.activating );
8578 activateButton.disable();
8579 this.$$cancel.prop('disabled', true);
8580 this.$$cancel.hide();
8581 app.setAddonState( plugin_url, feature, 'activate', 'addon', $( this ), clickedObject, app.callBackAddonState );
8582 return false;
8583 }
8584 }
8585 }
8586 } );
8587
8588 s.currentModal = modal;
8589
8590 $( window ).on( 'resize', function() {
8591
8592 modalWidth = app.getUpgradeModalWidth( isVideoModal );
8593
8594 if ( modal.isOpen() ) {
8595 modal.setBoxWidth( modalWidth );
8596 }
8597 } );
8598 },
8599
8600 /**
8601 * Install modal.
8602 *
8603 * @since 1.8.0
8604 *
8605 * @param {string} feature Feature name.
8606 * @param {string} plugin_url Likely the url that allows a download.
8607 * @param {string} type Feature license type: 'pro', 'basic', 'plus', 'agency', 'elite'.
8608 * @param {string} video Feature video URL.
8609 * @param {object} clickedObject What was clicked on.
8610 * @param {string} elementType Element type.
8611 * @param {string} icon Icon slug.
8612 */
8613 installModal: function( feature, plugin_url, type, video, clickedObject, elementType = false, icon = '' ) {
8614
8615 wpchar.debug('installModal');
8616 wpchar.debug( feature );
8617 wpchar.debug( plugin_url );
8618 wpchar.debug( type );
8619 wpchar.debug( video );
8620 wpchar.debug( clickedObject );
8621 wpchar.debug( elementType );
8622
8623 // Provide a default value.
8624 if ( typeof type === 'undefined' || type.length === 0 ) {
8625 type = 'pro';
8626 }
8627
8628 // Make sure we received only supported types.
8629 if ( $.inArray( type, [ 'pro', 'basic', 'plus', 'agency', 'elite' ] ) < 0 ) {
8630 return;
8631 }
8632
8633 var license_label = charitable_builder.charitable_license_label,
8634 message = charitable_builder.install[ type ].message.replace( /%name%/g, license_label ).replace( /%addon%/g, feature ),
8635 isVideoModal = ! _.isEmpty( video ), // eslint-disable-line
8636 modalWidth = app.getUpgradeModalWidth( isVideoModal ),
8637 title = '';
8638
8639 if ( elementType ) {
8640 title = '<span class="charitable-upgrade-pro-title">' + feature + ' ' + charitable_builder.install['pro-panel'].title + '</span>';
8641 } else {
8642 title = '<span class="charitable-upgrade-pro-title">' + feature + ' ' + charitable_builder.install[type].title + '</span>';
8643 }
8644
8645 var modal = $.alert( {
8646 backgroundDismiss : false,
8647 title : title,
8648 icon : icon !== '' ? 'fa ' + icon : 'fa fa-thumbs-up',
8649 content : message,
8650 boxWidth : modalWidth,
8651 theme : 'modern,charitable-install-form',
8652 closeIcon : false,
8653 buttons : {
8654 cancel: {
8655 btnClass : 'btn-confirm',
8656 keys : [ 'esc' ],
8657 isHidden : false, // initially not hidden
8658 isDisabled : false, // initially not disabled
8659 action: function() {
8660 }
8661 },
8662 confirmInstall: {
8663 text : charitable_builder.install[type].button,
8664 btnClass : 'btn-confirm',
8665 keys : [ 'enter' ],
8666 isHidden : false, // initially not hidden
8667 isDisabled : false, // initially not disabled
8668 action: function( confirmInstallButton ) {
8669 // change the name of the button to let the user know it's installing, or at least trying.
8670 confirmInstallButton.setText( charitable_builder.installing );
8671 confirmInstallButton.disable();
8672 this.$$cancel.prop('disabled', true);
8673 this.$$cancel.hide();
8674
8675 app.setAddonState( plugin_url, feature, 'install', 'addon', $( this ), clickedObject, app.callBackAddonState );
8676 return false;
8677 }
8678 }
8679 }
8680 } );
8681
8682 s.currentModal = modal;
8683
8684 $( window ).on( 'resize', function() {
8685
8686 modalWidth = app.getUpgradeModalWidth( isVideoModal );
8687
8688 if ( modal.isOpen() ) {
8689 modal.setBoxWidth( modalWidth );
8690 }
8691 } );
8692 },
8693
8694 /**
8695 * Upgrade modal for the Form panel (Lite only). Uses custom copy but
8696 * reuses the shared upgrade modal theme.
8697 *
8698 * @since 1.8.10.5
8699 */
8700 formPanelUpgradeModal: function() {
8701
8702 // Prevent stacking — bail if a modal is already open.
8703 if ( app.formPanelUpgradeModalOpen ) {
8704 return;
8705 }
8706
8707 var strings = ( typeof charitable_builder !== 'undefined' && charitable_builder.form_upgrade_modal ) ? charitable_builder.form_upgrade_modal : {};
8708
8709 app.formPanelUpgradeModalOpen = true;
8710
8711 $.alert( {
8712 backgroundDismiss: true,
8713 title : strings.title || '',
8714 icon : 'fa fa-lock',
8715 content : strings.message || '',
8716 boxWidth : app.getUpgradeModalWidth( false ),
8717 theme : 'modern,charitable-upgrade-form-lite',
8718 closeIcon : true,
8719 onOpenBefore: function() {
8720
8721 this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
8722
8723 if ( strings.doc ) {
8724 this.$btnc.after( strings.doc );
8725 }
8726 },
8727 onClose: function() {
8728 app.formPanelUpgradeModalOpen = false;
8729 },
8730 buttons: {
8731 confirm: {
8732 text : strings.button || '',
8733 btnClass: 'btn-confirm',
8734 keys : [ 'enter' ],
8735 action: function() {
8736 if ( strings.upgrade_url ) {
8737 window.open( strings.upgrade_url, '_blank' );
8738 }
8739 }
8740 }
8741 }
8742 } );
8743 },
8744
8745 /**
8746 * Upgrade modal.
8747 *
8748 * @since 1.8.0
8749 *
8750 * @param {string} feature Feature name.
8751 * @param {string} utmContent UTM content.
8752 * @param {string} type Feature license type: pro or elite.
8753 * @param {string} video Feature video URL.
8754 * @param {string} elementType Element type.
8755 * @param {string} icon Icon slug.
8756 */
8757 upgradeModal: function( feature, utmContent, type, video, elementType = false, icon = '' ) {
8758
8759 // Provide a default value.
8760 if ( typeof type === 'undefined' || type.length === 0 ) {
8761 type = 'pro';
8762 }
8763
8764 // Make sure we received only supported types.
8765 if ( $.inArray( type, [ 'pro', 'basic', 'plus', 'agency', 'elite' ] ) < 0 ) {
8766 return;
8767 }
8768
8769 var isVideoModal = ! _.isEmpty( video ), // eslint-disable-line
8770 modalWidth = app.getUpgradeModalWidth( isVideoModal ),
8771 title = '',
8772 message = '',
8773 button = '',
8774 typeCapitlized = 'pro' !== type.toLowerCase() ? type.charAt(0).toUpperCase() + type.slice(1) : 'PRO';
8775
8776 if ( elementType ) {
8777 title = feature + ' ' + charitable_builder.upgrade['pro-panel'].title.replace( /%plan%/g, typeCapitlized ),
8778 message = charitable_builder.upgrade[ 'pro-panel' ].message.replace( /%name%/g, feature ),
8779 message = message.replace( /%plan%/g, typeCapitlized ),
8780 button = charitable_builder.upgrade[ 'pro-panel' ].button.replace( /%name%/g, feature ).replace( 'addon', '' ),
8781 button = button.replace( /%plan%/g, typeCapitlized );
8782
8783 } else {
8784 title = feature + ' ' + charitable_builder.upgrade[type].title.replace( /%plan%/g, typeCapitlized ),
8785 message = charitable_builder.upgrade[ type ].message.replace( /%name%/g, feature ),
8786 message = message.replace( /%plan%/g, typeCapitlized ),
8787 button = charitable_builder.upgrade[ type ].button.replace( /%name%/g, feature ).replace( 'addon', '' ),
8788 button = button.replace( /%plan%/g, typeCapitlized );
8789 }
8790
8791 var modal = $.alert( {
8792 backgroundDismiss: true,
8793 title : title,
8794 icon : icon !== '' ? 'fa ' + icon : 'fa fa-lock',
8795 content : message,
8796 boxWidth : modalWidth,
8797 theme : 'modern,charitable-upgrade-form-lite',
8798 closeIcon : true,
8799 onOpenBefore: function() {
8800
8801 if ( isVideoModal ) {
8802 this.$el.addClass( 'has-video' );
8803 }
8804
8805 var videoHtml = isVideoModal ? '<iframe src="' + video + '" class="feature-video" frameborder="0" allowfullscreen="" width="475" height="267"></iframe>' : '';
8806
8807 // this.$btnc.after( '<div class="discount-note">' + charitable_builder.upgrade_bonus_modal + '</div>' );
8808 this.$btnc.after( charitable_builder.upgrade[type].doc.replace( /%25name%25/g, feature ) );
8809 this.$btnc.after( videoHtml );
8810
8811 this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
8812 },
8813 buttons : {
8814 confirm: {
8815 text : button,
8816 btnClass: 'btn-confirm',
8817 keys : [ 'enter' ],
8818 action: function() {
8819 window.open( app.getUpgradeURL( utmContent, type ), '_blank' );
8820 app.upgradeModalThankYou( type );
8821 }
8822 }
8823 }
8824 } );
8825
8826 $( window ).on( 'resize', function() {
8827
8828 modalWidth = app.getUpgradeModalWidth( isVideoModal );
8829
8830 if ( modal.isOpen() ) {
8831 modal.setBoxWidth( modalWidth );
8832 }
8833
8834 } );
8835
8836 },
8837
8838 /**
8839 * Get install URL according to the UTM content and license type.
8840 *
8841 * @since 1.8.0
8842 *
8843 * @param {string} utmContent UTM content.
8844 * @param {string} type Feature license type: pro or elite.
8845 *
8846 * @returns {string} Upgrade URL.
8847 */
8848 getInstallURL: function( utmContent, type, searchKeyword = false ) { // eslint-disable-line
8849
8850 var returnUrl = charitable_builder.charitable_addons_page;
8851
8852 if ( searchKeyword ) {
8853 returnUrl = returnUrl + '&search=' + searchKeyword.replace(/[^0-9a-z+ ]/gi, '').replace('Field', '').trim();
8854 }
8855
8856 return returnUrl;
8857
8858 },
8859
8860 /**
8861 * Get upgrade URL according to the UTM content and license type.
8862 *
8863 * @since 1.8.0
8864 *
8865 * @param {string} utmContent UTM content.
8866 * @param {string} type Feature license type: pro or elite.
8867 *
8868 * @returns {string} Upgrade URL.
8869 */
8870 getUpgradeURL: function( utmContent, type ) {
8871
8872 var baseURL = charitable_builder.upgrade[ type ].url;
8873
8874 if ( utmContent.toLowerCase().indexOf( 'template' ) > -1 ) {
8875 baseURL = charitable_builder.upgrade[ type ].url_template;
8876 }
8877
8878 // Test if the base URL already contains `?`.
8879 var appendChar = /(\?)/.test( baseURL ) ? '&' : '?';
8880
8881 // If the upgrade link is changed by partners, appendChar has to be encoded.
8882 if ( baseURL.indexOf( 'https://wpcharitable.com' ) === -1 ) {
8883 appendChar = encodeURIComponent( appendChar );
8884 }
8885
8886 return baseURL + appendChar + 'utm_content=' + encodeURIComponent( utmContent.trim() );
8887 },
8888
8889 /**
8890 * Upgrade modal thank you.
8891 *
8892 * @since 1.8.0
8893 *
8894 * @param {string} type Feature license type: pro or elite.
8895 */
8896 upgradeModalThankYou: function( type ) {
8897
8898 $.alert( {
8899 title : charitable_builder.thanks_for_interest,
8900 content : charitable_builder.upgrade[type].modal,
8901 icon : 'fa fa-info-circle',
8902 type : 'blue',
8903 boxWidth : '565px',
8904 buttons : {
8905 confirm: {
8906 text : charitable_builder.ok,
8907 btnClass: 'btn-confirm',
8908 keys : [ 'enter' ]
8909 }
8910 }
8911 } );
8912
8913 },
8914
8915 /**
8916 * Activation failed message.
8917 *
8918 * @since 1.8.0.x
8919 *
8920 * @param {string} feature Addon.
8921 */
8922 upgradeModalAddonInstalledAndActivated: function( plugin_url = '', plugin_name = '', successText = '' ) { // eslint-disable-line
8923
8924 $.alert( {
8925 title : '<span class="charitable-upgrade-pro-title">' + plugin_name + ' ' + charitable_builder.installed_activated_title + '</span>',
8926 content : charitable_builder.installed_activated_reboot,
8927 icon : 'fa fa-info-circle',
8928 type : 'blue',
8929 boxWidth : '565px',
8930 buttons : {
8931 confirm: {
8932 text : charitable_builder.ok,
8933 btnClass: 'btn-confirm',
8934 keys : [ 'esc' ]
8935 },
8936 saveRefresh: {
8937 text : charitable_builder.save_refresh,
8938 btnClass: 'btn-confirm',
8939 keys : [ 'enter' ],
8940 action: function( saveRefreshButton ) {
8941
8942 // Check for campaign title first
8943 if ( $('#charitable_settings_title').val().length === 0 ) {
8944 // Close this modal
8945 this.close();
8946 // Show the title error modal
8947 app.formSaveCheck();
8948 return false;
8949 }
8950
8951 saveRefreshButton.setText( charitable_builder.standby );
8952 saveRefreshButton.disable();
8953 this.$$confirm.prop('disabled', true);
8954 this.$$confirm.hide();
8955
8956 // Refresh the current page after saving the form.
8957 app.formSave( false, false, false, true );
8958
8959 return false;
8960
8961 }
8962 }
8963 }
8964 } );
8965
8966 },
8967
8968 /**
8969 * Activation confirmation.
8970 *
8971 * @since 1.8.0
8972 *
8973 * @param {string} plugin_url Plugin activation url.
8974 * @param {string} plugin_name Plugin name.
8975 * @param {string} successText Success text.
8976 *
8977 */
8978 upgradeModalAddonActivated: function( plugin_url = '', plugin_name = '', successText = '' ) { // eslint-disable-line
8979
8980 $.alert( {
8981 title : '<span class="charitable-upgrade-pro-title">' + plugin_name + ' ' + charitable_builder.activated_title + '</span>',
8982 content : charitable_builder.activated_reboot,
8983 icon : 'fa fa-info-circle',
8984 type : 'blue',
8985 boxWidth : '565px',
8986 buttons : {
8987 confirm: {
8988 text : charitable_builder.ok,
8989 btnClass: 'btn-confirm',
8990 keys : [ 'esc' ]
8991 },
8992 saveRefresh: {
8993 text : charitable_builder.save_refresh,
8994 btnClass: 'btn-confirm',
8995 keys : [ 'enter' ],
8996 action: function( saveRefreshButton ) {
8997
8998 // Check for campaign title first
8999 if ( $('#charitable_settings_title').val().length === 0 ) {
9000 // Close this modal
9001 this.close();
9002 // Show the title error modal
9003 app.formSaveCheck();
9004 return false;
9005 }
9006
9007 saveRefreshButton.setText( charitable_builder.standby );
9008 saveRefreshButton.disable();
9009 this.$$confirm.prop('disabled', true);
9010 this.$$confirm.hide();
9011
9012 // Refresh the current page after saving the form.
9013 app.formSave( false, false, false, true );
9014
9015 return false;
9016
9017 }
9018 }
9019 }
9020 } );
9021
9022 },
9023
9024 /**
9025 * Activation confirmation.
9026 *
9027 * @since 1.8.0
9028 *
9029 * @param {string} plugin_url Plugin activation url.
9030 * @param {string} plugin_name Plugin name.
9031 * @param {string} successText Success text.
9032 */
9033 upgradeModalAddonInstalledAndActivatedFailed: function( plugin_url = '', plugin_name = '', successText = '' ) { // eslint-disable-line
9034
9035 $.alert( {
9036 title : '<span class="charitable-upgrade-pro-title">' + plugin_name + ' ' + charitable_builder.installed_activated_failed_title + '</span>',
9037 content : charitable_builder.installed_activated_failed_reboot,
9038 icon : 'fa fa-info-circle',
9039 type : 'blue',
9040 boxWidth : '565px',
9041 buttons : {
9042 confirm: {
9043 text : charitable_builder.ok,
9044 btnClass: 'btn-confirm',
9045 keys : [ 'enter' ]
9046 }
9047 }
9048 } );
9049
9050 },
9051
9052 /**
9053 * Activation failed message.
9054 *
9055 * @since 1.8.0
9056 *
9057 * @param {string} plugin_url Plugin activation url.
9058 * @param {string} plugin_name Plugin name.
9059 * @param {string} successText Success text.
9060 */
9061 upgradeModalAddonActivatedFailed: function( plugin_url = '', plugin_name = '', successText = '' ) { // eslint-disable-line
9062
9063 $.alert( {
9064 title : '<span class="charitable-upgrade-pro-title">' + plugin_name + ' ' + charitable_builder.activated_failed_title + '</span>',
9065 content : charitable_builder.activated_failed_reboot,
9066 icon : 'fa fa-info-circle',
9067 type : 'blue',
9068 boxWidth : '565px',
9069 buttons : {
9070 confirm: {
9071 text : charitable_builder.ok,
9072 btnClass: 'btn-confirm',
9073 keys : [ 'enter' ]
9074 }
9075 }
9076 } );
9077
9078 },
9079
9080 /**
9081 * Get upgrade modal width.
9082 *
9083 * @since 1.8.0
9084 *
9085 * @param {boolean} isVideoModal Upgrade modal type (with video or not).
9086 *
9087 * @returns {string} Modal width in pixels.
9088 */
9089 getUpgradeModalWidth: function( isVideoModal ) {
9090
9091 var windowWidth = $( window ).width();
9092
9093 if ( windowWidth <= 300 ) {
9094 return '250px';
9095 }
9096
9097 if ( windowWidth <= 750 ) {
9098 return '350px';
9099 }
9100
9101 if ( ! isVideoModal || windowWidth <= 1024 ) {
9102 return '560px';
9103 }
9104
9105 return windowWidth > 1070 ? '1040px' : '994px';
9106 },
9107
9108
9109 /**
9110 * Util for checking/confirming function is a function.
9111 *
9112 * @since 1.8.0
9113 */
9114 isFunction: function( functionToCheck ) {
9115
9116 return functionToCheck && {}.toString.call(functionToCheck) === '[object Function]';
9117
9118 }
9119
9120 };
9121
9122 // Provide access to public functions/properties.
9123 return app;
9124
9125 }( document, window, jQuery ) ); // eslint-disable-line
9126
9127 CharitableCampaignBuilder.init();
9128