PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.4.2
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.4.2
7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / assets / js / admin.debug.js
wordpress-popup / assets / js Last commit date
vendor 5 years ago adblock.js 5 years ago admin.debug.js 5 years ago admin.min.js 5 years ago front.debug.js 5 years ago front.min.js 5 years ago preview.debug.js 5 years ago preview.min.js 5 years ago shared-ui.min.js 5 years ago wp-dashboard.debug.js 5 years ago wp-dashboard.min.js 5 years ago
admin.debug.js
10610 lines
1 (function () {
2 'use strict';
3 /**
4 * Defines the Hustle Object
5 *
6 * @type {{define, getModules, get, modules}}
7 */
8
9 window.Hustle = function ($, doc, win) {
10 var currentModules = {},
11 _modules = {},
12 _TemplateOptions = {
13 evaluate: /<#([\s\S]+?)#>/g,
14 interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
15 escape: /\{\{([^\}]+?)\}\}(?!\})/g
16 };
17
18 var define = function define(moduleName, module) {
19 var splits = moduleName.split('.');
20
21 if (splits.length) {
22 // if module_name has more than one object name, then add the module definition recursively
23 var recursive = function recursive(incomingModuleName, modules) {
24 var arr = incomingModuleName.split('.'),
25 _moduleName = arr.splice(0, 1)[0];
26 var invoked;
27
28 if (!_moduleName) {
29 return;
30 }
31
32 if (!arr.length) {
33 invoked = module.call(null, $, doc, win);
34 modules[_moduleName] = _.isFunction(invoked) || 'undefined' === typeof invoked ? invoked : _.extend(modules[_moduleName] || {}, invoked);
35 } else {
36 modules[_moduleName] = modules[_moduleName] || {};
37 }
38
39 if (arr.length && _moduleName) {
40 recursive(arr.join('.'), modules[_moduleName]);
41 }
42 };
43
44 recursive(moduleName, _modules);
45 } else {
46 var m = _modules[moduleName] || {};
47 _modules[moduleName] = _.extend(m, module.call(null, $, doc, win));
48 }
49 },
50 get = function get(moduleName) {
51 var module, _recursive;
52
53 if (moduleName.split('.').length) {
54 // recursively fetch the module
55 module = false;
56
57 _recursive = function recursive(incomingModuleName, modules) {
58 var arr = incomingModuleName.split('.'),
59 _moduleName = arr.splice(0, 1)[0];
60 module = modules[_moduleName];
61
62 if (arr.length) {
63 _recursive(arr.join('.'), modules[_moduleName]);
64 }
65 };
66
67 _recursive(moduleName, _modules);
68
69 return module;
70 }
71
72 return _modules[moduleName] || false;
73 },
74 Events = _.extend({}, Backbone.Events),
75 View = Backbone.View.extend({
76 initialize: function initialize() {
77 if (_.isFunction(this.initMix)) {
78 this.initMix.apply(this, arguments);
79 }
80
81 if (this.render) {
82 this.render = _.wrap(this.render, function (render) {
83 this.trigger('before_render');
84 render.call(this);
85 Events.trigger('view.rendered', this);
86 this.trigger('rendered');
87 });
88 }
89
90 if (_.isFunction(this.init)) {
91 this.init.apply(this, arguments);
92 }
93 }
94 }),
95 template = _.memoize(function (id) {
96 var compiled;
97 return function (data) {
98 compiled = compiled || _.template(document.getElementById(id).innerHTML, null, _TemplateOptions);
99 return compiled(data).replace('/*<![CDATA[*/', '').replace('/*]]>*/', '');
100 };
101 }),
102 createTemplate = _.memoize(function (str) {
103 var cache;
104 return function (data) {
105 cache = cache || _.template(str, null, _TemplateOptions);
106 return cache(data);
107 };
108 }),
109 getTemplateOptions = function getTemplateOptions() {
110 return $.extend(true, {}, _TemplateOptions);
111 },
112 setModule = function setModule(moduleId, moduleView) {
113 currentModules[moduleId] = moduleView;
114 },
115 getModules = function getModules() {
116 return currentModules;
117 },
118 getModule = function getModule(moduleId) {
119 return currentModules[moduleId];
120 },
121 consts = function () {
122 return {
123 ModuleShowCount: 'hustle_module_show_count-'
124 };
125 }();
126
127 return {
128 define: define,
129 setModule: setModule,
130 getModules: getModules,
131 getModule: getModule,
132 get: get,
133 Events: Events,
134 View: View,
135 template: template,
136 createTemplate: createTemplate,
137 getTemplateOptions: getTemplateOptions,
138 consts: consts
139 };
140 }(jQuery, document, window);
141 })(jQuery);
142 var Optin = window.Optin || {};
143 Optin.Models = {};
144
145 (function ($) {
146 'use strict';
147
148 Optin.NEVER_SEE_PREFIX = 'inc_optin_never_see_again-';
149 Optin.COOKIE_PREFIX = 'inc_optin_long_hidden-';
150 Optin.POPUP_COOKIE_PREFIX = 'inc_optin_popup_long_hidden-';
151 Optin.SLIDE_IN_COOKIE_PREFIX = 'inc_optin_slide_in_long_hidden-';
152 Optin.EMBEDDED_COOKIE_PREFIX = 'inc_optin_embedded_long_hidden-';
153 Optin.template = _.memoize(function (id) {
154 var compiled;
155 var options = {
156 evaluate: /<#([\s\S]+?)#>/g,
157 interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
158 escape: /\{\{([^\}]+?)\}\}(?!\})/g
159 };
160 return function (data) {
161 compiled = compiled || _.template($('#' + id).html(), null, options);
162 return compiled(data).replace('/*<![CDATA[*/', '').replace('/*]]>*/', '');
163 };
164 });
165 /**
166 * Compatibility with other plugin/theme e.g. upfront
167 *
168 */
169
170 Optin.templateCompat = _.memoize(function (id) {
171 var compiled;
172 return function (data) {
173 compiled = compiled || _.template($('#' + id).html());
174 return compiled(data).replace('/*<![CDATA[*/', '').replace('/*]]>*/', '');
175 };
176 });
177 Optin.cookie = {
178 // Get a cookie value.
179 get: function get(name) {
180 var c;
181 var cookiesArray = document.cookie.split(';'),
182 cookiesArrayLength = cookiesArray.length,
183 cookieName = name + '=';
184
185 for (var i = 0; i < cookiesArrayLength; i += 1) {
186 c = cookiesArray[i];
187
188 while (' ' === c.charAt(0)) {
189 c = c.substring(1, c.length);
190 }
191
192 if (0 === c.indexOf(cookieName)) {
193 var _val = c.substring(cookieName.length, c.length);
194
195 return _val ? JSON.parse(_val) : _val;
196 }
197 }
198
199 return null;
200 },
201 // Saves the value into a cookie.
202 set: function set(name, value, days) {
203 var date, expires;
204 value = Array.isArray(value) || $.isPlainObject(value) ? JSON.stringify(value) : value;
205
206 if (!isNaN(days)) {
207 date = new Date();
208 date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
209 expires = '; expires=' + date.toGMTString();
210 } else {
211 expires = '';
212 }
213
214 document.cookie = name + '=' + value + expires + '; path=/';
215 }
216 };
217 Optin.Mixins = {
218 _mixins: {},
219 _servicesMixins: {},
220 _desingMixins: {},
221 _displayMixins: {},
222 add: function add(id, obj) {
223 this._mixins[id] = obj;
224 },
225 getMixins: function getMixins() {
226 return this._mixins;
227 },
228 addServicesMixin: function addServicesMixin(id, obj) {
229 this._servicesMixins[id] = obj;
230 },
231 getServicesMixins: function getServicesMixins() {
232 return this._servicesMixins;
233 }
234 };
235 })(jQuery);
236 (function ($) {
237 'use strict';
238
239 Hustle.Events.on('view.rendered', function (view) {
240 if (view instanceof Backbone.View) {
241 var accessibleHide = function accessibleHide($elements) {
242 $elements.hide();
243 $elements.prop('tabindex', '-1');
244 $elements.prop('hidden', true);
245 },
246 accessibleShow = function accessibleShow($elements) {
247 $elements.show();
248 $elements.prop('tabindex', '0');
249 $elements.removeProp('hidden');
250 }; // Init tabs (new markup)
251
252
253 SUI.tabs({
254 callback: function callback(tab, panel) {
255 // eslint-disable-line no-unused-vars
256 var wrapper = tab.closest('.sui-tabs'); // Handlers for "CTA Helper Text" options.
257
258 var ctaHelperEnable = 'cta-helper-enable',
259 ctaHelperDisable = 'cta-helper-disable';
260
261 if ('tab-' + ctaHelperEnable === tab.attr('id')) {
262 wrapper.find('#input-' + ctaHelperEnable).click();
263 } else if ('tab-' + ctaHelperDisable === tab.attr('id')) {
264 wrapper.find('#input-' + ctaHelperDisable).click();
265 } // Handlers for "Set Schedule" modal.
266
267
268 var scheduleEveryday = 'schedule-everyday',
269 scheduleSomedays = 'schedule-somedays',
270 scheduleServer = 'timezone-server',
271 scheduleCustom = 'timezone-custom';
272
273 if ('tab-' + scheduleEveryday === tab.attr('id')) {
274 wrapper.find('#input-' + scheduleEveryday).click();
275 }
276
277 if ('tab-' + scheduleSomedays === tab.attr('id')) {
278 wrapper.find('#input-' + scheduleSomedays).click();
279 }
280
281 if ('tab-' + scheduleServer === tab.attr('id')) {
282 wrapper.find('#input-' + scheduleServer).click();
283 }
284
285 if ('tab-' + scheduleCustom === tab.attr('id')) {
286 wrapper.find('#input-' + scheduleCustom).click();
287 }
288 }
289 });
290 /**
291 * Hides and shows the content of the settings using sui-side-tabs.
292 * For us, non-designers: sui-side-tabs are the "buttons" that work as labels for radio inputs.
293 *
294 * @todo TO BE REMOVED.
295 * @since 4.0.0
296 * @since 4.3.0 Handle added for tabs using buttons instead of inputs.
297 */
298
299 view.$('.sui-side-tabs').each(function () {
300 var $this = $(this); // Show or hide dependent content based on the selected settings.
301 // Only working for old tabs not using buttons but inputs.
302
303 var $inputs = $this.find('.sui-tabs-menu .sui-tab-item input');
304
305 if (!$inputs.length) {
306 return;
307 }
308
309 var handleTabs = function handleTabs() {
310 // This holds the dependency name of the selected input.
311 // It's used to avoid hiding a container that should be shown
312 // when two or more tabs share the same container.
313 var shownDep = '';
314 $.each($inputs, function () {
315 var $input = $(this),
316 $label = $input.parent('label'),
317 dependencyName = $input.data('tab-menu'),
318 $tabContent = $(".sui-tabs-content [data-tab-content=\"".concat(dependencyName, "\"]")),
319 $tabDependent = $("[data-tab-dependent=\"".concat(dependencyName, "\"]"));
320
321 if ($input[0].checked) {
322 $label.addClass('active');
323
324 if (dependencyName) {
325 shownDep = dependencyName;
326 $tabContent.addClass('active');
327 accessibleShow($tabDependent);
328 }
329 } else {
330 $label.removeClass('active');
331
332 if (dependencyName !== shownDep) {
333 $tabContent.removeClass('active');
334 accessibleHide($tabDependent);
335 }
336 }
337 });
338 }; // Do it on load.
339
340
341 handleTabs(); // And do it on change.
342
343 $inputs.on('change', function () {
344 return handleTabs();
345 }); // Probably to be handled by SUI in the future.
346 // Handle new tabs using buttons instead of radios and labels.
347
348 var $buttons = $this.find('button.sui-tab-item');
349 $buttons.on('click', function () {
350 var $button = $(this),
351 $buttonRadio = $('#' + $button.data('label-for'));
352
353 if ($buttonRadio.length) {
354 $buttonRadio.trigger('click').trigger('change');
355 }
356 }); // Make buttons selected on load.
357
358 var $selected = $this.children('.hustle-tabs-option:checked');
359
360 if ($selected.length) {
361 var id = $selected.prop('id'),
362 $button = $this.find("button[data-label-for=\"".concat(id, "\"]"));
363 $button.trigger('click');
364 }
365 }); // Same as the one above but for the tabs markup in the new component files.
366
367 view.$('.sui-side-tabs').each(function () {
368 var $sideTabsContainer = $(this); // Show or hide dependent content based on the selected settings.
369
370 var $inputs = $sideTabsContainer.children('.hustle-tabs-option');
371
372 if (!$inputs.length) {
373 return;
374 }
375
376 var handleTabs = function handleTabs() {
377 // Used to avoid hiding a container that should be shown
378 // when two or more tabs share the same container.
379 var shownDep = '';
380 $.each($inputs, function () {
381 var $input = $(this),
382 inputId = $input.attr('id'),
383 $button = $sideTabsContainer.find("button[data-label-for=\"".concat(inputId, "\"]")),
384 dependencyId = $button.attr('aria-controls'),
385 //// Use this instead in 4.4 with new multiple-triggers in place. $tabDependent = $( `#${ dependencyId }` );
386 $tabDependent = $("div[id=\"".concat(dependencyId, "\"]"));
387
388 if ($input[0].checked) {
389 $button.addClass('active');
390
391 if (dependencyId) {
392 shownDep = dependencyId;
393 accessibleShow($tabDependent);
394 }
395 } else {
396 $button.removeClass('active');
397
398 if (dependencyId !== shownDep) {
399 accessibleHide($tabDependent);
400 }
401 }
402 });
403 }; // Do it on load.
404
405
406 handleTabs(); // And do it on change.
407
408 $inputs.on('change', function () {
409 return handleTabs();
410 });
411 var $buttons = $sideTabsContainer.children('.sui-tabs-menu').find('button.sui-tab-item');
412 $buttons.on('click', function () {
413 var $button = $(this),
414 // Use this instead in 4.4 with new multiple-triggers in place. $( '#' + $button.data( 'label-for' ) );
415 $buttonRadio = $('input[id="' + $button.data('label-for') + '"]');
416
417 if ($buttonRadio.length) {
418 $buttonRadio.trigger('click').trigger('change');
419 }
420 });
421 });
422 view.$('.select-content-switcher-wrapper').each(function () {
423 var $this = $(this),
424 $select = $this.find('.select-content-switcher'),
425 $options = $select.find('option'),
426 switchContent = function switchContent() {
427 var $selected = $select.find(':selected'),
428 dependencyName = $selected.data('switcher-menu'),
429 $selectedTabContent = $this.find(".select-switcher-content[data-switcher-content=\"".concat(dependencyName, "\"]"));
430 $.each($options, function () {
431 var $option = $(this);
432
433 if ($option.data('switcher-menu') === dependencyName) {
434 accessibleShow($selectedTabContent);
435 } else {
436 var $tabContent = $this.find(".select-switcher-content[data-switcher-content=\"".concat($option.data('switcher-menu'), "\"]"));
437 accessibleHide($tabContent);
438 }
439 });
440 }; // Do it on load.
441
442
443 switchContent(); // And do it on change.
444
445 $select.on('change', function () {
446 return switchContent();
447 });
448 });
449 /**
450 * Hides and shows the container dependent on toggles
451 * on view load and on change.
452 * Used in wizards and global settings page.
453 *
454 * @since 4.0.3
455 */
456
457 view.$('.hustle-toggle-with-container').each(function () {
458 var $this = $(this),
459 $checkbox = $this.find('input[type=checkbox]'),
460 $containersOn = $("[data-toggle-content=\"".concat($this.data('toggle-on'), "\"]")),
461 $containersOff = $("[data-toggle-content=\"".concat($this.data('toggle-off'), "\"]")),
462 doToggle = function doToggle() {
463 if ($checkbox[0].checked) {
464 Module.Utils.accessibleShow($containersOn);
465 Module.Utils.accessibleHide($containersOff);
466 } else {
467 Module.Utils.accessibleShow($containersOff);
468 Module.Utils.accessibleHide($containersOn);
469 }
470 }; // Do it on load.
471
472
473 doToggle(); // And do it on change.
474
475 $checkbox.on('change', doToggle);
476 });
477 /**
478 * Toggles the 'disabled' property from the field dependent to a radio
479 * based on the selected value.
480 * Used in wizards.
481 *
482 * @since 4.3.0
483 */
484
485 view.$('.hustle-radio-with-dependency-to-disable').each(function () {
486 var $radio = $(this),
487 relationName = $radio.data('disable'),
488 $dependentField = $("[data-disable-content=\"".concat(relationName, "\"]")),
489 disableOff = $dependentField.data('disable-off'),
490 disableOn = $dependentField.data('disable-on');
491
492 var toggleDisabled = function toggleDisabled() {
493 if (!$radio.is(':checked')) {
494 return;
495 }
496
497 if (disableOff) {
498 if (disableOff === $radio.val()) {
499 $dependentField.prop('disabled', false);
500 } else {
501 $dependentField.prop('disabled', true);
502 }
503
504 return;
505 }
506
507 if (disableOn) {
508 if (disableOn === $radio.val()) {
509 $dependentField.prop('disabled', true);
510 } else {
511 $dependentField.prop('disabled', false);
512 }
513 }
514 };
515
516 toggleDisabled();
517 $radio.on('change', toggleDisabled);
518 });
519 /**
520 * Toggles the 'disabled' property from the field dependent to a select
521 * based on the selected value.
522 * Used in wizards.
523 *
524 * @since 4.3.0
525 */
526
527 view.$('.hustle-select-with-dependency-to-disable').each(function () {
528 var $select = $(this),
529 relationName = $select.data('disable'),
530 $dependentField = $("[data-disable-content=\"".concat(relationName, "\"]")),
531 disableOff = $dependentField.data('disable-off'),
532 disableOn = $dependentField.data('disable-on');
533
534 var toggleDisabled = function toggleDisabled() {
535 if (disableOff) {
536 if (disableOff === $select.val()) {
537 $dependentField.prop('disabled', false);
538 } else {
539 $dependentField.prop('disabled', true);
540 }
541
542 return;
543 }
544
545 if (disableOn) {
546 if (disableOn === $select.val()) {
547 $dependentField.prop('disabled', true);
548 } else {
549 $dependentField.prop('disabled', false);
550 }
551 }
552 };
553
554 toggleDisabled();
555 $select.on('change', toggleDisabled);
556 });
557 Module.Utils.showHideDependencyOnSelectChange(view.$el);
558 }
559 }); // TODO: probably move this to the view where it's actually used.
560
561 $(document).ready(function () {
562 // Delete module
563 $('.hustle-delete-module-button').on('click', function (e) {
564 e.preventDefault();
565 var $this = $(e.currentTarget),
566 data = {
567 id: $this.data('id'),
568 nonce: $this.data('nonce'),
569 action: 'delete',
570 title: $this.data('title'),
571 description: $this.data('description'),
572 actionClass: 'hustle-single-module-button-action'
573 };
574 Module.deleteModal.open(data, $this[0]);
575 });
576 /**
577 * The "are you sure?" modal from before resetting the tracking data of modules.
578 *
579 * @since 4.0.0
580 * @param {Event} e Event.
581 */
582
583 $('.hustle-module-tracking-reset-button').on('click', function (e) {
584 e.preventDefault();
585 var $this = $(e.target),
586 data = {
587 id: $this.data('module-id'),
588 nonce: optinVars.single_module_action_nonce,
589 action: 'reset-tracking',
590 title: $this.data('title'),
591 description: $this.data('description'),
592 actionClass: 'hustle-single-module-button-action'
593 };
594 Module.deleteModal.open(data, $this[0]);
595 }); // Makes the 'copy' button work.
596
597 $('.hustle-copy-shortcode-button').on('click', function (e) {
598 e.preventDefault();
599 var $button = $(e.target),
600 shortcode = $button.data('shortcode'),
601 $inputWrapper = $button.closest('.sui-with-button-inside');
602
603 if ('undefined' !== typeof shortcode) {
604 // Actions in listing pages.
605 var $temp = $('<input />');
606 $('body').append($temp);
607 $temp.val(shortcode).trigger('select');
608 document.execCommand('copy');
609 $temp.remove();
610 Module.Notification.open('success', optinVars.messages.shortcode_copied);
611 } else if ($inputWrapper.length) {
612 // Copy shortcode in wizard pages.
613 var $inputWithCopy = $inputWrapper.find('input[type="text"]');
614 $inputWithCopy.trigger('select');
615 document.execCommand('copy');
616 }
617 }); // Dismiss for all the notices using the template from Hustle_Notifications::show_notice().
618
619 $('.hustle-dismissible-admin-notice .notice-dismiss, .hustle-dismissible-admin-notice .dismiss-notice').on('click', function (e) {
620 e.preventDefault();
621 var $container = $(e.currentTarget).closest('.hustle-dismissible-admin-notice');
622 $.post(ajaxurl, {
623 action: 'hustle_dismiss_notification',
624 name: $container.data('name'),
625 _ajax_nonce: optinVars.dismiss_notice_nonce
626 }).always($container.fadeOut());
627 }); // Opens the confirmation modal for dismissing the tracking migration notice.
628
629 $('#hustle-tracking-migration-notice .hustle-notice-dismiss').on('click', function (e) {
630 e.preventDefault();
631 $('#hustle-dismiss-modal-button').on('click', function (ev) {
632 ev.preventDefault();
633 $.post(ajaxurl, {
634 action: 'hustle_dismiss_notification',
635 name: $(ev.currentTarget).data('name'),
636 _ajax_nonce: $(ev.currentTarget).data('nonce')
637 }).always(function () {
638 return location.reload();
639 });
640 });
641 SUI.openModal('hustle-dialog--migrate-dismiss-confirmation', $('.sui-header-title'));
642 });
643
644 if ($('.sui-form-field input[type=number]').length) {
645 $('.sui-form-field input[type=number]').on('keydown', function (e) {
646 if ($(this)[0].hasAttribute('min') && 0 <= $(this).attr('min')) {
647 var char = e.originalEvent.key.replace(/[^0-9^.^,]/, '');
648
649 if (0 === char.length && !(e.originalEvent.ctrlKey || e.originalEvent.metaKey)) {
650 e.preventDefault();
651 }
652 }
653 });
654 }
655
656 setTimeout(function () {
657 if ($('.hustle-scroll-to').length) {
658 $('html, body').animate({
659 scrollTop: $('.hustle-scroll-to').offset().top
660 }, 'slow');
661 }
662 }, 100); //table checkboxes
663
664 $('.hustle-check-all').on('click', function (e) {
665 var $this = $(e.target),
666 $list = $this.parents('.sui-wrap-hustle').find('.hustle-list'),
667 allChecked = $this.is(':checked');
668 $list.find('.hustle-listing-checkbox').prop('checked', allChecked);
669 $this.parents('.sui-wrap-hustle').find('.hustle-check-all').prop('checked', allChecked);
670 $('.hustle-bulk-apply-button').prop('disabled', !allChecked);
671 });
672 $('.hustle-list .hustle-listing-checkbox').on('click', function (e) {
673 var $this = $(e.target),
674 $list = $this.parents('.sui-wrap-hustle').find('.hustle-list'),
675 allChecked = $this.is(':checked') && !$list.find('.hustle-listing-checkbox:not(:checked)').length,
676 count = $list.find('.hustle-listing-checkbox:checked').length,
677 disabled = 0 === count;
678 $('.hustle-check-all').prop('checked', allChecked);
679 $('.hustle-bulk-apply-button').prop('disabled', disabled);
680 });
681 $('.hustle-bulk-apply-button').on('click', function (e) {
682 var $this = $(e.target),
683 value = $('select option:selected', $this.closest('.hui-bulk-actions')).val(),
684 elements = $('.hustle-list .hustle-listing-checkbox:checked');
685
686 if (0 === elements.length || 'undefined' === value) {
687 return false;
688 }
689
690 var ids = [];
691 $.each(elements, function () {
692 ids.push($(this).val());
693 });
694
695 if ('delete-all' === value) {
696 var data = {
697 ids: ids.join(','),
698 nonce: $this.siblings('input[name="hustle_nonce"]').val(),
699 title: $this.data('title'),
700 description: $this.data('description'),
701 action: value
702 };
703 Module.deleteModal.open(data, $this[0]);
704 return false;
705 }
706 });
707 });
708 })(jQuery);
709 Hustle.define('Modals.Migration', function ($) {
710 'use strict';
711
712 var migrationModalView = Backbone.View.extend({
713 el: '#hustle-dialog--migrate',
714 data: {},
715 events: {
716 'click #hustle-migrate-start': 'migrateStart',
717 'click #hustle-create-new-module': 'createModule',
718 'click .sui-box-selector': 'enableContinue',
719 'click .hustle-dialog-migrate-skip': 'dismissModal'
720 },
721 initialize: function initialize() {
722 if (!this.$el.length) {
723 return;
724 }
725
726 var currentSlide = '',
727 focusOnOpen = '';
728
729 if (0 === this.$el.data('isFirst')) {
730 currentSlide = '#hustle-dialog--migrate-slide-2';
731 focusOnOpen = 'hustle-migrate-start';
732 } else {
733 currentSlide = '#hustle-dialog--migrate-slide-1';
734 focusOnOpen = 'hustle-migrate-get-started';
735 }
736
737 this.$(currentSlide).addClass('sui-active sui-loaded');
738 setTimeout(function () {
739 return SUI.openModal('hustle-dialog--migrate', focusOnOpen, $('.sui-wrap-hustle')[0], false);
740 }, 100);
741 this.$progressBar = this.$el.find('.sui-progress .sui-progress-bar span');
742 this.$progressText = this.$el.find('.sui-progress .sui-progress-text span');
743 this.$partialRows = this.$el.find('#hustle-partial-rows');
744 },
745 migrateStart: function migrateStart(e) {
746 var me = this;
747 var button = $(e.target);
748 var $container = this.$el,
749 $dialog = $container.find('#hustle-dialog--migrate-slide-2'),
750 $description = $dialog.find('#hustle-dialog--migrate-slide-2-description'); // On load button
751
752 button.addClass('sui-button-onload'); // Remove skip migration link
753
754 $dialog.find('.hustle-dialog-migrate-skip').remove();
755 $description.text($description.data('migrate-text'));
756 Module.Utils.accessibleHide($dialog.find('div[data-migrate-start]'));
757 Module.Utils.accessibleHide($dialog.find('div[data-migrate-failed]'));
758 Module.Utils.accessibleShow($dialog.find('div[data-migrate-progress]'));
759 SUI.closeNotice('hustle-dialog--migrate-error-notice');
760 me.migrateTracking(e);
761 button.removeClass('sui-button-onload');
762 e.preventDefault();
763 },
764 migrateComplete: function migrateComplete() {
765 var slide = this.$('#hustle-dialog--migrate-slide-2'),
766 self = this;
767 var title = slide.find('#hustle-dialog--migrate-slide-2-title');
768 var description = slide.find('#hustle-dialog--migrate-slide-2-description');
769 this.$el.find('sui-button-onload').removeClass('sui-button-onload');
770 title.text(title.data('done-text'));
771 description.text(description.data('done-text'));
772 Module.Utils.accessibleHide(slide.find('div[data-migrate-progress]'));
773 Module.Utils.accessibleShow(slide.find('div[data-migrate-done]'));
774 this.$el.closest('.sui-modal').on('click', function (e) {
775 return self.closeDialog(e);
776 });
777 },
778 migrateFailed: function migrateFailed() {
779 var slide = this.$el.find('#hustle-dialog--migrate-slide-2'),
780 description = slide.find('#dialogDescription');
781 description.text('');
782 Module.Utils.accessibleHide(slide.find('div[data-migrate-start]'));
783 Module.Utils.accessibleShow(slide.find('div[data-migrate-failed]'));
784 Module.Utils.accessibleHide(slide.find('div[data-migrate-progress]'));
785 var noticeId = 'hustle-dialog--migrate-error-notice',
786 $notice = $('#' + noticeId),
787 message = $notice.data('message');
788 Module.Notification.open('error', message, false, noticeId, false);
789 },
790 updateProgress: function updateProgress(migratedRows, rowsPercentage, totalRows) {
791 if ('undefined' === typeof this.totalRows) {
792 this.totalRows = totalRows;
793 this.$el.find('#hustle-total-rows').text(totalRows);
794 }
795
796 this.$partialRows.text(migratedRows);
797 var width = rowsPercentage + '%';
798 this.$progressBar.css('width', width);
799
800 if (100 >= rowsPercentage) {
801 this.$progressText.text(rowsPercentage + '%');
802 }
803 },
804 migrateTracking: function migrateTracking(e) {
805 e.preventDefault();
806 var self = this,
807 $button = $(e.currentTarget),
808 nonce = $button.data('nonce'),
809 data = {
810 action: 'hustle_migrate_tracking',
811 _ajax_nonce: nonce
812 };
813 $.ajax({
814 type: 'POST',
815 url: ajaxurl,
816 dataType: 'json',
817 data: data,
818 success: function success(res) {
819 if (res.success) {
820 var migratedRows = res.data.migrated_rows,
821 migratedPercentage = res.data.migrated_percentage,
822 totalRows = res.data.total_entries || '0';
823
824 if ('done' !== res.data.current_meta) {
825 self.updateProgress(migratedRows, migratedPercentage, totalRows);
826 self.migrateTracking(e);
827 } else {
828 self.updateProgress(migratedRows, migratedPercentage, totalRows); // Set a small delay so the users can see the progress update in front before moving
829 // forward and they don't think some rows were not migrated.
830
831 setTimeout(function () {
832 return self.migrateComplete();
833 }, 500);
834 }
835 } else {
836 self.migrateFailed();
837 }
838 },
839 error: function error() {
840 self.migrateFailed();
841 }
842 });
843 return false;
844 },
845 createModule: function createModule(e) {
846 var button = $(e.target),
847 $selection = this.$el.find('.sui-box-selector input:checked');
848
849 if ($selection.length) {
850 this.dismissModal();
851 button.addClass('sui-button-onload');
852 var moduleType = $selection.val(),
853 page = 'undefined' !== typeof optinVars.module_page[moduleType] ? optinVars.module_page[moduleType] : optinVars.module_page.popup;
854 window.location = "?page=".concat(page, "&create-module=true");
855 } else {// Show an error message or something?
856 }
857
858 e.preventDefault();
859 },
860 closeDialog: function closeDialog(e) {
861 SUI.closeModal();
862 e.preventDefault();
863 e.stopPropagation();
864 },
865 enableContinue: function enableContinue() {
866 this.$el.find('#hustle-create-new-module').prop('disabled', false);
867 },
868 dismissModal: function dismissModal(e) {
869 if (e) {
870 e.preventDefault();
871 }
872
873 $.post(ajaxurl, {
874 action: 'hustle_dismiss_notification',
875 name: 'migrate_modal',
876 _ajax_nonce: this.$el.data('nonce')
877 });
878 }
879 });
880 new migrationModalView();
881 });
882 Hustle.define('Modals.Preview', function ($) {
883 'use strict';
884
885 return Backbone.View.extend({
886 el: '#hustle-dialog--preview',
887 iframeWindow: null,
888 events: {
889 'click .hustle-modal-close': 'close',
890 'click .hustle-preview-device-button': 'previewDeviceSelected',
891 'click #hustle-preview-reload-module-button': 'reloadModuleClicked'
892 },
893 open: function open(moduleId, moduleType, $button) {
894 var previewData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
895 this.displayModuleName(previewData);
896 this.maybeHideReloadButton(moduleType);
897 SUI.openModal('hustle-dialog--preview', $button, null, false, false);
898 var data = {
899 action: 'open',
900 moduleId: moduleId,
901 moduleType: moduleType,
902 previewData: previewData
903 };
904 this.initiateIframe(data);
905 },
906 displayModuleName: function displayModuleName(previewData) {
907 if (previewData) {
908 this.$('#hustle-dialog--preview-description').html(previewData.module_name || '');
909 }
910 },
911 maybeHideReloadButton: function maybeHideReloadButton(moduleType) {
912 var $reloadButton = this.$('#hustle-preview-reload-module-button');
913
914 if ('embedded' === moduleType) {
915 $reloadButton.addClass('sui-hidden-important');
916 } else {
917 $reloadButton.removeClass('sui-hidden-important');
918 }
919 },
920 initiateIframe: function initiateIframe(data) {
921 var _this = this;
922
923 var $iframe = this.$('#hustle-preview-iframe'); // Load the iframe the first time it's opened only.
924
925 if ('undefined' === typeof $iframe.attr('src')) {
926 $iframe[0].src = $iframe.data('src');
927 $iframe.on('load', function () {
928 var $previewContainer = _this.$('#hustle-preview-iframe-container'); // Remove the "loading" container after the iframe's page loads and show the iframe.
929
930
931 $previewContainer.show();
932 $previewContainer.prop('aria-hidden', false);
933
934 _this.$('#hustle-preview-loader').remove();
935
936 _this.iframeWindow = $iframe[0].contentWindow;
937
938 _this.talkToIframe(data);
939 });
940 } else {
941 // Prevent the "finished loading" message from being read each time the modal is opened.
942 this.$('#hustle-sr-text-preview-loaded').remove();
943 this.talkToIframe(data);
944 }
945 },
946 close: function close() {
947 var _this2 = this;
948
949 // Delay this a bit so the modal is closed before emptying the preview containers.
950 setTimeout(function () {
951 return _this2.talkToIframe({
952 action: 'close'
953 });
954 }, 500);
955 },
956 reloadModuleClicked: function reloadModuleClicked() {
957 this.talkToIframe({
958 action: 'reload'
959 });
960 },
961 previewDeviceSelected: function previewDeviceSelected(e) {
962 var $button = $(e.currentTarget),
963 device = $button.data('device');
964 this.$('.hustle-preview-device-button').removeClass('sui-active');
965 $button.addClass('sui-active');
966 this.$('#hustle-sr-text-preview-selected-device').html($button.data('selected'));
967
968 if ('desktop' === device) {
969 this.$el.removeClass('hustle-preview-mobile');
970 this.$el.addClass('hustle-preview-desktop');
971 } else if ('mobile' === device) {
972 this.$el.removeClass('hustle-preview-desktop');
973 this.$el.addClass('hustle-preview-mobile');
974 }
975 },
976 talkToIframe: function talkToIframe(message) {
977 // Avoid sending messages if the iframe isn't initialized.
978 if (this.iframeWindow) {
979 this.iframeWindow.postMessage(message, window.location);
980 }
981 }
982 });
983 });
984 Hustle.define('Modals.ReleaseHighlight', function ($) {
985 'use strict';
986
987 var welcomeModalView = Backbone.View.extend({
988 el: '#hustle-dialog--release-highlight',
989 initialize: function initialize() {
990 var _this = this;
991
992 if (!this.$el.length) {
993 return;
994 }
995
996 setTimeout(function () {
997 return _this.show();
998 }, 100);
999 this.$el.on('close', function () {
1000 return _this.dismissModal();
1001 });
1002 },
1003 show: function show() {
1004 var _this2 = this;
1005
1006 if ('undefined' === typeof SUI) {
1007 setTimeout(function () {
1008 return _this2.show();
1009 }, 100);
1010 return;
1011 }
1012
1013 SUI.openModal('hustle-dialog--release-highlight', $('.sui-header-title')[0], this.$('.hustle-modal-close'), true);
1014 },
1015 dismissModal: function dismissModal() {
1016 $.post(ajaxurl, {
1017 action: 'hustle_dismiss_notification',
1018 name: this.$el.data('name'),
1019 _ajax_nonce: optinVars.dismiss_notice_nonce
1020 });
1021 }
1022 });
1023 new welcomeModalView();
1024 });
1025 Hustle.define('Modals.ReviewConditions', function ($) {
1026 'use strict';
1027
1028 var ReviewConditionsModalView = Backbone.View.extend({
1029 el: '#hustle-dialog--review_conditions',
1030 events: {
1031 'click .hustle-review-conditions-dismiss': 'dismissModal'
1032 },
1033 initialize: function initialize() {
1034 if (!this.$el.length) {
1035 return;
1036 }
1037
1038 setTimeout(this.show, 100, this);
1039 },
1040 show: function show(reviewConditions) {
1041 if ('undefined' === typeof SUI || 'undefined' === typeof SUI.openModal) {
1042 setTimeout(reviewConditions.show, 100, reviewConditions);
1043 return;
1044 }
1045
1046 SUI.openModal('hustle-dialog--review_conditions', $('.sui-header-title'));
1047 },
1048 dismissModal: function dismissModal() {
1049 $.post(ajaxurl, {
1050 action: 'hustle_dismiss_notification',
1051 name: '41_visibility_behavior_update',
1052 _ajax_nonce: this.$el.data('nonce')
1053 });
1054 }
1055 });
1056 new ReviewConditionsModalView();
1057 });
1058 Hustle.define('Upgrade_Modal', function () {
1059 'use strict';
1060
1061 return Backbone.View.extend({
1062 el: '#wph-upgrade-modal',
1063 opts: {},
1064 events: {
1065 'click .wpmudev-i_close': 'close'
1066 },
1067 initialize: function initialize(options) {
1068 this.opts = _.extend({}, this.opts, options);
1069 },
1070 close: function close(e) {
1071 e.preventDefault();
1072 e.stopPropagation();
1073 this.$el.removeClass('wpmudev-modal-active');
1074 }
1075 });
1076 });
1077 Hustle.define('Modals.Welcome', function ($) {
1078 'use strict';
1079
1080 var welcomeModalView = Backbone.View.extend({
1081 el: '#hustle-dialog--welcome',
1082 events: {
1083 'click #hustle-new-create-module': 'createModule',
1084 'click .sui-box-selector': 'enableContinue',
1085 'click #getStarted': 'dismissModal',
1086 'click .sui-modal-skip': 'dismissModal',
1087 'click .hustle-button-dismiss-welcome': 'dismissModal'
1088 },
1089 initialize: function initialize() {
1090 if (!this.$el.length) {
1091 return;
1092 }
1093
1094 setTimeout(this.show, 100, this);
1095 },
1096 show: function show(self) {
1097 if ('undefined' === typeof SUI) {
1098 setTimeout(self.show, 100, self);
1099 return;
1100 }
1101
1102 SUI.openModal('hustle-dialog--welcome', $('.sui-header-title')[0], self.$('#hustle-dialog--welcome-first .sui-button-icon.hustle-button-dismiss-welcome'), true);
1103 SUI.slideModal('hustle-dialog--welcome-first');
1104 },
1105 createModule: function createModule(e) {
1106 var button = $(e.target),
1107 $selection = this.$el.find('.sui-box-selector input:checked');
1108
1109 if ($selection.length) {
1110 button.addClass('sui-button-onload');
1111 var moduleType = $selection.val(),
1112 page = 'undefined' !== typeof optinVars.module_page[moduleType] ? optinVars.module_page[moduleType] : optinVars.module_page.popup;
1113 window.location = "?page=".concat(page, "&create-module=true");
1114 }
1115
1116 e.preventDefault();
1117 },
1118 enableContinue: function enableContinue() {
1119 this.$el.find('#hustle-new-create-module').prop('disabled', false);
1120 },
1121 dismissModal: function dismissModal(e) {
1122 if (e) {
1123 e.preventDefault();
1124 }
1125
1126 $.post(ajaxurl, {
1127 action: 'hustle_dismiss_notification',
1128 name: 'welcome_modal',
1129 _ajax_nonce: this.$el.data('nonce')
1130 });
1131 }
1132 });
1133 new welcomeModalView();
1134 });
1135 (function ($) {
1136 'use strict';
1137
1138 Optin.View = {};
1139 Optin.View.Conditions = Optin.View.Conditions || {};
1140 var ConditionBase = Hustle.View.extend({
1141 conditionId: '',
1142 className: 'sui-builder-field sui-accordion-item sui-accordion-item--open',
1143 _template: Optin.template('hustle-visibility-rule-tpl'),
1144 template: false,
1145 _defaults: {
1146 typeName: '',
1147 conditionName: ''
1148 },
1149 _events: {
1150 'change input': 'changeInput',
1151 'change textarea': 'changeInput',
1152 'change select': 'changeInput'
1153 },
1154 init: function init(opts) {
1155 this.undelegateEvents();
1156 this.$el.removeData().off();
1157 this.type = opts.type;
1158 this.groupId = opts.groupId;
1159 this.filter_type = opts.filter_type; // eslint-disable-line camelcase
1160
1161 this.id = this.conditionId;
1162 this.template = 'undefined' !== typeof this.cpt ? Optin.template('hustle-visibility-rule-tpl--post_type') : Optin.template('hustle-visibility-rule-tpl--' + this.conditionId);
1163 /**
1164 * Defines typeName and conditionName based on type and id so that it can be used in the template later on
1165 *
1166 * @type {Object}
1167 * @private
1168 */
1169
1170 this._defaults = {
1171 typeName: optinVars.module_type_name,
1172 conditionName: optinVars.messages.conditions[this.conditionId] ? optinVars.messages.conditions[this.conditionId] : this.conditionId,
1173 groupId: this.groupId,
1174 id: this.conditionId,
1175 source: opts.source
1176 };
1177 this.data = this.getData();
1178 this.render();
1179 this.events = $.extend(true, {}, this.events, this._events);
1180 this.delegateEvents();
1181
1182 if (this.onInit && _.isFunction(this.onInit)) {
1183 this.onInit.apply(this, arguments);
1184 }
1185
1186 return this;
1187 },
1188 getData: function getData() {
1189 return _.extend({}, this._defaults, this.defaults(), this.model.get(this.conditionId), {
1190 type: this.type
1191 });
1192 },
1193 getTitle: function getTitle() {
1194 return this.title.replace('{type_name}', this.data.typeName);
1195 },
1196 getBody: function getBody() {
1197 return 'function' === typeof this.body ? this.body.apply(this, arguments) : this.body.replace('{type_name}', this.data.typeName);
1198 },
1199 getHeader: function getHeader() {
1200 return this.header;
1201 },
1202 countLines: function countLines(value) {
1203 // trim trailing return char if exists
1204 var text = value.replace(/\s+$/g, '');
1205 var split = text.split('\n');
1206 return split.length;
1207 },
1208 render: function render() {
1209 this.setProperties();
1210
1211 var html = this._template(_.extend({}, {
1212 title: this.getTitle(),
1213 body: this.getBody(),
1214 header: this.getHeader()
1215 }, this._defaults, {
1216 type: this.type
1217 }));
1218
1219 this.$el.html('');
1220 this.$el.html(html);
1221 $('.wph-conditions--box .wph-conditions--item:not(:last-child)').removeClass('wph-conditions--open').addClass('wph-conditions--closed');
1222 $('.wph-conditions--box .wph-conditions--item:not(:last-child) section').hide();
1223
1224 if (this.rendered && 'function' === typeof this.rendered) {
1225 this.rendered.apply(this, arguments);
1226 }
1227
1228 return this;
1229 },
1230
1231 /**
1232 * Updates attribute value into the condition hash
1233 *
1234 * @param {string} attribute Name of the attribute to update.
1235 * @param {*} val New value of the attribute.
1236 */
1237 updateAttribute: function updateAttribute(attribute, val) {
1238 this.data = this.model.get(this.conditionId);
1239 this.data[attribute] = val;
1240 this.model.set(this.conditionId, this.data); // TODO: instead of triggering manually, clone the retrieved object so
1241 // backbone recognizes the change.
1242
1243 this.model.trigger('change');
1244 },
1245 getAttribute: function getAttribute(attribute) {
1246 var data = this.model.get(this.conditionId);
1247 return data && data[attribute] ? data[attribute] : false;
1248 },
1249 refreshLabel: function refreshLabel() {
1250 var html = this.getHeader();
1251 this.$el.find('.wph-condition--preview').html('');
1252 this.$el.find('.sui-accordion-item-header .sui-tag').html(html);
1253 },
1254
1255 /**
1256 * Triggered on input change
1257 *
1258 * @param {event} e Event.
1259 */
1260 changeInput: function changeInput(e) {
1261 var el = e.target,
1262 $el = $(el);
1263 var val = $el.is('.sui-select') ? $el.val() : e.target.value; //stop handler in /assets/js/admin/mixins/model-updater.js
1264
1265 e.stopImmediatePropagation();
1266
1267 if ($el.is(':checkbox')) {
1268 val = $el.is(':checked');
1269 } // skip for input search
1270
1271
1272 if ($el.is('.select2-search__field')) {
1273 return false;
1274 }
1275
1276 var attribute = el.getAttribute('data-attribute');
1277 this.updateAttribute(attribute, val);
1278 this.refreshLabel();
1279 },
1280
1281 /**
1282 * Returns configs of condition
1283 *
1284 * @return {Object|boolean} The configs value, or true if not set.
1285 */
1286 getConfigs: function getConfigs() {
1287 return this.defaults() || true;
1288 }
1289 });
1290
1291 var reenableScroll = function reenableScroll() {
1292 /**
1293 * reenable scrolling for the container
1294 * select2 disables scrolling after select so we reenable it
1295 */
1296 $('.wph-conditions--items').data('select2ScrollPosition', {});
1297 },
1298 ToggleButtonTogglerMixin = {
1299 events: {
1300 'change input[type="radio"]': 'setCurrentLi'
1301 },
1302 setCurrentLi: function setCurrentLi(e) {
1303 var $this = $(e.target),
1304 $li = $this.closest('li');
1305 $li.siblings().removeClass('current');
1306 $li.toggleClass('current', $this.is(':checked'));
1307 }
1308 };
1309 /**
1310 * Posts
1311 */
1312
1313
1314 Optin.View.Conditions.posts = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
1315 conditionId: 'posts',
1316 setProperties: function setProperties() {
1317 this.title = optinVars.messages.conditions.posts;
1318 },
1319 defaults: function defaults() {
1320 return {
1321 filter_type: 'except',
1322 // except | only
1323 posts: []
1324 };
1325 },
1326 onInit: function onInit() {//this.listenTo( this.model, 'change', this.render );
1327 },
1328 getHeader: function getHeader() {
1329 if (this.getAttribute('posts').length) {
1330 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('posts').length);
1331 }
1332
1333 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1334 },
1335 body: function body() {
1336 return this.template(this.getData());
1337 },
1338 rendered: function rendered() {
1339 this.$('.hustle-select-ajax').SUIselect2({
1340 tags: 'true',
1341 width: '100%',
1342 dropdownCssClass: 'sui-select-dropdown',
1343 ajax: {
1344 url: ajaxurl,
1345 delay: 250,
1346 // wait 250 milliseconds before triggering the request
1347 dataType: 'json',
1348 type: 'POST',
1349 data: function data(params) {
1350 var query = {
1351 action: 'get_new_condition_ids',
1352 search: params.term,
1353 postType: 'post'
1354 };
1355 return query;
1356 },
1357 processResults: function processResults(data) {
1358 return {
1359 results: data.data
1360 };
1361 },
1362 cache: true
1363 },
1364 createTag: function createTag() {
1365 return false;
1366 }
1367 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1368 }
1369 }));
1370 /**
1371 * Pages
1372 */
1373
1374 Optin.View.Conditions.pages = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
1375 conditionId: 'pages',
1376 setProperties: function setProperties() {
1377 this.title = optinVars.messages.conditions.pages;
1378 },
1379 defaults: function defaults() {
1380 return {
1381 filter_type: 'except',
1382 // except | only
1383 pages: []
1384 };
1385 },
1386 onInit: function onInit() {//this.listenTo( this.model, 'change', this.render );
1387 },
1388 getHeader: function getHeader() {
1389 if (this.getAttribute('pages').length) {
1390 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('pages').length);
1391 }
1392
1393 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1394 },
1395 body: function body() {
1396 return this.template(this.getData());
1397 },
1398 rendered: function rendered() {
1399 this.$('.hustle-select-ajax').SUIselect2({
1400 tags: 'true',
1401 width: '100%',
1402 dropdownCssClass: 'sui-select-dropdown',
1403 ajax: {
1404 url: ajaxurl,
1405 delay: 250,
1406 // wait 250 milliseconds before triggering the request
1407 dataType: 'json',
1408 type: 'POST',
1409 data: function data(params) {
1410 var query = {
1411 action: 'get_new_condition_ids',
1412 search: params.term,
1413 postType: 'page'
1414 };
1415 return query;
1416 },
1417 processResults: function processResults(data) {
1418 return {
1419 results: data.data
1420 };
1421 },
1422 cache: true
1423 },
1424 createTag: function createTag() {
1425 return false;
1426 }
1427 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1428 }
1429 }));
1430 /**
1431 * Custom Post Types
1432 */
1433
1434 if (optinVars.post_types) {
1435 _.each(optinVars.post_types, function (cptDetails, cpt) {
1436 Optin.View.Conditions[cptDetails.name] = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
1437 conditionId: cptDetails.name,
1438 cpt: true,
1439 setProperties: function setProperties() {
1440 this.title = cptDetails.label;
1441 },
1442 defaults: function defaults() {
1443 return {
1444 filter_type: 'except',
1445 // except | only
1446 selected_cpts: [],
1447 postType: cpt,
1448 postTypeLabel: cptDetails.label
1449 };
1450 },
1451 getHeader: function getHeader() {
1452 if (this.getAttribute('selected_cpts').length) {
1453 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('selected_cpts').length);
1454 }
1455
1456 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1457 },
1458 body: function body() {
1459 return this.template(this.getData());
1460 },
1461 rendered: function rendered() {
1462 this.$('.hustle-select-ajax').SUIselect2({
1463 tags: 'true',
1464 width: '100%',
1465 dropdownCssClass: 'sui-select-dropdown',
1466 ajax: {
1467 url: ajaxurl,
1468 delay: 250,
1469 // wait 250 milliseconds before triggering the request
1470 dataType: 'json',
1471 type: 'POST',
1472 data: function data(params) {
1473 var query = {
1474 action: 'get_new_condition_ids',
1475 search: params.term,
1476 postType: cpt
1477 };
1478 return query;
1479 },
1480 processResults: function processResults(data) {
1481 return {
1482 results: data.data
1483 };
1484 },
1485 cache: true
1486 },
1487 createTag: function createTag() {
1488 return false;
1489 }
1490 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1491 }
1492 }));
1493 });
1494 }
1495 /**
1496 * Categories
1497 */
1498
1499
1500 Optin.View.Conditions.categories = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
1501 conditionId: 'categories',
1502 setProperties: function setProperties() {
1503 this.title = optinVars.messages.conditions.categories;
1504 },
1505 defaults: function defaults() {
1506 return {
1507 filter_type: 'except',
1508 // except | only
1509 categories: []
1510 };
1511 },
1512 onInit: function onInit() {//this.listenTo( this.model, 'change', this.render );
1513 },
1514 getHeader: function getHeader() {
1515 if (this.getAttribute('categories').length) {
1516 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('categories').length);
1517 }
1518
1519 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1520 },
1521 body: function body() {
1522 return this.template(this.getData());
1523 },
1524 rendered: function rendered() {
1525 this.$('.hustle-select-ajax').SUIselect2({
1526 tags: 'true',
1527 width: '100%',
1528 dropdownCssClass: 'sui-select-dropdown',
1529 ajax: {
1530 url: ajaxurl,
1531 delay: 250,
1532 // wait 250 milliseconds before triggering the request
1533 dataType: 'json',
1534 type: 'POST',
1535 data: function data(params) {
1536 var query = {
1537 action: 'get_new_condition_ids',
1538 search: params.term,
1539 postType: 'category'
1540 };
1541 return query;
1542 },
1543 processResults: function processResults(data) {
1544 return {
1545 results: data.data
1546 };
1547 },
1548 cache: true
1549 },
1550 createTag: function createTag() {
1551 return false;
1552 }
1553 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1554 }
1555 }));
1556 /**
1557 * Tags
1558 */
1559
1560 Optin.View.Conditions.tags = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
1561 conditionId: 'tags',
1562 setProperties: function setProperties() {
1563 this.title = optinVars.messages.conditions.tags;
1564 },
1565 defaults: function defaults() {
1566 return {
1567 filter_type: 'except',
1568 // except | only
1569 tags: []
1570 };
1571 },
1572 onInit: function onInit() {//this.listenTo( this.model, 'change', this.render );
1573 },
1574 getHeader: function getHeader() {
1575 if (this.getAttribute('tags').length) {
1576 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('tags').length);
1577 }
1578
1579 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1580 },
1581 body: function body() {
1582 return this.template(this.getData());
1583 },
1584 rendered: function rendered() {
1585 this.$('.hustle-select-ajax').SUIselect2({
1586 width: '100%',
1587 tags: 'true',
1588 dropdownCssClass: 'sui-select-dropdown',
1589 ajax: {
1590 url: ajaxurl,
1591 delay: 250,
1592 // wait 250 milliseconds before triggering the request
1593 dataType: 'json',
1594 type: 'POST',
1595 data: function data(params) {
1596 var query = {
1597 action: 'get_new_condition_ids',
1598 search: params.term,
1599 postType: 'tag'
1600 };
1601 return query;
1602 },
1603 processResults: function processResults(data) {
1604 return {
1605 results: data.data
1606 };
1607 },
1608 cache: true
1609 },
1610 createTag: function createTag() {
1611 return false;
1612 }
1613 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1614 }
1615 }));
1616 /**
1617 * Visitor logged in / not logged in
1618 */
1619
1620 Optin.View.Conditions.visitor_logged_in_status = ConditionBase.extend({
1621 // eslint-disable-line camelcase
1622 conditionId: 'visitor_logged_in_status',
1623 setProperties: function setProperties() {
1624 this.title = optinVars.messages.conditions.visitor_logged_in;
1625 },
1626 defaults: function defaults() {
1627 return {
1628 show_to: 'logged_in'
1629 };
1630 },
1631 getHeader: function getHeader() {
1632 if (this.getAttribute('show_to').length && 'logged_out' === this.getAttribute('show_to')) {
1633 return optinVars.messages.condition_labels.logged_out;
1634 }
1635
1636 return optinVars.messages.condition_labels.logged_in;
1637 },
1638 body: function body() {
1639 return this.template(this.getData());
1640 }
1641 });
1642 /**
1643 * Amount of times the module has been shown to the same visitor
1644 */
1645
1646 Optin.View.Conditions.shown_less_than = ConditionBase.extend({
1647 // eslint-disable-line camelcase
1648 conditionId: 'shown_less_than',
1649 setProperties: function setProperties() {
1650 this.title = optinVars.messages.conditions.shown_less_than;
1651 },
1652 defaults: function defaults() {
1653 return {
1654 less_or_more: 'less_than',
1655 less_than: ''
1656 };
1657 },
1658 getHeader: function getHeader() {
1659 if (0 < this.getAttribute('less_than')) {
1660 if ('less_than' === this.getAttribute('less_or_more')) {
1661 return optinVars.messages.condition_labels.number_views.replace('{number}', this.getAttribute('less_than'));
1662 }
1663
1664 return optinVars.messages.condition_labels.number_views_more.replace('{number}', this.getAttribute('less_than'));
1665 }
1666
1667 return optinVars.messages.condition_labels.any;
1668 },
1669 body: function body() {
1670 return this.template(this.getData());
1671 }
1672 });
1673 /**
1674 * Visitor is on mobile / desktop
1675 */
1676
1677 Optin.View.Conditions.visitor_device = ConditionBase.extend({
1678 // eslint-disable-line camelcase
1679 conditionId: 'visitor_device',
1680 setProperties: function setProperties() {
1681 this.title = optinVars.messages.conditions.only_on_mobile;
1682 },
1683 defaults: function defaults() {
1684 return {
1685 filter_type: 'mobile' // mobile | not_mobile
1686
1687 };
1688 },
1689 getHeader: function getHeader() {
1690 if ('not_mobile' === this.getAttribute('filter_type')) {
1691 return optinVars.messages.condition_labels.desktop_only;
1692 }
1693
1694 return optinVars.messages.condition_labels.mobile_only;
1695 },
1696 body: function body() {
1697 return this.template(this.getData());
1698 }
1699 });
1700 /**
1701 * From referrer
1702 */
1703
1704 Optin.View.Conditions.from_referrer = ConditionBase.extend({
1705 // eslint-disable-line camelcase
1706 conditionId: 'from_referrer',
1707 disable: ['from_referrer'],
1708 setProperties: function setProperties() {
1709 this.title = optinVars.messages.conditions.from_specific_ref;
1710 },
1711 defaults: function defaults() {
1712 return {
1713 filter_type: 'true',
1714 // true | false
1715 refs: ''
1716 };
1717 },
1718 getHeader: function getHeader() {
1719 var length = 0;
1720
1721 if (this.getAttribute('refs').length) {
1722 length = this.countLines(this.getAttribute('refs'));
1723 }
1724
1725 if (length) {
1726 return ('false' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.except_these : optinVars.messages.condition_labels.only_these).replace('{number}', length);
1727 }
1728
1729 return 'false' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.any : optinVars.messages.condition_labels.none;
1730 },
1731 body: function body() {
1732 return this.template(this.getData());
1733 }
1734 });
1735 /**
1736 * Source of arrival
1737 */
1738
1739 Optin.View.Conditions.source_of_arrival = ConditionBase.extend({
1740 // eslint-disable-line camelcase
1741 conditionId: 'source_of_arrival',
1742 setProperties: function setProperties() {
1743 this.title = optinVars.messages.conditions.from_search_engine;
1744 },
1745 defaults: function defaults() {
1746 return {
1747 source_direct: 'false',
1748 // true | false
1749 source_external: 'false',
1750 // true | false
1751 source_internal: 'false',
1752 // true | false
1753 source_not_search: 'false',
1754 // true | false
1755 source_search: 'false' // true | false
1756
1757 };
1758 },
1759 getHeader: function getHeader() {
1760 var conditions = 0;
1761 var direct = _.isTrue(this.getAttribute('source_direct')) && ++conditions,
1762 external = _.isTrue(this.getAttribute('source_external')) && ++conditions,
1763 internal = _.isTrue(this.getAttribute('source_internal')) && ++conditions,
1764 search = _.isTrue(this.getAttribute('source_search')) && ++conditions,
1765 notSearch = _.isTrue(this.getAttribute('source_not_search')) && ++conditions;
1766
1767 if (search && notSearch || direct && internal && external) {
1768 return optinVars.messages.condition_labels.any;
1769 } else if (conditions) {
1770 return optinVars.messages.condition_labels.any_conditions.replace('{number}', conditions);
1771 }
1772
1773 return optinVars.messages.condition_labels.any;
1774 },
1775 body: function body() {
1776 return this.template(this.getData());
1777 }
1778 });
1779 /**
1780 * On/not on specific url
1781 */
1782
1783 Optin.View.Conditions.on_url = ConditionBase.extend({
1784 // eslint-disable-line camelcase
1785 conditionId: 'on_url',
1786 setProperties: function setProperties() {
1787 this.title = optinVars.messages.conditions.on_specific_url;
1788 },
1789 defaults: function defaults() {
1790 return {
1791 filter_type: 'except',
1792 // except | only
1793 urls: ''
1794 };
1795 },
1796 getHeader: function getHeader() {
1797 var length = 0;
1798
1799 if (this.getAttribute('urls').length) {
1800 length = this.countLines(this.getAttribute('urls'));
1801 }
1802
1803 if (length) {
1804 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', length);
1805 }
1806
1807 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1808 },
1809 body: function body() {
1810 return this.template(this.getData());
1811 }
1812 });
1813 /**
1814 * On/not on specific browser
1815 */
1816
1817 Optin.View.Conditions.on_browser = ConditionBase.extend({
1818 // eslint-disable-line camelcase
1819 conditionId: 'on_browser',
1820 setProperties: function setProperties() {
1821 this.title = optinVars.messages.conditions.on_specific_browser;
1822 },
1823 defaults: function defaults() {
1824 return {
1825 browsers: '',
1826 filter_type: 'except' // except | only
1827
1828 };
1829 },
1830 getHeader: function getHeader() {
1831 if (this.getAttribute('browsers').length) {
1832 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('browsers').length);
1833 }
1834
1835 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1836 },
1837 body: function body() {
1838 return this.template(this.getData());
1839 },
1840 rendered: function rendered() {
1841 this.$('.sui-select').val(this.getAttribute('browsers'));
1842 SUI.select.init(this.$('.sui-select'));
1843 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1844 }
1845 });
1846 /**
1847 * Visitor commented or not
1848 */
1849
1850 Optin.View.Conditions.visitor_commented = ConditionBase.extend({
1851 // eslint-disable-line camelcase
1852 conditionId: 'visitor_commented',
1853 setProperties: function setProperties() {
1854 this.title = optinVars.messages.conditions.visitor_has_never_commented;
1855 },
1856 defaults: function defaults() {
1857 return {
1858 filter_type: 'true' // true | false
1859
1860 };
1861 },
1862 getHeader: function getHeader() {
1863 return 'false' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.false : optinVars.messages.condition_labels.true;
1864 },
1865 body: function body() {
1866 return this.template(this.getData());
1867 }
1868 });
1869 /**
1870 * User has role
1871 */
1872
1873 Optin.View.Conditions.user_roles = ConditionBase.extend({
1874 // eslint-disable-line camelcase
1875 conditionId: 'user_roles',
1876 setProperties: function setProperties() {
1877 this.title = optinVars.messages.conditions.on_specific_roles;
1878 },
1879 defaults: function defaults() {
1880 return {
1881 roles: '',
1882 filter_type: 'except' // except | only
1883
1884 };
1885 },
1886 getHeader: function getHeader() {
1887 if (this.getAttribute('roles').length) {
1888 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('roles').length);
1889 }
1890
1891 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1892 },
1893 body: function body() {
1894 return this.template(this.getData());
1895 },
1896 rendered: function rendered() {
1897 this.$('.sui-select').val(this.getAttribute('roles'));
1898 SUI.select.init(this.$('.sui-select'));
1899 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1900 }
1901 });
1902 /**
1903 * Page templates
1904 */
1905
1906 Optin.View.Conditions.page_templates = ConditionBase.extend({
1907 // eslint-disable-line camelcase
1908 conditionId: 'page_templates',
1909 setProperties: function setProperties() {
1910 this.title = optinVars.messages.conditions.on_specific_templates;
1911 },
1912 defaults: function defaults() {
1913 return {
1914 templates: '',
1915 filter_type: 'except' // except | only
1916
1917 };
1918 },
1919 getHeader: function getHeader() {
1920 if (this.getAttribute('templates').length) {
1921 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('templates').length);
1922 }
1923
1924 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1925 },
1926 body: function body() {
1927 return this.template(this.getData());
1928 },
1929 rendered: function rendered() {
1930 this.$('.sui-select').val(this.getAttribute('templates'));
1931 SUI.select.init(this.$('.sui-select'));
1932 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1933 }
1934 });
1935 /**
1936 * Show modules based on user registration time
1937 */
1938
1939 Optin.View.Conditions.user_registration = ConditionBase.extend({
1940 // eslint-disable-line camelcase
1941 conditionId: 'user_registration',
1942 setProperties: function setProperties() {
1943 this.title = optinVars.messages.conditions.user_registration;
1944 },
1945 defaults: function defaults() {
1946 return {
1947 from_date: 0,
1948 to_date: 0
1949 };
1950 },
1951 getHeader: function getHeader() {
1952 var from = 0 < this.getAttribute('from_date') ? optinVars.messages.condition_labels.reg_date.replace('{number}', this.getAttribute('from_date')) : optinVars.messages.condition_labels.immediately;
1953 var upTo = 0 < this.getAttribute('to_date') ? optinVars.messages.condition_labels.reg_date.replace('{number}', this.getAttribute('to_date')) : optinVars.messages.condition_labels.forever;
1954 return from + ' - ' + upTo;
1955 },
1956 body: function body() {
1957 return this.template(this.getData());
1958 }
1959 });
1960 /**
1961 * Visitor country
1962 */
1963
1964 Optin.View.Conditions.visitor_country = ConditionBase.extend({
1965 // eslint-disable-line camelcase
1966 conditionId: 'visitor_country',
1967 setProperties: function setProperties() {
1968 this.title = optinVars.messages.conditions.not_in_a_country;
1969 },
1970 defaults: function defaults() {
1971 return {
1972 countries: '',
1973 filter_type: 'except' // only | except
1974
1975 };
1976 },
1977 getHeader: function getHeader() {
1978 if (this.getAttribute('countries').length) {
1979 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('countries').length);
1980 }
1981
1982 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
1983 },
1984 body: function body() {
1985 return this.template(this.getData());
1986 },
1987 rendered: function rendered() {
1988 this.$('.sui-select').val(this.getAttribute('countries'));
1989 SUI.select.init(this.$('.sui-select'));
1990 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
1991 }
1992 });
1993 /**
1994 * Static Pages
1995 */
1996
1997 Optin.View.Conditions.wp_conditions = ConditionBase.extend({
1998 // eslint-disable-line camelcase
1999 conditionId: 'wp_conditions',
2000 setProperties: function setProperties() {
2001 this.title = optinVars.messages.conditions.wp_conditions;
2002 },
2003 defaults: function defaults() {
2004 return {
2005 wp_conditions: '',
2006 filter_type: 'except' // except | only
2007
2008 };
2009 },
2010 getHeader: function getHeader() {
2011 if (this.getAttribute('wp_conditions').length) {
2012 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('wp_conditions').length);
2013 }
2014
2015 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2016 },
2017 body: function body() {
2018 return this.template(this.getData());
2019 },
2020 rendered: function rendered() {
2021 this.$('.sui-select').val(this.getAttribute('wp_conditions'));
2022 SUI.select.init(this.$('.sui-select'));
2023 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2024 }
2025 });
2026 /**
2027 * Archive Pages
2028 */
2029
2030 Optin.View.Conditions.archive_pages = ConditionBase.extend({
2031 // eslint-disable-line camelcase
2032 conditionId: 'archive_pages',
2033 setProperties: function setProperties() {
2034 this.title = optinVars.messages.conditions.archive_pages;
2035 },
2036 defaults: function defaults() {
2037 return {
2038 archive_pages: '',
2039 filter_type: 'except' // except | only
2040
2041 };
2042 },
2043 getHeader: function getHeader() {
2044 if (this.getAttribute('archive_pages').length) {
2045 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('archive_pages').length);
2046 }
2047
2048 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2049 },
2050 body: function body() {
2051 return this.template(this.getData());
2052 },
2053 rendered: function rendered() {
2054 this.$('.sui-select').val(this.getAttribute('archive_pages'));
2055 SUI.select.init(this.$('.sui-select'));
2056 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2057 }
2058 });
2059 /**********************************************************************************************************************************************************/
2060
2061 /*********************************** WooCommerce Conditions ***********************************************************************************************/
2062
2063 /**********************************************************************************************************************************************************/
2064
2065 /**
2066 * All WooCommerce Pages
2067 */
2068
2069 Optin.View.Conditions.wc_pages = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
2070 // eslint-disable-line camelcase
2071 conditionId: 'wc_pages',
2072 setProperties: function setProperties() {
2073 this.title = optinVars.messages.conditions.wc_pages;
2074 },
2075 defaults: function defaults() {
2076 return {
2077 filter_type: 'all' // all | none
2078
2079 };
2080 },
2081 getHeader: function getHeader() {
2082 if ('none' === this.getAttribute('filter_type')) {
2083 return optinVars.messages.condition_labels.none;
2084 }
2085
2086 return optinVars.messages.condition_labels.all;
2087 },
2088 body: function body() {
2089 return this.template(this.getData());
2090 }
2091 }));
2092 /**
2093 * WooCommerce Categories
2094 */
2095
2096 Optin.View.Conditions.wc_categories = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
2097 // eslint-disable-line camelcase
2098 conditionId: 'wc_categories',
2099 setProperties: function setProperties() {
2100 this.title = optinVars.messages.conditions.wc_categories;
2101 },
2102 defaults: function defaults() {
2103 return {
2104 filter_type: 'except',
2105 // except | only
2106 wc_categories: [] // eslint-disable-line camelcase
2107
2108 };
2109 },
2110 onInit: function onInit() {},
2111 getHeader: function getHeader() {
2112 if (this.getAttribute('wc_categories').length) {
2113 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('wc_categories').length);
2114 }
2115
2116 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2117 },
2118 body: function body() {
2119 return this.template(this.getData());
2120 },
2121 rendered: function rendered() {
2122 this.$('.hustle-select-ajax').SUIselect2({
2123 tags: 'true',
2124 width: '100%',
2125 ajax: {
2126 url: ajaxurl,
2127 delay: 250,
2128 // wait 250 milliseconds before triggering the request
2129 dataType: 'json',
2130 type: 'POST',
2131 data: function data(params) {
2132 var query = {
2133 action: 'get_new_condition_ids',
2134 search: params.term,
2135 postType: 'wc_category'
2136 };
2137 return query;
2138 },
2139 processResults: function processResults(data) {
2140 return {
2141 results: data.data
2142 };
2143 },
2144 cache: true
2145 },
2146 createTag: function createTag() {
2147 return false;
2148 }
2149 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2150 }
2151 }));
2152 /**
2153 * WooCommerce Tags
2154 */
2155
2156 Optin.View.Conditions.wc_tags = ConditionBase.extend(_.extend({}, ToggleButtonTogglerMixin, {
2157 // eslint-disable-line camelcase
2158 conditionId: 'wc_tags',
2159 setProperties: function setProperties() {
2160 this.title = optinVars.messages.conditions.wc_tags;
2161 },
2162 defaults: function defaults() {
2163 return {
2164 filter_type: 'except',
2165 // except | only
2166 wc_tags: [] // eslint-disable-line camelcase
2167
2168 };
2169 },
2170 onInit: function onInit() {},
2171 getHeader: function getHeader() {
2172 if (this.getAttribute('wc_tags').length) {
2173 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('wc_tags').length);
2174 }
2175
2176 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2177 },
2178 body: function body() {
2179 return this.template(this.getData());
2180 },
2181 rendered: function rendered() {
2182 this.$('.hustle-select-ajax').SUIselect2({
2183 tags: 'true',
2184 width: '100%',
2185 dropdownCssClass: 'sui-select-dropdown',
2186 ajax: {
2187 url: ajaxurl,
2188 delay: 250,
2189 // wait 250 milliseconds before triggering the request
2190 dataType: 'json',
2191 type: 'POST',
2192 data: function data(params) {
2193 var query = {
2194 action: 'get_new_condition_ids',
2195 search: params.term,
2196 postType: 'wc_tag'
2197 };
2198 return query;
2199 },
2200 processResults: function processResults(data) {
2201 return {
2202 results: data.data
2203 };
2204 },
2205 cache: true
2206 },
2207 createTag: function createTag() {
2208 return false;
2209 }
2210 }).on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2211 }
2212 }));
2213 /**
2214 * WooCommerce Archive Pages
2215 */
2216
2217 Optin.View.Conditions.wc_archive_pages = ConditionBase.extend({
2218 // eslint-disable-line camelcase
2219 conditionId: 'wc_archive_pages',
2220 setProperties: function setProperties() {
2221 this.title = optinVars.messages.conditions.wc_archive_pages;
2222 },
2223 defaults: function defaults() {
2224 return {
2225 wc_archive_pages: '',
2226 filter_type: 'except' // except | only
2227
2228 };
2229 },
2230 getHeader: function getHeader() {
2231 if (this.getAttribute('wc_archive_pages').length) {
2232 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('wc_archive_pages').length);
2233 }
2234
2235 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2236 },
2237 body: function body() {
2238 return this.template(this.getData());
2239 },
2240 rendered: function rendered() {
2241 this.$('.sui-select').val(this.getAttribute('wc_archive_pages'));
2242 SUI.select.init(this.$('.sui-select'));
2243 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2244 }
2245 });
2246 /**
2247 * WooCommerce Static Pages
2248 */
2249
2250 Optin.View.Conditions.wc_static_pages = ConditionBase.extend({
2251 // eslint-disable-line camelcase
2252 conditionId: 'wc_static_pages',
2253 setProperties: function setProperties() {
2254 this.title = optinVars.messages.conditions.wc_static_pages;
2255 },
2256 defaults: function defaults() {
2257 return {
2258 wc_static_pages: '',
2259 filter_type: 'except' // except | only
2260
2261 };
2262 },
2263 getHeader: function getHeader() {
2264 if (this.getAttribute('wc_static_pages').length) {
2265 return ('only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.only_these : optinVars.messages.condition_labels.except_these).replace('{number}', this.getAttribute('wc_static_pages').length);
2266 }
2267
2268 return 'only' === this.getAttribute('filter_type') ? optinVars.messages.condition_labels.none : optinVars.messages.condition_labels.all;
2269 },
2270 body: function body() {
2271 return this.template(this.getData());
2272 },
2273 rendered: function rendered() {
2274 this.$('.sui-select').val(this.getAttribute('wc_static_pages'));
2275 SUI.select.init(this.$('.sui-select'));
2276 this.$('.sui-select').on('select2:selecting', reenableScroll).on('select2:unselecting', reenableScroll);
2277 }
2278 });
2279 /**
2280 * Cookie is set
2281 */
2282
2283 Optin.View.Conditions.cookie_set = ConditionBase.extend({
2284 // eslint-disable-line camelcase
2285 conditionId: 'cookie_set',
2286 setProperties: function setProperties() {
2287 this.title = optinVars.messages.conditions.cookie_set;
2288 },
2289 defaults: function defaults() {
2290 return {
2291 cookie_name: '',
2292 cookie_value: '',
2293 filter_type: 'exists',
2294 // exists | doesnt_exists
2295 cookie_value_conditions: 'anything'
2296 };
2297 },
2298 changeInput: function changeInput(e) {
2299 var el = e.target,
2300 $el = $(el);
2301 var updated,
2302 $selectedTabInput,
2303 $selectedTabInputValue,
2304 $cookieNameInput,
2305 val = $el.is('.sui-select') ? $el.val() : e.target.value;
2306 e.stopImmediatePropagation(); // Because cookies condition uses two inputs based on which condition type is selected
2307 // we have to update a value based on currently selected option.
2308
2309 if ('undefined' !== typeof $el.find(':selected').data('switcher-menu')) {
2310 $selectedTabInput = $el.closest('.select-content-switcher-wrapper').find(".select-switcher-content[data-switcher-content=\"".concat($el.find(':selected').data('switcher-menu'), "\"] input"));
2311 $selectedTabInputValue = $selectedTabInput.val();
2312 $selectedTabInputValue = $selectedTabInputValue ? $selectedTabInputValue : '';
2313 updated = this.updateAttribute('cookie_value', $selectedTabInputValue);
2314 } // Let's make sure we use the proper cookie name.
2315
2316
2317 if ('filter_type' === $el.data('attribute')) {
2318 $cookieNameInput = $el.closest('.sui-side-tabs').find('.sui-tab-boxed:not(.active) input[data-attribute="cookie_name"]').val();
2319 $el.closest('.sui-side-tabs').find('.sui-tab-boxed.active input[data-attribute="cookie_name"]').val($cookieNameInput);
2320 updated = this.updateAttribute('cookie_name', $cookieNameInput);
2321 }
2322
2323 if ($el.is(':checkbox')) {
2324 val = $el.is(':checked');
2325 } // skip for input search
2326
2327
2328 if ($el.is('.select2-search__field')) {
2329 return false;
2330 }
2331
2332 var attribute = el.getAttribute('data-attribute');
2333 updated = this.updateAttribute(attribute, val);
2334 this.refreshLabel();
2335 return updated;
2336 },
2337 getHeader: function getHeader() {
2338 if (this.getAttribute('cookie_name')) {
2339 if ('exists' === this.getAttribute('filter_type')) {
2340 if ('anything' === this.getAttribute('cookie_value_conditions')) {
2341 return optinVars.messages.condition_labels.cookie_anything.replace('{name}', this.getAttribute('cookie_name')).replace(/(<([^>]+)>)/ig, '');
2342 }
2343
2344 return optinVars.messages.condition_labels.cookie_value.replace('{name}', this.getAttribute('cookie_name')).replace('{value_condition}', optinVars.wp_cookie_set[this.getAttribute('cookie_value_conditions')]).replace('{value}', this.getAttribute('cookie_value')).replace(/(<([^>]+)>)/ig, '');
2345 }
2346
2347 return optinVars.messages.condition_labels.cookie_doesnt_exist.replace('{name}', this.getAttribute('cookie_name')).replace(/(<([^>]+)>)/ig, '');
2348 }
2349
2350 return '-';
2351 },
2352 body: function body() {
2353 return this.template(this.getData());
2354 },
2355 rendered: function rendered() {
2356 SUI.select.init(this.$('.sui-select'));
2357 }
2358 });
2359 $(document).trigger('hustleAddViewConditions', [ConditionBase]);
2360 })(jQuery);
2361 Hustle.define('imageUploader', function () {
2362 'use strict';
2363
2364 return Backbone.View.extend({
2365 events: {
2366 'click .hustle-image-uploader-browse': 'open',
2367 'click .hustle-image-uploader-clear': 'clear'
2368 },
2369 mediaFrame: false,
2370 initialize: function initialize(options) {
2371 this.attribute = options.attribute;
2372
2373 if (!this.model || !this.attribute) {
2374 throw new Error('Undefined model or attribute.');
2375 }
2376
2377 this.render();
2378 },
2379 render: function render() {
2380 this.defineMediaFrame();
2381 return this;
2382 },
2383 // If no image is set, show the upload button. Display the selected image otherwise.
2384 showImagePreviewOrButton: function showImagePreviewOrButton() {
2385 var selectedImage = this.model.get(this.attribute);
2386
2387 if ('' === selectedImage || 'undefined' === typeof selectedImage) {
2388 this.$el.removeClass('sui-has_file');
2389 } else {
2390 this.$el.addClass('sui-has_file');
2391 }
2392 },
2393 defineMediaFrame: function defineMediaFrame() {
2394 var self = this;
2395 this.mediaFrame = wp.media({
2396 title: optinVars.media_uploader.select_or_upload,
2397 button: {
2398 text: optinVars.media_uploader.use_this_image
2399 },
2400 multiple: false
2401 }).on('select', function () {
2402 var media = self.mediaFrame.state().get('selection').first().toJSON();
2403 var imageSrc, imageThumbnail;
2404
2405 if (media && media.url) {
2406 imageSrc = media.url;
2407 imageThumbnail = '';
2408 self.model.set(self.attribute, imageSrc);
2409
2410 if (media.sizes && media.sizes.thumbnail && media.sizes.thumbnail.url) {
2411 imageThumbnail = media.sizes.thumbnail.url;
2412 }
2413
2414 self.$el.find('.sui-upload-file .hustle-upload-file-url').text(imageSrc);
2415 self.$el.find('.sui-image-preview').css('background-image', 'url( ' + imageThumbnail + ' )');
2416 self.showImagePreviewOrButton();
2417 }
2418 });
2419 },
2420 open: function open(e) {
2421 e.preventDefault();
2422 this.mediaFrame.open();
2423 },
2424 clear: function clear(e) {
2425 e.preventDefault();
2426 this.model.set(this.attribute, '');
2427 this.$el.find('.sui-upload-file .hustle-upload-file-url').text('');
2428 this.$el.find('.sui-image-preview').css('background-image', 'url()');
2429 this.showImagePreviewOrButton();
2430 }
2431 });
2432 });
2433 /* global tinyMCE */
2434 Hustle.define('Modals.Edit_Field', function ($) {
2435 'use strict';
2436
2437 return Backbone.View.extend({
2438 el: '#hustle-dialog--edit-field',
2439 events: {
2440 'change input[name="time_format"]': 'changeTimeFormat',
2441 'blur input[name="name"]': 'trimName',
2442 'change input': 'fieldUpdated',
2443 'click input[type="radio"]': 'fieldUpdated',
2444 'change select': 'fieldUpdated',
2445 'change input[name="version"]': 'handleCaptchaSave'
2446 },
2447 initialize: function initialize(options) {
2448 var _this = this;
2449
2450 this.field = options.field;
2451 this.changed = {}; // Same as this.field, but with the values for the field's view. Won't be stored.
2452
2453 this.fieldData = options.fieldData;
2454 this.model = options.model;
2455 this.render();
2456 this.$el.off('close').on('close', function () {
2457 return _this.modalClosed();
2458 });
2459 this.$('#hustle-apply-changes').off('mouseup').on('mouseup', function (e) {
2460 return _this.applyChanges(e);
2461 });
2462 },
2463 render: function render() {
2464 this.renderHeader();
2465 this.renderLabels();
2466 this.renderSettings();
2467 this.renderStyling();
2468 this.handleCaptchaSave(); //select the first tab
2469
2470 this.$('.hustle-data-pane').first().trigger('click'); // Make the search box work within the modal.
2471
2472 SUI.select.init(this.$('.sui-select'));
2473 },
2474 renderHeader: function renderHeader() {
2475 var $tagContainer = this.$('.hustle-field-tag-container');
2476
2477 if (!$tagContainer.length) {
2478 this.$('.sui-box-header').append('<div class="sui-actions-left hustle-field-tag-container"><span class="sui-tag"></span></div>');
2479 }
2480
2481 this.$('.sui-box-header .sui-tag').text(this.field.type);
2482 },
2483 renderLabels: function renderLabels() {
2484 if (-1 !== $.inArray(this.field.type, ['recaptcha', 'gdpr', 'submit'])) {
2485 this.$('#hustle-data-tab--labels').removeClass('hustle-data-pane').addClass('sui-hidden');
2486 this.$('#hustle-data-pane--labels').addClass('sui-hidden');
2487 return;
2488 }
2489
2490 this.$('#hustle-data-tab--labels').removeClass('sui-hidden').addClass('hustle-data-pane');
2491 this.$('#hustle-data-pane--labels').removeClass('sui-hidden'); // Check if a specific template for this field exists.
2492
2493 var templateId = 'hustle-' + this.field.type + '-field-labels-tpl'; // If a specific template doesn't exist, use the common template.
2494
2495 if (!$('#' + templateId).length) {
2496 templateId = 'hustle-common-field-labels-tpl';
2497 }
2498
2499 var template = Optin.template(templateId);
2500 this.$('#hustle-data-pane--labels').html(template(this.fieldData));
2501 Hustle.Events.trigger('view.rendered', this);
2502 },
2503 renderSettings: function renderSettings() {
2504 if ('hidden' === this.field.type) {
2505 this.$('#hustle-data-tab--settings').removeClass('hustle-data-pane').addClass('sui-hidden');
2506 this.$('#hustle-data-pane--settings').addClass('sui-hidden');
2507 Module.Utils.accessibleHide(this.$('[data-tabs]'));
2508 return;
2509 }
2510
2511 Module.Utils.accessibleShow(this.$('[data-tabs]'));
2512 this.$('#hustle-data-tab--settings').removeClass('sui-hidden').addClass('hustle-data-pane');
2513 this.$('#hustle-data-pane--settings').removeClass('sui-hidden'); // Fixes a bug caused by tinyMCE used in a popup.
2514
2515 $(document).on('focusin', function (e) {
2516 if ($(e.target).closest('.wp-link-input').length) {
2517 e.stopImmediatePropagation();
2518 }
2519 }); // Check if a specific template for this field exists.
2520
2521 var templateId = 'hustle-' + this.field.type + '-field-settings-tpl'; // If a specific template doesn't exist, use the common template.
2522
2523 if (!$('#' + templateId).length) {
2524 templateId = 'hustle-common-field-settings-tpl';
2525 }
2526
2527 var template = Optin.template(templateId);
2528 this.$('#hustle-data-pane--settings').html(template(this.fieldData));
2529 Hustle.Events.trigger('view.rendered', this);
2530
2531 if ('gdpr' === this.field.type) {
2532 // These only allow inline elements.
2533 var editorSettings = {
2534 tinymce: {
2535 wpautop: false,
2536 toolbar1: 'bold,italic,strikethrough,link',
2537 valid_elements: 'a[href|target=_blank],strong/b,i,u,s,em,del',
2538 // eslint-disable-line camelcase
2539 forced_root_block: '' // eslint-disable-line camelcase
2540
2541 },
2542 quicktags: {
2543 buttons: 'strong,em,del,link'
2544 }
2545 };
2546 wp.editor.remove('gdpr_message');
2547 wp.editor.initialize('gdpr_message', editorSettings);
2548 } else if ('recaptcha' === this.field.type) {
2549 var _editorSettings = {
2550 tinymce: {
2551 toolbar: ['bold italic link alignleft aligncenter alignright']
2552 },
2553 quicktags: true
2554 };
2555 wp.editor.remove('v3_recaptcha_badge_replacement');
2556 wp.editor.initialize('v3_recaptcha_badge_replacement', _editorSettings);
2557 wp.editor.remove('v2_invisible_badge_replacement');
2558 wp.editor.initialize('v2_invisible_badge_replacement', _editorSettings);
2559 }
2560 },
2561 renderStyling: function renderStyling() {
2562 if ('hidden' === this.field.type) {
2563 this.$('#hustle-data-tab--styling').removeClass('hustle-data-pane').addClass('sui-hidden');
2564 this.$('#hustle-data-pane--styling').addClass('sui-hidden');
2565 return;
2566 }
2567
2568 this.$('#hustle-data-tab--styling').removeClass('sui-hidden').addClass('hustle-data-pane');
2569 this.$('#hustle-data-pane--styling').removeClass('sui-hidden'); // Check if a specific template for this field exists.
2570
2571 var templateId = 'hustle-' + this.field.type + '-field-styling-tpl'; // If a specific template doesn't exist, use the common template.
2572
2573 if (!$('#' + templateId).length) {
2574 templateId = 'hustle-common-field-styling-tpl';
2575 }
2576
2577 var template = Optin.template(templateId);
2578 this.$('#hustle-data-pane--styling').html(template(this.fieldData));
2579 },
2580 fieldUpdated: function fieldUpdated(e) {
2581 var $this = $(e.target),
2582 dataName = $this.attr('name'),
2583 dataValue = $this.is(':checkbox') ? $this.is(':checked') : $this.val();
2584 this.changed[dataName] = dataValue;
2585 },
2586 modalClosed: function modalClosed() {
2587 this.undelegateEvents();
2588 this.stopListening();
2589 },
2590 changeTimeFormat: function changeTimeFormat(e) {
2591 var $this = $(e.target),
2592 dataValue = $this.val();
2593
2594 if ('12' === dataValue) {
2595 $('#hustle-date-format').closest('.sui-form-field').show();
2596 $('input[name="time_hours"]').prop('min', 1).prop('max', 12);
2597 } else {
2598 $('#hustle-date-format').closest('.sui-form-field').hide();
2599 $('input[name="time_hours"]').prop('min', 0).prop('max', 23);
2600 }
2601 },
2602 handleCaptchaSave: function handleCaptchaSave() {
2603 if ('recaptcha' !== this.field.type) {
2604 return;
2605 }
2606
2607 var avaiableCaptcha = $('#available_recaptchas').val();
2608
2609 if (avaiableCaptcha) {
2610 avaiableCaptcha = avaiableCaptcha.split(',');
2611 var version = $('input[name="version"]:checked').val();
2612
2613 if (-1 === _.indexOf(avaiableCaptcha, version)) {
2614 $('#hustle-dialog--edit-field').find('#hustle-apply-changes').attr('disabled', 'disabled');
2615 } else {
2616 $('#hustle-dialog--edit-field').find('#hustle-apply-changes').attr('disabled', false);
2617 }
2618 } else {
2619 $('#hustle-dialog--edit-field').find('#hustle-apply-changes').attr('disabled', 'disabled');
2620 }
2621 },
2622
2623 /**
2624 * Trim and replace spaces in field name.
2625 *
2626 * @since 4.0.0
2627 * @param {Object} e
2628 */
2629 trimName: function trimName(e) {
2630 var $input = this.$(e.target),
2631 newVal = $input.val().trim().replace(/ /g, '_');
2632 $input.val(newVal);
2633 },
2634
2635 /**
2636 * Add the saved settings to the model.
2637 *
2638 * @since 4.0.0
2639 * @param {Object} e
2640 */
2641 applyChanges: function applyChanges(e) {
2642 // TODO: do validation
2643 // TODO: keep consistency with how stuff is saved in visibility conditions
2644 var $button = this.$(e.target),
2645 formFields = Object.assign({}, this.model.get('form_elements')); // if gdpr message
2646
2647 if ('gdpr' === this.field.type && 'undefined' !== typeof tinyMCE) {
2648 // gdpr_message editor
2649 var gdprMessageEditor = tinyMCE.get('gdpr_message'),
2650 $gdprMessageTextarea = this.$('textarea#gdpr_message'),
2651 gdprMessage = 'true' === $gdprMessageTextarea.attr('aria-hidden') ? gdprMessageEditor.getContent() : $gdprMessageTextarea.val();
2652 this.changed.gdpr_message = gdprMessage; // eslint-disable-line camelcase
2653 } else if ('recaptcha' === this.field.type && 'undefined' !== typeof tinyMCE) {
2654 // v3 recaptcha badge editor.
2655 var v3messageEditor = tinyMCE.get('v3_recaptcha_badge_replacement'),
2656 $v3messageTextarea = this.$('textarea#v3_recaptcha_badge_replacement'),
2657 v3message = 'true' === $v3messageTextarea.attr('aria-hidden') ? v3messageEditor.getContent() : $v3messageTextarea.val();
2658 this.changed.v3_recaptcha_badge_replacement = v3message; // eslint-disable-line camelcase
2659 // v2 invisible badge editor.
2660
2661 var v2messageEditor = tinyMCE.get('v2_invisible_badge_replacement'),
2662 $v2messageTextarea = this.$('textarea#v2_invisible_badge_replacement'),
2663 v2message = 'true' === $v2messageTextarea.attr('aria-hidden') ? v2messageEditor.getContent() : $v2messageTextarea.val();
2664 this.changed.v2_invisible_badge_replacement = v2message; // eslint-disable-line camelcase
2665 } // If there were changes.
2666
2667
2668 if (Object.keys(this.changed).length) {
2669 var oldField = _.extend({}, this.field);
2670
2671 _.extend(this.field, this.changed); // Don't allow to override Email field created by default
2672 // and prevent field's names from being empty.
2673
2674
2675 if ('name' in this.changed && 'email' !== oldField.name && 'email' === this.field.name || 'name' in this.changed && !this.field.name.trim().length) {
2676 this.field.name = oldField.name;
2677 delete this.changed.name;
2678 }
2679
2680 if ('email' === oldField.name) {
2681 this.field.name = 'email';
2682 delete this.changed.name;
2683 }
2684
2685 if (this.field.name !== oldField.name) {
2686 // Check if the new name already in use.
2687 var newOriginalName = this.field.name;
2688 var i = 0,
2689 newName = newOriginalName;
2690
2691 while (newName in formFields) {
2692 i++;
2693 newName = newOriginalName + '-' + i;
2694 }
2695
2696 this.field.name = newName;
2697 this.changed.name = newName;
2698 } // "Name" is the unique identifier. If it changed, return and let the callback handle it.
2699
2700
2701 if (!('name' in this.changed)) {
2702 // Update this field.
2703 formFields[this.field.name] = this.field; // Alternative to deep cloning the formFields object to trigger 'change' in the model.
2704
2705 this.model.unset('form_elements', {
2706 silent: true
2707 });
2708 this.model.set('form_elements', formFields);
2709 }
2710
2711 this.trigger('field:updated', this.field, this.changed, oldField);
2712 }
2713
2714 $button.addClass('sui-button-onload');
2715 setTimeout(function () {
2716 return $button.removeClass('sui-button-onload');
2717 }, 300);
2718 }
2719 });
2720 });
2721 /* global moment */
2722 Hustle.define('Modals.EditSchedule', function ($) {
2723 'use strict';
2724
2725 return Backbone.View.extend({
2726 el: '#hustle-schedule-dialog-content',
2727 dialogId: 'hustle-dialog--add-schedule',
2728 events: {
2729 'click #hustle-schedule-save': 'saveSchedule',
2730 'click .hustle-schedule-cancel': 'cancel',
2731 'click .hustle-schedule-delete': 'openDeleteModal',
2732 'change .hustle-datepicker-field[name=start_date]': 'changeMinDate',
2733 'change [name=not_schedule_start]': 'changeMinDate',
2734 // Change events.
2735 'change .hustle-checkbox-with-dependencies input[type="checkbox"]': 'checkboxWithDependenciesChanged',
2736 'change select[name="custom_timezone"]': 'customTimezoneChanged'
2737 },
2738 initialize: function initialize(opts) {
2739 this.model = opts.model;
2740 },
2741 open: function open() {
2742 var modalId = this.dialogId;
2743 var focusAfterClosed = 'hustle-schedule-focus';
2744 var focusWhenOpen = undefined;
2745 var hasOverlayMask = false;
2746 this.renderContent();
2747 $('.hustle-datepicker-field').datepicker({
2748 beforeShow: function beforeShow() {
2749 $('#ui-datepicker-div').addClass('sui-calendar');
2750 },
2751 dateFormat: 'm/d/yy'
2752 }); // Make the search box work within the modal.
2753
2754 SUI.select.init(this.$('.sui-select'));
2755 this.changeMinDate();
2756 SUI.openModal(modalId, focusAfterClosed, focusWhenOpen, hasOverlayMask);
2757 },
2758 changeMinDate: function changeMinDate() {
2759 var minDate;
2760
2761 if (!$('[name=not_schedule_end]').is(':checked') && !$('[name=not_schedule_start]').is(':checked')) {
2762 minDate = $('[name=start_date]').val();
2763 }
2764
2765 $('.hustle-datepicker-field[name=end_date]').datepicker('option', {
2766 minDate: minDate
2767 });
2768 },
2769 renderContent: function renderContent() {
2770 var template = Optin.template('hustle-schedule-dialog-content-tpl'),
2771 $container = $('#hustle-schedule-dialog-content'),
2772 data = Object.assign({}, this.model.get('schedule'));
2773 data.is_schedule = this.model.get('is_schedule'); // eslint-disable-line camelcase
2774
2775 data.serverCurrentTime = this.getTimeToDisplay('server');
2776 data.customCurrentTime = this.getTimeToDisplay('custom');
2777 this.setElement(template(data));
2778 $container.html(this.$el); // Bind SUI elements again.
2779
2780 Hustle.Events.trigger('view.rendered', this); // We hide/show some elements on change, so keep the view displaying what it should when re-rendering the modal.
2781
2782 this.refreshViewOnRender(data);
2783 },
2784 refreshViewOnRender: function refreshViewOnRender(data) {
2785 // Hide/show dependent elements.
2786 this.$('.hustle-checkbox-with-dependencies input').each(function () {
2787 $(this).trigger('change');
2788 }); // Display the correct tab.
2789
2790 if ('server' === data.time_to_use) {
2791 $('#tab-timezone-server').click();
2792 } else {
2793 $('#tab-timezone-custom').click();
2794 } // Display the correct tab.
2795
2796
2797 if ('all' === data.active_days) {
2798 $('#tab-schedule-everyday').click();
2799 } else {
2800 $('#tab-schedule-somedays').click();
2801 } // Comparing the model's value with the value selected in the "select" element.
2802
2803
2804 var timezoneSelectValue = this.$('select[name="custom_timezone"]').val(),
2805 timezoneModelValue = data.custom_timezone; // We're retrieving the timezone options from a wp function, so we can't
2806 // update its selected value on js render as we do with other selects.
2807
2808 if (timezoneModelValue !== timezoneSelectValue) {
2809 this.$('select[name="custom_timezone"]').val(timezoneModelValue).trigger('change');
2810 }
2811 },
2812 getTimeToDisplay: function getTimeToDisplay(source) {
2813 var timezone = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2814 var settings = this.model.get('schedule');
2815 var utcOffset = false,
2816 currentTime = false;
2817
2818 if ('server' === source) {
2819 utcOffset = optinVars.schedule.wp_gmt_offset || 0;
2820 } else {
2821 var customTimezone = timezone || settings.custom_timezone;
2822
2823 if (customTimezone.includes('UTC')) {
2824 var selectedOffset = customTimezone.replace('UTC', ''); // There's a timezone with the value "UTC".
2825
2826 utcOffset = selectedOffset.length ? parseFloat(selectedOffset) : 0;
2827 } else {
2828 var endMoment = moment().tz(customTimezone);
2829 currentTime = endMoment.format('hh:mm a');
2830 }
2831 } // Calculate the time with the manual offset.
2832 // Moment.js doesn't support manual offsets with decimals, so not using it here.
2833
2834
2835 if (false === currentTime && false !== utcOffset) {
2836 // This isn't the correct timestamp for the given offset.
2837 // We just want to display the time for reference.
2838 var timestamp = Date.now() + utcOffset * 3600 * 1000,
2839 _endMoment = moment.utc(timestamp);
2840
2841 currentTime = _endMoment.format('hh:mm a');
2842 }
2843
2844 return currentTime;
2845 },
2846 saveSchedule: function saveSchedule(e) {
2847 var $button = $(e.currentTarget),
2848 data = this.processFormForSave(),
2849 wasScheduled = '1' === this.model.get('is_schedule');
2850 $button.addClass('sui-button-onload');
2851 $button.prop('disabled', true);
2852 setTimeout(function () {
2853 $button.removeClass('sui-button-onload');
2854 $button.prop('disabled', false);
2855 }, 500);
2856 this.model.set('is_schedule', '1');
2857 this.model.set('schedule', data);
2858 this.model.userHasChange();
2859 this.closeModal(); // Display a notification only when the schedule wasn't set, but now it is.
2860
2861 if (!wasScheduled) {
2862 Module.Notification.open('success', optinVars.schedule.new_schedule_set, false);
2863 }
2864
2865 this.trigger('schedule:updated');
2866 },
2867 processFormForSave: function processFormForSave() {
2868 var $form = $('#hustle-edit-schedule-form');
2869 var data = Module.Utils.serializeObject($form);
2870 return data;
2871 },
2872 cancel: function cancel() {
2873 this.renderContent();
2874 this.closeModal();
2875 },
2876 openDeleteModal: function openDeleteModal(e) {
2877 var dialogId = 'hustle-dialog--delete-schedule',
2878 template = Optin.template('hustle-delete-schedule-dialog-content-tpl'),
2879 $this = $(e.currentTarget),
2880 data = {
2881 id: $this.data('id'),
2882 title: $this.data('title'),
2883 description: $this.data('description'),
2884 action: 'delete',
2885 actionClass: 'hustle-button-delete'
2886 },
2887 newFocusAfterClosed = 'hustle-schedule-notice',
2888 newFocusFirst = undefined,
2889 hasOverlayMask = true,
2890 content = template(data),
2891 footer = $('#' + dialogId + ' #hustle-delete-schedule-dialog-content'),
2892 deleteButton = footer.find('button.hustle-delete-confirm'); // Add the templated content to the modal.
2893
2894 if (!deleteButton.length) {
2895 footer.append(content);
2896 } // Add the title to the modal.
2897
2898
2899 $('#' + dialogId + ' #hustle-dialog--delete-schedule-title').html(data.title);
2900 $('#' + dialogId + ' #hustle-dialog--delete-schedule-description').html(data.description);
2901 SUI.replaceModal(dialogId, newFocusAfterClosed, newFocusFirst, hasOverlayMask);
2902 $('#hustle-delete-schedule-dialog-content').off('submit').on('submit', $.proxy(this.deactivateSchedule, this));
2903 },
2904 deactivateSchedule: function deactivateSchedule(e) {
2905 e.preventDefault();
2906 this.closeModal();
2907 this.model.set('is_schedule', '0');
2908 this.model.userHasChange();
2909 this.trigger('schedule:updated');
2910 },
2911 checkboxWithDependenciesChanged: function checkboxWithDependenciesChanged(e) {
2912 var $this = $(e.currentTarget),
2913 disableWhenOn = $this.data('disable-on'),
2914 hideWhenOn = $this.data('hide-on');
2915
2916 if (disableWhenOn) {
2917 var $dependencies = this.$("[data-checkbox-content=\"".concat(disableWhenOn, "\"]"));
2918
2919 if ($this.is(':checked')) {
2920 $dependencies.addClass('sui-disabled');
2921 $dependencies.prop('disabled', true);
2922
2923 if ($dependencies.parent().hasClass('select-container')) {
2924 $dependencies.parent().addClass('sui-disabled');
2925 }
2926 } else {
2927 $dependencies.removeClass('sui-disabled');
2928 $dependencies.prop('disabled', false);
2929
2930 if ($dependencies.parent().hasClass('select-container')) {
2931 $dependencies.parent().removeClass('sui-disabled');
2932 }
2933 }
2934 }
2935
2936 if (hideWhenOn) {
2937 var _$dependencies = this.$("[data-checkbox-content=\"".concat(hideWhenOn, "\"]"));
2938
2939 if ($this.is(':checked')) {
2940 Module.Utils.accessibleHide(_$dependencies);
2941 } else {
2942 Module.Utils.accessibleShow(_$dependencies);
2943 }
2944 }
2945 },
2946 customTimezoneChanged: function customTimezoneChanged(e) {
2947 var value = $(e.currentTarget).val(),
2948 timeContainer = this.$('#hustle-custom-timezone-current-time'),
2949 currentTime = this.getTimeToDisplay('custom', value);
2950 timeContainer.text(currentTime);
2951 },
2952 closeModal: function closeModal() {
2953 $('.hustle-datepicker-field').datepicker('destroy');
2954 SUI.closeModal();
2955 }
2956 });
2957 });
2958 Hustle.define('Modals.Optin_Fields', function () {
2959 'use strict';
2960
2961 return Backbone.View.extend({
2962 el: '#hustle-dialog--optin-fields',
2963 model: {},
2964 selectedFields: [],
2965 events: {
2966 'click .sui-box-selector input': 'selectFields',
2967 'click #hustle-insert-fields': 'insertFields'
2968 },
2969 initialize: function initialize(options) {
2970 var _this = this;
2971
2972 this.model = options.model;
2973 this.selectedFields = [];
2974 this.$el.off('close').on('close', function () {
2975 return _this.closeModalActions();
2976 });
2977 },
2978 // TODO: don't make them selected on click, but on "Insert fields".
2979 selectFields: function selectFields(e) {
2980 var $input = this.$(e.target),
2981 value = $input.val(),
2982 $selectorLabel = this.$el.find('label[for="' + $input.attr('id') + '"]');
2983 $selectorLabel.toggleClass('selected');
2984
2985 if ($input.prop('checked')) {
2986 this.selectedFields.push(value);
2987 } else {
2988 this.selectedFields = _.without(this.selectedFields, value);
2989 }
2990 },
2991 insertFields: function insertFields(e) {
2992 var // self = this,
2993 $button = this.$(e.target);
2994 $button.addClass('sui-button-onload');
2995 this.trigger('fields:added', this.selectedFields);
2996 setTimeout(function () {
2997 $button.removeClass('sui-button-onload');
2998 SUI.closeModal();
2999 }, 300);
3000 },
3001 closeModalActions: function closeModalActions() {
3002 this.undelegateEvents();
3003 this.stopListening();
3004 var selectedFieldsNames = Object.keys(this.model.get('form_elements'));
3005 var selector = '.sui-box-selector'; // Don't deselect reCAPTCHA if it's added.
3006
3007 if (selectedFieldsNames.includes('recaptcha')) {
3008 selector += ':not(.hustle-optin-insert-field-label--recaptcha)';
3009 } // Don't deselect GDPR if it's added.
3010
3011
3012 if (selectedFieldsNames.includes('gdpr')) {
3013 selector += ':not(.hustle-optin-insert-field-label--gdpr)';
3014 }
3015
3016 var $label = this.$el.find(selector),
3017 $input = $label.find('input');
3018 setTimeout(function () {
3019 // Uncheck options
3020 $label.removeClass('selected');
3021 $input.prop('checked', false);
3022 $input[0].checked = false;
3023 }, 200);
3024 }
3025 });
3026 });
3027 Hustle.define('Modals.PublishFlow', function ($) {
3028 'use strict';
3029
3030 return Backbone.View.extend({
3031 el: '#hustle-dialog--publish-flow',
3032 initialize: function initialize() {},
3033 open: function open() {
3034 var $icon = this.$('#hustle-dialog--publish-flow-icon'); // We're adding this via js to be able to use the php template, which isn't handling icons like this as of now.
3035
3036 if (!$icon.length) {
3037 $icon = $('<span id="hustle-dialog--publish-flow-icon" class="sui-lg" aria-hidden="true" style="margin-bottom: 20px;"></span>');
3038 $icon.insertBefore('#hustle-dialog--publish-flow-title');
3039 }
3040
3041 this.setLoading(); // Remove max-height from bottom image.
3042
3043 this.$('.sui-box').find('.sui-image').css('max-height', '');
3044 SUI.openModal('hustle-dialog--publish-flow', $('.hustle-publish-button')[0], this.$('.hustle-modal-close')[0], true);
3045 },
3046 setLoading: function setLoading() {
3047 var $icon = this.$('#hustle-dialog--publish-flow-icon'),
3048 $content = this.$('.sui-box'),
3049 $closeButton = this.$('.sui-box-header .hustle-modal-close'),
3050 $title = this.$('#hustle-dialog--publish-flow-title'),
3051 $desc = this.$('#hustle-dialog--publish-flow-description'),
3052 $scheduleNotice = this.$('#hustle-published-notice-with-schedule-end');
3053 $icon.removeClass('sui-icon-' + $content.data('ready-icon'));
3054 $icon.addClass('sui-icon-' + $content.data('loading-icon'));
3055
3056 if ('loader' === $content.attr('data-loading-icon')) {
3057 $icon.addClass('sui-loading');
3058 }
3059
3060 $title.text($content.data('loading-title'));
3061 $desc.text($content.data('loading-desc'));
3062 $scheduleNotice.hide();
3063 $closeButton.hide();
3064 },
3065 setPublished: function setPublished(isScheduled, hasEnd) {
3066 var $icon = this.$('#hustle-dialog--publish-flow-icon'),
3067 $content = this.$('.sui-box'),
3068 $closeButton = this.$('.sui-box-header .hustle-modal-close'),
3069 $title = this.$('#hustle-dialog--publish-flow-title'),
3070 $desc = this.$('#hustle-dialog--publish-flow-description'),
3071 $scheduleNotice = this.$('#hustle-published-notice-with-schedule-end'),
3072 descText = !isScheduled ? $content.data('ready-desc') : $content.data('ready-desc-alt');
3073 $icon.removeClass('sui-icon-' + $content.data('loading-icon'));
3074 $icon.addClass('sui-icon-' + $content.data('ready-icon'));
3075
3076 if ('loader' === $content.attr('data-loading-icon')) {
3077 $icon.removeClass('sui-loading');
3078 } // Display the notice for when the schedule has an end.
3079
3080
3081 if (isScheduled && hasEnd) {
3082 $content.find('.sui-image').css('max-height', '120px');
3083 $scheduleNotice.show();
3084 } else {
3085 $scheduleNotice.hide();
3086 }
3087
3088 $title.text($content.data('ready-title'));
3089 $desc.text(descText); // Focus ready title
3090 // This will help screen readers know when module has been published
3091
3092 $title.focus();
3093 $closeButton.show();
3094 }
3095 });
3096 });
3097 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
3098
3099 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3100
3101 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
3102
3103 Hustle.define('Modals.Visibility_Conditions', function ($) {
3104 'use strict';
3105
3106 return Backbone.View.extend({
3107 el: '#hustle-dialog--visibility-options',
3108 selectedConditions: [],
3109 opts: {
3110 groupId: 0,
3111 conditions: []
3112 },
3113 events: {
3114 'click .sui-box-selector input': 'selectConditions'
3115 },
3116 initialize: function initialize(options) {
3117 $('#hustle-add-conditions').off('click').on('click', $.proxy(this.addConditions, this));
3118 this.opts = _.extend({}, this.opts, options);
3119 this.selectedConditions = this.opts.conditions;
3120 this.$('.hustle-visibility-condition-option').prop('checked', false).prop('disabled', false);
3121
3122 var _iterator = _createForOfIteratorHelper(this.selectedConditions),
3123 _step;
3124
3125 try {
3126 for (_iterator.s(); !(_step = _iterator.n()).done;) {
3127 var conditionId = _step.value;
3128 this.$('#hustle-condition--' + conditionId).prop('checked', true).prop('disabled', true);
3129 }
3130 } catch (err) {
3131 _iterator.e(err);
3132 } finally {
3133 _iterator.f();
3134 }
3135 },
3136 selectConditions: function selectConditions(e) {
3137 var $input = this.$(e.target),
3138 $selectorLabel = this.$el.find('label[for="' + $input.attr('id') + '"]'),
3139 value = $input.val();
3140 $selectorLabel.toggleClass('selected');
3141
3142 if ($input.prop('checked')) {
3143 this.selectedConditions.push(value);
3144 } else {
3145 this.selectedConditions = _.without(this.selectedConditions, value);
3146 }
3147 },
3148 addConditions: function addConditions(e) {
3149 var me = this,
3150 $button = this.$(e.target);
3151 $button.addClass('sui-button-onload');
3152 this.trigger('conditions:added', {
3153 groupId: this.opts.groupId,
3154 conditions: this.selectedConditions
3155 });
3156 setTimeout(function () {
3157 // Hide dialog
3158 SUI.closeModal();
3159 $button.removeClass('sui-button-onload');
3160 me.undelegateEvents();
3161 }, 500);
3162 }
3163 });
3164 });
3165 function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
3166
3167 function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3168
3169 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3170
3171 function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
3172
3173 function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
3174
3175 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
3176
3177 /* global Chart */
3178 (function ($) {
3179 'use strict';
3180
3181 Optin.listingBase = Hustle.View.extend({
3182 el: '.sui-wrap-hustle',
3183 logShown: false,
3184 moduleType: '',
3185 previewView: null,
3186 _events: {
3187 // Modals.
3188 'click .hustle-create-module': 'openCreateModal',
3189 'click .hustle-manage-tracking-button': 'openManageTrackingModal',
3190 'click .hustle-import-module-button': 'openImportModal',
3191 'click .hustle-upgrade-modal-button': 'openUpgradeModal',
3192 // Modules' actions.
3193 'click .hustle-single-module-button-action': 'handleSingleModuleAction',
3194 'click .hustle-preview-module-button': 'previewModule',
3195 // Bulk actions.
3196 'click form.sui-bulk-actions .hustle-bulk-apply-button': 'bulkActionCheck',
3197 'click #hustle-dialog--delete .hustle-delete': 'bulkActionSend',
3198 'click #hustle-bulk-action-reset-tracking-confirmation .hustle-delete': 'bulkActionSend',
3199 // Utilities.
3200 'click .sui-accordion-item-action .hustle-onload-icon-action': 'addLoadingIconToActionsButton'
3201 },
3202 initialize: function initialize(opts) {
3203 this.events = $.extend(true, {}, this.events, this._events);
3204 this.delegateEvents();
3205 this.moduleType = opts.moduleType;
3206 var newModuleModal = Hustle.get('Modals.New_Module'),
3207 importModal = Hustle.get('Modals.ImportModule');
3208 this.newModuleModal = new newModuleModal(this.moduleType, this.getPreviewView());
3209 this.ImportModal = new importModal(); // Why this doesn't work when added in events
3210
3211 $('.sui-accordion-item-header').on('click', $.proxy(this.openTrackingChart, this)); // Open the tracking chart when the class is present. Used when coming from 'view tracking' in Dashboard.
3212
3213 if ($('.hustle-display-chart').length) {
3214 this.openTrackingChart($('.hustle-display-chart'));
3215 }
3216
3217 this.doActionsBasedOnUrl();
3218 },
3219 doActionsBasedOnUrl: function doActionsBasedOnUrl() {
3220 // Display the "Create module" dialog.
3221 if ('true' === Module.Utils.getUrlParam('create-module')) {
3222 setTimeout(function () {
3223 $('.hustle-create-module').trigger('click');
3224 }, 100);
3225 } // Display "Upgrade modal".
3226
3227
3228 if ('true' === Module.Utils.getUrlParam('requires-pro')) {
3229 var self = this;
3230 setTimeout(function () {
3231 return self.openUpgradeModal();
3232 }, 100);
3233 } // Display notice based on URL parameters.
3234
3235
3236 if (Module.Utils.getUrlParam('show-notice')) {
3237 var status = 'success' === Module.Utils.getUrlParam('show-notice') ? 'success' : 'error',
3238 notice = Module.Utils.getUrlParam('notice'),
3239 message = notice && 'undefined' !== optinVars.messages[notice] ? optinVars.messages[notice] : Module.Utils.getUrlParam('notice-message'),
3240 closeTimeParam = Module.Utils.getUrlParam('notice-close', null),
3241 closeTime = 'false' === closeTimeParam ? false : closeTimeParam;
3242
3243 if ('undefined' !== typeof message && message.length) {
3244 Module.Notification.open(status, message, closeTime);
3245 }
3246 } // View module stats.
3247
3248
3249 var viewId = Module.Utils.getUrlParam('view-stats');
3250
3251 if (viewId) {
3252 var $header = $('.hustle-list .sui-accordion-item-header[data-id="' + viewId + '"]');
3253 $header.trigger('click');
3254 $('html, body').animate({
3255 scrollTop: $header.closest('.sui-accordion-item').offset().top - 40
3256 }, 1000);
3257 }
3258 },
3259 handleSingleModuleAction: function handleSingleModuleAction(e) {
3260 this.addLoadingIcon(e);
3261 Module.handleActions.initAction(e, 'listing', this);
3262 },
3263 previewModule: function previewModule(e) {
3264 e.preventDefault();
3265 var $button = $(e.currentTarget);
3266 this.getPreviewView().open($button.data('id'), $button.data('type'), $button, {
3267 module_name: $button.data('name')
3268 });
3269 },
3270 getPreviewView: function getPreviewView() {
3271 if (!this.previewView) {
3272 var previewView = Hustle.get('Modals.Preview');
3273 this.previewView = new previewView();
3274 }
3275
3276 return this.previewView;
3277 },
3278 openTrackingChart: function openTrackingChart(e) {
3279 var flexHeader = '';
3280
3281 if (e.target) {
3282 if ($(e.target).closest('.sui-accordion-item-action').length) {
3283 return true;
3284 }
3285
3286 e.preventDefault();
3287 e.stopPropagation();
3288 flexHeader = $(e.currentTarget);
3289 } else {
3290 flexHeader = e;
3291 }
3292
3293 var self = this,
3294 flexItem = flexHeader.parent();
3295 var flexChart = flexItem.find('.sui-chartjs-animated');
3296
3297 if (flexItem.hasClass('sui-accordion-item--disabled')) {
3298 flexItem.removeClass('sui-accordion-item--open');
3299 } else if (flexItem.hasClass('sui-accordion-item--open')) {
3300 flexItem.removeClass('sui-accordion-item--open');
3301 } else {
3302 flexItem.addClass('sui-accordion-item--open');
3303 }
3304
3305 flexItem.find('.sui-accordion-item-data').addClass('sui-onload');
3306 flexChart.removeClass('sui-chartjs-loaded');
3307
3308 if (flexItem.hasClass('sui-accordion-item--open')) {
3309 var id = flexHeader.data('id'),
3310 nonce = flexHeader.data('nonce'),
3311 data = {
3312 id: id,
3313 _ajax_nonce: nonce,
3314 action: 'hustle_tracking_data'
3315 };
3316 $.ajax({
3317 url: ajaxurl,
3318 type: 'POST',
3319 data: data,
3320 success: function success(resp) {
3321 if (resp.success && resp.data) {
3322 flexItem.find('.sui-accordion-item-body').html(resp.data.html);
3323 self.trackingChart.init(flexItem, resp.data.charts_data);
3324 flexChart = flexItem.find('.sui-chartjs-animated'); // Init tabs
3325
3326 SUI.suiTabs();
3327 }
3328
3329 flexItem.find('.sui-accordion-item-data').removeClass('sui-onload');
3330 flexChart.addClass('sui-chartjs-loaded');
3331 },
3332 error: function error() {
3333 flexItem.find('.sui-accordion-item-data').removeClass('sui-onload');
3334 flexChart.addClass('sui-chartjs-loaded');
3335 }
3336 });
3337 }
3338 },
3339 getChecked: function getChecked(type) {
3340 var query = '.sui-wrap-hustle .sui-accordion-item-title input[type=checkbox]';
3341
3342 if ('checked' === type) {
3343 query += ':checked';
3344 }
3345
3346 return $(query);
3347 },
3348 bulkActionCheck: function bulkActionCheck(e) {
3349 var $this = $(e.target),
3350 value = $this.closest('.hustle-bulk-actions-container').find('select[name="hustle_action"] option:selected').val(),
3351 //$( 'select option:selected', $this.closest( '.sui-box' ) ).val(),
3352 elements = this.getChecked('checked');
3353
3354 if (0 === elements.length || 'undefined' === value) {
3355 return false;
3356 }
3357
3358 if ('delete' === value) {
3359 var data = {
3360 actionClass: 'hustle-delete',
3361 action: 'delete',
3362 title: $this.data('delete-title'),
3363 description: $this.data('delete-description')
3364 };
3365 Module.deleteModal.open(data, $this[0]);
3366 return false;
3367 } else if ('reset-tracking' === value) {
3368 var _data = {
3369 actionClass: 'hustle-delete',
3370 action: 'reset-tracking',
3371 title: $this.data('reset-title'),
3372 description: $this.data('reset-description')
3373 };
3374 Module.deleteModal.open(_data, $this[0]);
3375 return false;
3376 }
3377
3378 this.bulkActionSend(e, value);
3379 },
3380 bulkActionSend: function bulkActionSend(e, action) {
3381 e.preventDefault();
3382 this.addLoadingIcon(e);
3383 var value = action ? action : $(e.target).data('hustle-action'),
3384 elements = this.getChecked('checked');
3385
3386 if (0 === elements.length) {
3387 return false;
3388 }
3389
3390 var ids = [];
3391 $.each(elements, function () {
3392 ids.push($(this).val());
3393 });
3394 var button = $('.sui-bulk-actions .hustle-bulk-apply-button'),
3395 data = {
3396 ids: ids,
3397 hustle: value,
3398 type: button.data('type'),
3399 _ajax_nonce: button.data('nonce'),
3400 action: 'hustle_listing_bulk'
3401 };
3402 $.ajax({
3403 url: ajaxurl,
3404 type: 'POST',
3405 data: data,
3406 success: function success(resp) {
3407 if (resp.success) {
3408 location.reload();
3409 } else {
3410 SUI.closeModal(); //show error notice
3411 }
3412 }
3413 });
3414 },
3415 addLoadingIcon: function addLoadingIcon(e) {
3416 var $button = $(e.currentTarget);
3417
3418 if ($button.hasClass('sui-button')) {
3419 $button.addClass('sui-button-onload');
3420 }
3421 },
3422 addLoadingIconToActionsButton: function addLoadingIconToActionsButton(e) {
3423 var $actionButton = $(e.currentTarget),
3424 $mainButton = $actionButton.closest('.sui-accordion-item-action').find('.sui-dropdown-anchor');
3425 $mainButton.addClass('sui-button-onload');
3426 },
3427 // ===================================
3428 // Modals
3429 // ===================================
3430 openCreateModal: function openCreateModal(e) {
3431 if (false === $(e.currentTarget).data('enabled')) {
3432 this.openUpgradeModal();
3433 } else {
3434 this.newModuleModal.open();
3435 }
3436 },
3437 openUpgradeModal: function openUpgradeModal(e) {
3438 var focusOnClose = this.$('#hustle-create-new-module')[0];
3439
3440 if (e) {
3441 e.preventDefault();
3442 e.stopPropagation();
3443 focusOnClose = e.currentTarget;
3444 }
3445
3446 $('.sui-button-onload').removeClass('sui-button-onload');
3447
3448 if (!$('#hustle-modal--upgrade-to-pro').length) {
3449 return;
3450 }
3451
3452 SUI.openModal('hustle-modal--upgrade-to-pro', focusOnClose, 'hustle-button--upgrade-to-pro', true);
3453 },
3454 openImportModal: function openImportModal(e) {
3455 var $this = $(e.currentTarget);
3456
3457 if (false === $this.data('enabled')) {
3458 this.openUpgradeModal();
3459 } else {
3460 this.ImportModal.open(e);
3461 }
3462 },
3463
3464 /**
3465 * The "are you sure?" modal from before resetting the tracking data of modules.
3466 *
3467 * @since 4.0.0
3468 * @param {Event} e Event.
3469 */
3470 openManageTrackingModal: function openManageTrackingModal(e) {
3471 var template = Optin.template('hustle-manage-tracking-form-tpl'),
3472 $modal = $('#hustle-dialog--manage-tracking'),
3473 $button = $(e.currentTarget),
3474 moduleId = $button.data('module-id'),
3475 data = {
3476 //moduleID: $button.data( 'module-id' ),
3477 enabledTrackings: $button.data('tracking-types').split(',')
3478 };
3479 $modal.find('#hustle-manage-tracking-form-container').html(template(data));
3480 $modal.find('#hustle-button-toggle-tracking-types').data('module-id', moduleId);
3481 SUI.openModal('hustle-dialog--manage-tracking', $button, 'hustle-module-tracking--inline', true);
3482 },
3483 // ===================================
3484 // Tracking charts
3485 // ===================================
3486
3487 /**
3488 * Renders the module's charts in the listing pages.
3489 * It also handles the view when the 'conversions type' select changes.
3490 *
3491 * @since 4.0.4
3492 */
3493 trackingChart: {
3494 chartsData: {},
3495 theCharts: {},
3496 init: function init($container, chartsData) {
3497 var _this = this;
3498
3499 $container.find('select.hustle-conversion-type').each(function (i, el) {
3500 SUI.select.init($(el));
3501 $(el).on('change.select2', function (e) {
3502 return _this.conversionTypeChanged(e, $container);
3503 });
3504 });
3505 this.chartsData = chartsData;
3506 Object.values(chartsData).forEach(function (chart) {
3507 return _this.updateChart(chart);
3508 });
3509 },
3510 conversionTypeChanged: function conversionTypeChanged(e, $container) {
3511 var $select = $(e.currentTarget),
3512 conversionType = $select.val(),
3513 moduleSubType = $select.data('moduleType'),
3514 subTypeChart = this.chartsData[moduleSubType],
3515 $conversionsCount = $container.find(".hustle-tracking-".concat(moduleSubType, "-conversions-count")),
3516 $conversionsRate = $container.find(".hustle-tracking-".concat(moduleSubType, "-conversions-rate")); // Update the number for the conversions count and conversion rate at the top of the chart.
3517
3518 $conversionsCount.text(subTypeChart[conversionType].conversions_count);
3519 $conversionsRate.text(subTypeChart[conversionType].conversion_rate + '%');
3520 this.updateChart(subTypeChart, conversionType, false);
3521 },
3522 updateChart: function updateChart(chart) {
3523 var conversionType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
3524 var render = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
3525 var views = chart.views,
3526 submissions = chart[conversionType].conversions,
3527 datasets = [{
3528 label: 'Submissions',
3529 data: submissions,
3530 backgroundColor: ['#E1F6FF'],
3531 borderColor: ['#17A8E3'],
3532 borderWidth: 2,
3533 pointRadius: 0,
3534 pointHitRadius: 20,
3535 pointHoverRadius: 5,
3536 pointHoverBorderColor: '#17A8E3',
3537 pointHoverBackgroundColor: '#17A8E3'
3538 }, {
3539 label: 'Views',
3540 data: views,
3541 backgroundColor: ['#F8F8F8'],
3542 borderColor: ['#DDDDDD'],
3543 borderWidth: 2,
3544 pointRadius: 0,
3545 pointHitRadius: 20,
3546 pointHoverRadius: 5,
3547 pointHoverBorderColor: '#DDDDDD',
3548 pointHoverBackgroundColor: '#DDDDDD'
3549 }]; // The chart was already created. Update it.
3550
3551 if ('undefined' !== typeof this.theCharts[chart.id]) {
3552 // The container has been re-rendered, so render the chart again.
3553 if (render) {
3554 this.theCharts[chart.id].destroy();
3555 this.createNewChart(chart, datasets);
3556 } else {
3557 // Just update the chart otherwise.
3558 this.theCharts[chart.id].data.datasets = datasets;
3559 this.theCharts[chart.id].update();
3560 }
3561 } else {
3562 this.createNewChart(chart, datasets);
3563 }
3564 },
3565 createNewChart: function createNewChart(chart, datasets) {
3566 var yAxesHeight = Math.max.apply(Math, _toConsumableArray(chart.views)) + 2;
3567 var chartContainer = document.getElementById(chart.id);
3568
3569 if (Math.max.apply(Math, _toConsumableArray(chart.views)) < Math.max.apply(Math, _toConsumableArray(chart.conversions))) {
3570 yAxesHeight = Math.max.apply(Math, _toConsumableArray(chart.conversions)) + 2;
3571 }
3572
3573 if (!chartContainer) {
3574 return;
3575 }
3576
3577 var days = chart.days,
3578 chartData = {
3579 labels: days,
3580 datasets: datasets
3581 };
3582 var chartOptions = {
3583 maintainAspectRatio: false,
3584 legend: {
3585 display: false
3586 },
3587 scales: {
3588 xAxes: [{
3589 display: false,
3590 gridLines: {
3591 color: 'rgba(0, 0, 0, 0)'
3592 }
3593 }],
3594 yAxes: [{
3595 display: false,
3596 gridLines: {
3597 color: 'rgba(0, 0, 0, 0)'
3598 },
3599 ticks: {
3600 beginAtZero: false,
3601 min: 0,
3602 max: yAxesHeight,
3603 stepSize: 1
3604 }
3605 }]
3606 },
3607 elements: {
3608 line: {
3609 tension: 0
3610 },
3611 point: {
3612 radius: 0.5
3613 }
3614 },
3615 tooltips: {
3616 custom: function custom(tooltip) {
3617 if (!tooltip) {
3618 return;
3619 } // Disable displaying the color box
3620
3621
3622 tooltip.displayColors = false;
3623 },
3624 callbacks: {
3625 title: function title(tooltipItem) {
3626 if (0 === tooltipItem[0].datasetIndex) {
3627 return optinVars.labels.submissions.replace('%d', tooltipItem[0].yLabel); // + ' Submissions';
3628 } else if (1 === tooltipItem[0].datasetIndex) {
3629 return optinVars.labels.views.replace('%d', tooltipItem[0].yLabel); //+ ' Views';
3630 }
3631 },
3632 label: function label(tooltipItem) {
3633 return tooltipItem.xLabel;
3634 },
3635 // Set label text color
3636 labelTextColor: function labelTextColor() {
3637 return '#AAAAAA';
3638 }
3639 }
3640 }
3641 };
3642 this.theCharts[chart.id] = new Chart(chartContainer, {
3643 type: 'line',
3644 fill: 'start',
3645 data: chartData,
3646 options: chartOptions
3647 });
3648 }
3649 }
3650 });
3651 })(jQuery);
3652 Hustle.define('Modals.New_Module', function ($) {
3653 'use strict';
3654
3655 return Backbone.View.extend({
3656 el: '#hustle-dialog--create-new-module',
3657 previewView: null,
3658 moduleType: '',
3659 moduleName: false,
3660 moduleMode: 'optin',
3661 moduleTemplate: 'none',
3662 $moveForwardButton: null,
3663 data: {},
3664 mainDialogLabelId: 'hustle-create-new-module-dialog-label',
3665 mainDialogDescriptionId: 'hustle-create-new-module-dialog-description',
3666 events: {
3667 'keydown input[name="name"]': 'nameChanged',
3668 'click #hustle-create-module': 'createModule',
3669 'change input[name="mode"]': 'modeChanged',
3670 'click #hustle-go-to-templates-button': 'goToTemplatesStep',
3671 'click .hustle-template-select-button': 'createNonSshare',
3672 'click .hustle-template-preview-button': 'previewTemplate',
3673 'click .hustle-modal-go-back': 'goToStepOne'
3674 },
3675 initialize: function initialize(moduleType, previewView) {
3676 this.moduleType = moduleType;
3677 this.previewView = previewView;
3678 var nextButtonSelector = 'social_sharing' !== this.moduleType ? '#hustle-go-to-templates-button' : '#hustle-create-module';
3679 this.$moveForwardButton = this.$(nextButtonSelector);
3680 },
3681 open: function open() {
3682 SUI.openModal('hustle-dialog--create-new-module', 'hustle-create-new-module', 'hustle-module-name');
3683
3684 if ('social_sharing' !== this.moduleType) {
3685 this.goToStepOne();
3686 }
3687 },
3688 nameChanged: function nameChanged(e) {
3689 var _this = this;
3690
3691 setTimeout(function () {
3692 _this.$('.sui-error-message').hide();
3693
3694 var value = $(e.currentTarget).val().trim();
3695
3696 if (0 === value.length) {
3697 _this.moduleName = false;
3698
3699 _this.$moveForwardButton.prop('disabled', true);
3700
3701 _this.$('#error-empty-name').closest('.sui-form-field').addClass('sui-form-field-error');
3702
3703 _this.$('#error-empty-name').show();
3704 } else {
3705 _this.moduleName = value;
3706
3707 _this.$moveForwardButton.prop('disabled', false);
3708
3709 _this.$('#error-empty-name').closest('.sui-form-field').removeClass('sui-form-field-error');
3710
3711 _this.$('#error-empty-name').hide();
3712 }
3713 }, 300);
3714 },
3715 modeChanged: function modeChanged(e) {
3716 var value = $(e.currentTarget).val();
3717 this.moduleMode = value;
3718 },
3719 goToStepOne: function goToStepOne(e) {
3720 var animation = e ? 'back' : null;
3721 this.$el.attr('aria-labelledby', this.mainDialogLabelId);
3722 this.$el.attr('aria-describedby', this.mainDialogDescriptionId);
3723 SUI.slideModal('hustle-create-new-module-step-1', 'hustle-module-name', animation);
3724 },
3725 goToTemplatesStep: function goToTemplatesStep(e) {
3726 e.preventDefault();
3727
3728 if (this.isNameValid() && this.isModeValid()) {
3729 var stepId = 'optin' === this.moduleMode ? 'optin-templates' : 'informational-templates',
3730 stepLabelId = "hustle-create-new-module-dialog-step-".concat(stepId, "-label"),
3731 stepDescriptionId = "hustle-create-new-module-dialog-step-".concat(stepId, "-description");
3732 this.$el.attr('aria-labelledby', stepLabelId);
3733 this.$el.attr('aria-describedby', stepDescriptionId);
3734 SUI.slideModal("hustle-create-new-module-step-".concat(stepId), this.$el.find("#hustle-create-new-module-step-".concat(stepId, " .hustle-template-option--none"))[0], 'next');
3735 }
3736 },
3737 isNameValid: function isNameValid() {
3738 return false !== this.moduleName;
3739 },
3740 isModeValid: function isModeValid() {
3741 return 'optin' === this.moduleMode || 'informational' === this.moduleMode;
3742 },
3743 createNonSshare: function createNonSshare(e) {
3744 var selectedTemplate = $(e.currentTarget).data('template');
3745 this.moduleTemplate = selectedTemplate;
3746 this.createModule(e);
3747 },
3748 createModule: function createModule(e) {
3749 var nonce = this.$el.data('nonce'),
3750 errorMessage = this.$el.data('error-message'),
3751 $button = $(e.currentTarget),
3752 data = {
3753 module_name: this.moduleName,
3754 module_type: this.moduleType,
3755 module_mode: this.moduleMode,
3756 module_template: this.moduleTemplate,
3757 action: 'hustle_create_new_module',
3758 _ajax_nonce: nonce
3759 };
3760 $button.addClass('sui-button-onload');
3761 $.ajax({
3762 url: ajaxurl,
3763 type: 'POST',
3764 data: data
3765 }).done(function (res) {
3766 // Go to the wizard of this type of module on success, or listing page if limits were reached.
3767 if (res && res.data && res.data.redirect_url) {
3768 window.location.replace(res.data.redirect_url);
3769 } else {
3770 $button.removeClass('sui-button-onload');
3771 Module.Notification.open('error', errorMessage, false);
3772 }
3773 }).fail(function () {
3774 $button.removeClass('sui-button-onload');
3775 Module.Notification.open('error', errorMessage, false);
3776 });
3777 },
3778 previewTemplate: function previewTemplate(e) {
3779 var $button = $(e.currentTarget);
3780 this.previewView.open(0, $button.data('module-type'), $button, {
3781 module_name: $button.data('name'),
3782 module_type: $button.data('module-type'),
3783 template_name: $button.data('template'),
3784 template_mode: $button.data('module-mode')
3785 });
3786 }
3787 });
3788 });
3789 Hustle.define('Modals.ImportModule', function ($) {
3790 'use strict';
3791
3792 return Backbone.View.extend({
3793 el: '#hustle-dialog--import',
3794 events: {
3795 'change #hustle-import-file-input': 'selectUploadFile',
3796 'click .sui-upload-file': 'changeFile',
3797 'click .sui-upload-file button': 'resetUploadFile',
3798 'click .hustle-import-check-all-checkbox': 'checkAll',
3799 'change .hustle-module-meta-checkbox': 'uncheckAllOption'
3800 },
3801 initialize: function initialize() {},
3802 open: function open(e) {
3803 var $this = $(e.currentTarget),
3804 moduleId = $this.data('module-id'),
3805 template = Optin.template('hustle-import-modal-options-tpl'),
3806 $importDialog = $('#hustle-dialog--import'),
3807 $submitButton = $importDialog.find('#hustle-import-module-submit-button'),
3808 isNew = 'undefined' === typeof moduleId,
3809 templateData = {
3810 isNew: isNew,
3811 isOptin: 'optin' === $this.data('module-mode') // Always "false" when importing into a new module.
3812
3813 };
3814 $importDialog.find('#hustle-import-modal-options').html(template(templateData));
3815
3816 if (isNew) {
3817 $submitButton.removeAttr('data-module-id'); // Bind the tabs again with their SUI actions.
3818 // Only the modal for importing a new module has tabs.
3819
3820 SUI.tabs();
3821 $importDialog.find('.sui-tab-item').on('click', function () {
3822 var $tab = $(this),
3823 $radio = $('#' + $tab.data('label-for'));
3824 $radio.click();
3825 });
3826 } else {
3827 $submitButton.attr('data-module-id', moduleId);
3828 }
3829
3830 SUI.openModal('hustle-dialog--import', e.currentTarget, 'hustle-import-file-input', true);
3831 },
3832 selectUploadFile: function selectUploadFile(e) {
3833 e.preventDefault();
3834 var $this = $(e.target),
3835 value = $this.val().replace(/C:\\fakepath\\/i, ''); // Hide previous error.
3836
3837 SUI.closeNotice('hustle-dialog--import-error-notice');
3838
3839 if (value) {
3840 $('.sui-upload-file span:first').text(value);
3841 $('.sui-upload').addClass('sui-has_file');
3842 $('#hustle-import-module-submit-button').prop('disabled', false);
3843 } else {
3844 $('.sui-upload').removeClass('sui-has_file');
3845 $('.sui-upload-file span:first').text('');
3846 $('#hustle-import-module-submit-button').prop('disabled', true);
3847 }
3848 },
3849 resetUploadFile: function resetUploadFile(e) {
3850 e.stopPropagation();
3851 $('#hustle-import-file-input').val('').trigger('change');
3852 },
3853 changeFile: function changeFile() {
3854 $('#hustle-import-file-input').trigger('click');
3855 },
3856 checkAll: function checkAll(e) {
3857 var $this = $(e.currentTarget),
3858 value = $this.is(':checked'),
3859 $container = $this.closest('.hui-inputs-list'),
3860 $checkboxes = $container.find('input.hustle-module-meta-checkbox:not(.hustle-import-check-all-checkbox)');
3861 $checkboxes.prop('checked', value);
3862 },
3863 uncheckAllOption: function uncheckAllOption(e) {
3864 var $this = $(e.currentTarget),
3865 $container = $this.closest('.hui-inputs-list'),
3866 $allCheckbox = $container.find('.hustle-import-check-all-checkbox'),
3867 isAllChecked = $allCheckbox.is(':checked');
3868
3869 if (!isAllChecked) {
3870 return;
3871 }
3872
3873 $allCheckbox.prop('checked', false);
3874 }
3875 });
3876 });
3877 function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
3878
3879 function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3880
3881 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3882
3883 function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); }
3884
3885 function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
3886
3887 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
3888
3889 Hustle.define('Mixins.Model_Updater', function ($) {
3890 'use strict';
3891
3892 return {
3893 initMix: function initMix() {
3894 this.events = _.extend({}, this.events, this._events);
3895 this.delegateEvents();
3896 },
3897 _events: {
3898 'change textarea': '_updateText',
3899 'change input[type="text"]': '_updateText',
3900 'change input[type="url"]': '_updateText',
3901 'change input[type="hidden"]': '_updateText',
3902 'change input[type="number"]': '_updateText',
3903 'change input[type="checkbox"]': '_updateCheckbox',
3904 'change input[type=radio]': '_updateRadios',
3905 'change select': '_updateSelect'
3906 },
3907 _updateText: function _updateText(e) {
3908 var $this = $(e.target),
3909 attr = $this.data('attribute'),
3910 model = this[$this.data('model') || 'model'],
3911 opts = _.isTrue($this.data('silent')) ? {
3912 silent: true
3913 } : {};
3914
3915 if (model && attr) {
3916 e.stopPropagation();
3917 model.set.call(model, attr, e.target.value, opts);
3918 }
3919 },
3920 _updateCheckbox: function _updateCheckbox(e) {
3921 var $this = $(e.target),
3922 attr = $this.data('attribute'),
3923 value = $this.val(),
3924 model = this[$this.data('model') || 'model'],
3925 opts = _.isTrue($this.data('silent')) ? {
3926 silent: true
3927 } : {};
3928
3929 if (model && attr) {
3930 e.stopPropagation(); // If the checkboxes values should behave as an array, instead of as an on/off toggle.
3931
3932 if ('on' !== value) {
3933 var newValue = [];
3934 var current = model.get.call(model, attr);
3935
3936 if ($this.is(':checked')) {
3937 newValue = _toConsumableArray(current);
3938 newValue.push(value);
3939 } else {
3940 newValue = _.without(current, value);
3941 }
3942
3943 model.set.call(model, attr, newValue, opts);
3944 } else {
3945 model.set.call(model, attr, $this.is(':checked') ? '1' : '0', opts);
3946 }
3947 }
3948 },
3949 _updateRadios: function _updateRadios(e) {
3950 var $this = $(e.target),
3951 attribute = $this.data('attribute'),
3952 model = this[$this.data('model') || 'model'],
3953 opts = _.isTrue($this.data('silent')) ? {
3954 silent: true
3955 } : {};
3956
3957 if (model && attribute) {
3958 e.stopPropagation();
3959 model.set.call(model, attribute, e.target.value, opts);
3960 }
3961 },
3962 _updateSelect: function _updateSelect(e) {
3963 var $this = $(e.target),
3964 attr = $this.data('attribute'),
3965 model = this[$this.data('model') || 'model'],
3966 opts = _.isTrue($this.data('silent')) ? {
3967 silent: true
3968 } : {};
3969
3970 if (model && attr) {
3971 e.stopPropagation();
3972 model.set.call(model, attr, $this.val(), opts);
3973 }
3974 }
3975 };
3976 });
3977 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
3978
3979 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3980
3981 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
3982
3983 /* global moment, sprintf */
3984 Hustle.define('Mixins.Module_Settings', function ($) {
3985 'use strict';
3986
3987 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
3988 el: '#hustle-wizard-behaviour',
3989 events: {},
3990 triggersModel: null,
3991 init: function init(opts) {
3992 var self = this,
3993 Model = opts.BaseModel.extend({
3994 defaults: {},
3995 initialize: function initialize(data) {
3996 _.extend(this, data);
3997
3998 var Triggers = Hustle.get('Models.Trigger');
3999
4000 if (!(this.get('triggers') instanceof Backbone.Model)) {
4001 this.set('triggers', new Triggers(this.triggers), {
4002 silent: true
4003 });
4004 self.triggersModel = this.get('triggers');
4005 }
4006 }
4007 });
4008 this.model = new Model(optinVars.current.settings || {});
4009 this.moduleType = optinVars.current.data.module_type;
4010 var EditScheduleModalView = Hustle.get('Modals.EditSchedule');
4011 this.editScheduleView = new EditScheduleModalView({
4012 model: this.model
4013 });
4014 this.listenTo(this.model, 'change', this.viewChanged);
4015
4016 if ('embedded' !== this.moduleType) {
4017 this.listenTo(this.model.get('triggers'), 'change', this.triggersViewChanged);
4018 } // Called just to trigger the "view.rendered" action.
4019
4020
4021 this.render();
4022 },
4023 render: function render() {
4024 this.renderScheduleSection();
4025 this.editScheduleView.on('schedule:updated', $.proxy(this.renderScheduleSection, this)); // Order matters. Tags will show up in disabled accordions if calling 'toggleTriggerAccordionsDisabled' first.
4026
4027 this.initTriggerAccordionsTag();
4028 this.toggleTriggerAccordionsDisabled();
4029 },
4030 renderScheduleSection: function renderScheduleSection() {
4031 var _this = this;
4032
4033 var template = Optin.template('hustle-schedule-row-tpl'),
4034 $container = $('#hustle-schedule-row'),
4035 scheduleSettings = this.model.get('schedule'),
4036 data = Object.assign({}, scheduleSettings),
4037 strings = {
4038 startDate: '',
4039 startTime: '',
4040 endDate: '',
4041 endTime: '',
4042 activeDays: '',
4043 activeTime: ''
4044 };
4045 var hasFinished = false;
4046 data.is_schedule = this.model.get('is_schedule'); // eslint-disable-line camelcase
4047 // Here we'll build the strings dependent on the selected settings. Skip if scheduling is disabled.
4048
4049 if (data.is_schedule) {
4050 // Translated months and 'AM/PM' strings.
4051 var months = Object.assign({}, optinVars.schedule.months),
4052 meridiem = optinVars.schedule.meridiem; // Schedule start string. Skip if disabled.
4053
4054 if ('0' === data.not_schedule_start) {
4055 var stringDate = data.start_date.split('/'),
4056 month = months[stringDate[0] - 1],
4057 ampm = meridiem[data.start_meridiem_offset];
4058 strings.startDate = "".concat(stringDate[1], " ").concat(month, " ").concat(stringDate[2]);
4059 strings.startTime = "(".concat(data.start_hour, ":").concat(data.start_minute, " ").concat(ampm, ")");
4060 } // Schedule end string. Skip if disabled.
4061
4062
4063 if ('0' === data.not_schedule_end) {
4064 var _stringDate = data.end_date.split('/'),
4065 _month = months[_stringDate[0] - 1],
4066 _ampm = meridiem[data.end_meridiem_offset];
4067
4068 strings.endDate = "".concat(_stringDate[1], " ").concat(_month, " ").concat(_stringDate[2]);
4069 strings.endTime = "(".concat(data.end_hour, ":").concat(data.end_minute, " ").concat(_ampm, ")");
4070 hasFinished = this.isScheduleFinished(data);
4071 } // Selected weekdays string. Skip if 'every day' is selected.
4072
4073
4074 if ('week_days' === data.active_days) {
4075 var weekDays = optinVars.schedule.week_days,
4076 days = data.week_days.map(function (day) {
4077 return weekDays[day].toUpperCase();
4078 });
4079 strings.activeDays = days.join(', ');
4080 } // Per day start and end string. Skip if 'during all day' is enabled.
4081
4082
4083 if ('0' === data.is_active_all_day) {
4084 var startAmpm = meridiem[data.day_start_meridiem_offset],
4085 endAmpm = meridiem[data.day_end_meridiem_offset],
4086 dayStart = "".concat(data.day_start_hour, ":").concat(data.day_start_minute, " ").concat(startAmpm),
4087 dayEnd = "".concat(data.day_end_hour, ":").concat(data.day_end_minute, " ").concat(endAmpm);
4088 strings.activeTime = dayStart + ' - ' + dayEnd;
4089 }
4090 }
4091
4092 data.strings = strings;
4093 data.hasFinished = hasFinished;
4094 $container.html(template(data));
4095 $container.find('.hustle-button-open-schedule-dialog').on('click', function () {
4096 return _this.editScheduleView.open();
4097 });
4098 },
4099 isScheduleFinished: function isScheduleFinished(settings) {
4100 var currentTime = new Date().getTime();
4101 var timeToUse = settings.time_to_use,
4102 date = settings.end_date,
4103 hour = settings.end_hour,
4104 minute = settings.end_minute,
4105 ampm = settings.end_meridiem_offset,
4106 dateString = "".concat(date, " ").concat(hour, ":").concat(minute, " ").concat(ampm);
4107 var endTimestamp = false,
4108 utcOffset = false;
4109
4110 if ('server' === timeToUse) {
4111 utcOffset = optinVars.schedule.wp_gmt_offset;
4112 } else {
4113 var customTimezone = settings.custom_timezone; // It's using a manual UTC offset.
4114
4115 if (customTimezone.includes('UTC')) {
4116 var selectedOffset = customTimezone.replace('UTC', ''); // There's a timezone with the value "UTC".
4117
4118 utcOffset = selectedOffset.length ? parseFloat(selectedOffset) : 0;
4119 } else {
4120 var endMoment = moment.tz(dateString, 'MM/DD/YYYY hh:mm aa', customTimezone);
4121 endTimestamp = endMoment.format('x');
4122 }
4123 } // Calculate the timestamp with the manual offset.
4124
4125
4126 if (false === endTimestamp && false !== utcOffset) {
4127 var offset = 60 * utcOffset,
4128 sign = 0 < offset ? '+' : '-',
4129 abs = Math.abs(offset),
4130 formattedOffset = sprintf('%s%02d:%02d', sign, abs / 60, abs % 60);
4131
4132 var _endMoment = moment.parseZone(dateString + ' ' + formattedOffset, 'MM/DD/YYYY hh:mm a Z');
4133
4134 endTimestamp = _endMoment.format('x');
4135 } // Check if the end time already passed.
4136
4137
4138 if (currentTime > endTimestamp) {
4139 return true;
4140 }
4141
4142 return false;
4143 },
4144 viewChanged: function viewChanged(model) {
4145 var changed = model.changed;
4146
4147 if ('on_submit' in changed) {
4148 var $toggleDiv = this.$('#hustle-on-submit-delay-wrapper');
4149
4150 if ($toggleDiv.length) {
4151 if ('nothing' !== changed.on_submit) {
4152 $toggleDiv.removeClass('sui-hidden');
4153 } else {
4154 $toggleDiv.addClass('sui-hidden');
4155 }
4156 }
4157 }
4158 },
4159 triggersViewChanged: function triggersViewChanged(model) {
4160 var changed = model.changed,
4161 changedKey = Object.keys(changed)[0];
4162
4163 if ('trigger' === changedKey) {
4164 this.toggleTriggerAccordionsDisabled();
4165 } else {
4166 var $changedInput = this.$("[name=\"trigger_".concat(changedKey, "\"]")); // TODO: standardize this. Some inputs have "trigger_" prefixed to their key.
4167 // The sidetabs have "triggers.".
4168
4169 if (!$changedInput.length) {
4170 $changedInput = this.$("[name=\"triggers.".concat(changedKey, "\"]"));
4171 }
4172
4173 this.updateTriggerTag($changedInput.closest('.hustle-trigger-accordion-item'));
4174
4175 if ('on_scroll' === changedKey) {
4176 var scrollPast = this.triggersModel.get('on_scroll'),
4177 hide = 'scrolled' === scrollPast ? 'selector' : 'scrolled',
4178 show = 'scrolled' === scrollPast ? 'scrolled' : 'selector';
4179 this.$("#hustle-on-scroll--".concat(hide, "-toggle-wrapper")).addClass('sui-hidden');
4180 this.$("#hustle-on-scroll--".concat(show, "-toggle-wrapper")).removeClass('sui-hidden');
4181 }
4182 }
4183 },
4184 toggleTriggerAccordionsDisabled: function toggleTriggerAccordionsDisabled() {
4185 var triggerAccordions = this.$('.hustle-trigger-accordion-item'),
4186 selectedTriggers = this.triggersModel.get('trigger');
4187
4188 var toggleDisabled = function toggleDisabled($accordion) {
4189 var accordionTrigger = $accordion.data('trigger'),
4190 $optionTag = $accordion.find('.hustle-trigger-tag'),
4191 isActive = selectedTriggers.includes(accordionTrigger);
4192
4193 if (isActive) {
4194 $optionTag.show();
4195 $accordion.removeClass('sui-accordion-item--disabled');
4196 } else {
4197 $optionTag.hide();
4198 $accordion.addClass('sui-accordion-item--disabled');
4199 }
4200 };
4201
4202 var _iterator = _createForOfIteratorHelper(triggerAccordions),
4203 _step;
4204
4205 try {
4206 for (_iterator.s(); !(_step = _iterator.n()).done;) {
4207 var accordion = _step.value;
4208 toggleDisabled($(accordion));
4209 }
4210 } catch (err) {
4211 _iterator.e(err);
4212 } finally {
4213 _iterator.f();
4214 }
4215 },
4216 initTriggerAccordionsTag: function initTriggerAccordionsTag() {
4217 var triggerAccordions = this.$('.hustle-trigger-accordion-item');
4218
4219 var _iterator2 = _createForOfIteratorHelper(triggerAccordions),
4220 _step2;
4221
4222 try {
4223 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4224 var accordion = _step2.value;
4225 this.updateTriggerTag($(accordion));
4226 }
4227 } catch (err) {
4228 _iterator2.e(err);
4229 } finally {
4230 _iterator2.f();
4231 }
4232 },
4233 updateTriggerTag: function updateTriggerTag($triggerAccordion) {
4234 var trigger = $triggerAccordion.data('trigger'),
4235 $accordionTitle = $triggerAccordion.find('.sui-accordion-item-title'),
4236 $tag = $triggerAccordion.find('.hustle-trigger-tag');
4237
4238 if ($tag.length) {
4239 $tag.remove();
4240 }
4241
4242 var tagTextArray = this.getTriggerTagText(trigger);
4243
4244 var _iterator3 = _createForOfIteratorHelper(tagTextArray),
4245 _step3;
4246
4247 try {
4248 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
4249 var text = _step3.value;
4250
4251 if (text.trim().length) {
4252 var newTag = "<span class=\"sui-tag hustle-trigger-tag\">".concat(text, "</span>");
4253 $accordionTitle.append(newTag);
4254 }
4255 }
4256 } catch (err) {
4257 _iterator3.e(err);
4258 } finally {
4259 _iterator3.f();
4260 }
4261 },
4262 getTriggerTagText: function getTriggerTagText(trigger) {
4263 var tagText = [];
4264
4265 if ('scroll' === trigger) {
4266 tagText = this.getTriggerTagTextForScroll();
4267 } else if ('click' === trigger) {
4268 tagText = this.getTriggerTagTextForClick();
4269 } else {
4270 tagText = this.getTriggerTagTextForDelays(trigger);
4271 }
4272
4273 return tagText;
4274 },
4275 getTriggerTagTextForScroll: function getTriggerTagTextForScroll() {
4276 var scrollPast = this.triggersModel.get('on_scroll');
4277 var selected, tagBase;
4278
4279 if ('selector' === scrollPast) {
4280 selected = _.escape(this.triggersModel.get('on_scroll_css_selector'));
4281 tagBase = optinVars.triggers.scroll_element_tag;
4282 } else {
4283 selected = this.triggersModel.get('on_scroll_page_percent');
4284 tagBase = optinVars.triggers.scroll_percentage_tag;
4285 }
4286
4287 var text = tagBase.replace('{value}', selected),
4288 textArray = [text];
4289 return textArray;
4290 },
4291 getTriggerTagTextForClick: function getTriggerTagTextForClick() {
4292 var textArray = [],
4293 isElementEnabled = '1' === this.triggersModel.get('enable_on_click_element'),
4294 isShortcodeEnabled = '1' === this.triggersModel.get('enable_on_click_shortcode');
4295
4296 if (isElementEnabled) {
4297 var savedElement = _.escape(this.triggersModel.get('on_click_element'));
4298
4299 textArray.push(savedElement);
4300 }
4301
4302 if (isShortcodeEnabled) {
4303 textArray.push('[wd_hustle]');
4304 }
4305
4306 return textArray;
4307 },
4308 getTriggerTagTextForDelays: function getTriggerTagTextForDelays(trigger) {
4309 var triggerDelayOptions = {
4310 time: {
4311 time: 'on_time_delay',
4312 unit: 'on_time_unit'
4313 },
4314 exit_intent: {
4315 time: 'on_exit_intent_delayed_time',
4316 unit: 'on_exit_intent_delayed_unit'
4317 },
4318 adblock: {
4319 time: 'on_adblock_delay',
4320 unit: 'on_adblock_delay_unit'
4321 }
4322 },
4323 tagOptions = triggerDelayOptions[trigger];
4324 var selectedTime = this.triggersModel.get(tagOptions.time);
4325 var text = optinVars.triggers.immediately_tag;
4326
4327 if ('0' !== selectedTime) {
4328 var selectedUnit = this.triggersModel.get(tagOptions.unit),
4329 translatedUnit = optinVars.triggers[selectedUnit],
4330 delayTagBase = optinVars.triggers.delayed_tag;
4331 text = delayTagBase.replace('{time}', selectedTime).replace('{unit}', translatedUnit);
4332 }
4333
4334 var textArray = [text];
4335 return textArray;
4336 }
4337 });
4338 });
4339 Hustle.define('Mixins.Module_Content', function () {
4340 'use strict';
4341
4342 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
4343 el: '#hustle-wizard-content',
4344 events: {},
4345 init: function init(opts) {
4346 this.model = new opts.BaseModel(optinVars.current.content || {});
4347 this.moduleType = optinVars.current.data.module_type;
4348 this.listenTo(this.model, 'change', this.modelUpdated);
4349 this.render();
4350 },
4351 render: function render() {
4352 this.initImageUploaders();
4353
4354 if ('true' === Module.Utils.getUrlParam('new')) {
4355 Module.Notification.open('success', optinVars.messages.module_created, 10000);
4356 }
4357 },
4358 initImageUploaders: function initImageUploaders() {
4359 var MediaHolder = Hustle.get('imageUploader'),
4360 attrsWithImageUpload = ['feature_image', 'background_image'];
4361
4362 for (var _i = 0, _attrsWithImageUpload = attrsWithImageUpload; _i < _attrsWithImageUpload.length; _i++) {
4363 var attribute = _attrsWithImageUpload[_i];
4364 var $wrapper = this.$('#hustle-choose-' + attribute);
4365
4366 if ($wrapper.length) {
4367 new MediaHolder({
4368 el: $wrapper,
4369 model: this.model,
4370 attribute: attribute,
4371 moduleType: this.moduleType
4372 });
4373 }
4374 }
4375 },
4376
4377 /**
4378 * Updates the active container `aria-labelledby` attr so it matches the active button.
4379 * For accessibility.
4380 * We should make this behavior reusable. Maybe it should be handled it sui.
4381 *
4382 * @since 4.3.0
4383 */
4384 updateLabelledbyCtaContent: function updateLabelledbyCtaContent() {
4385 var selectedLabelForAttr = this.$('input[name="show_cta"]:checked').attr('id'),
4386 newLabeledBy = this.$("[data-label-for=\"".concat(selectedLabelForAttr, "\"]")).attr('id');
4387 this.$('#tab-content-cta-button-one').attr('aria-labelledby', newLabeledBy);
4388 },
4389 modelUpdated: function modelUpdated(model) {
4390 var changedKey = Object.keys(model.changed)[0],
4391 actionToDo = this.getActionOnModelUpdated(changedKey);
4392
4393 if ('undefined' !== typeof actionToDo) {
4394 actionToDo(model.changed);
4395 }
4396
4397 Hustle.Events.trigger('modules.view.contentUpdate', model.changed);
4398 },
4399 getActionOnModelUpdated: function getActionOnModelUpdated(changedKey) {
4400 var _this = this;
4401
4402 var functions = {
4403 show_cta: function show_cta(changed) {
4404 if ('0' !== changed.show_cta) {
4405 Module.Utils.showHideDependencyOnSelectChange(_this.$('#hustle-content-cta-options-container'));
4406 }
4407 }
4408 };
4409 return functions[changedKey];
4410 }
4411 });
4412 });
4413 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
4414
4415 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
4416
4417 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
4418
4419 /* global ace */
4420 Hustle.define('Mixins.Module_Design', function ($) {
4421 'use strict';
4422
4423 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
4424 el: '#hustle-wizard-appearance',
4425 cssEditor: false,
4426 fontFamilies: {},
4427 fontFamiliesOptions: [],
4428
4429 /**
4430 * Keeps track of the updated properties.
4431 * It allows us to submit only the updated props during the AJAX save
4432 * and to avoid save issues with max_input_vars set on 1000.
4433 *
4434 * @since 4.3.0
4435 */
4436 updatedProperties: {},
4437
4438 /**
4439 * Keeps track of which elements are shown.
4440 *
4441 * @since 4.3.0
4442 */
4443 contentPropIsShown: {
4444 title: true,
4445 sub_title: true,
4446 main_content: true,
4447 feature_image: true,
4448 background_image: true,
4449 show_never_see_link: true,
4450 show_cta: true
4451 },
4452 events: {
4453 'click .hustle-css-stylable': 'insertSelector',
4454 'click .hustle-reset-settings-block > button': 'resetSettingsBlock',
4455 'change [data-link-fields]': 'linkFieldsChanged',
4456 'change [data-linked-fields]': 'linkedFieldsChanged',
4457 'change .hustle-font-family-select': 'fontFamilyUpdated',
4458 'change select[name="feature_image_width_option"]': 'updateFeatureImageWidth',
4459 'click .hustle-button-apply-global-font': 'applyGlobalFontClicked',
4460 'change .hustle-required-field': 'requiredFieldChanged'
4461 },
4462 init: function init(opts) {
4463 this.model = new opts.BaseModel(optinVars.current.design || {});
4464 this.beforeRender();
4465 this.render();
4466 },
4467 beforeRender: function beforeRender() {
4468 var _this = this;
4469
4470 this.listenTo(this.model, 'change', this.modelUpdated);
4471 Hustle.Events.on('modules.view.contentUpdate', function (changed) {
4472 return _this.contentModelUpdated(changed);
4473 });
4474 Hustle.Events.on('modules.view.emailsUpdate', function (changed) {
4475 return _this.emailsModelUpdated(changed);
4476 });
4477 Hustle.Events.on('modules.view.integrationsUpdate', function (changed) {
4478 return _this.integrationsModelUpdate(changed);
4479 });
4480 this.$('#hustle-color-palettes-list').on('select2:open', function () {
4481 return _this.addCreatePalettesLink();
4482 }); // Initialize wpColorPickers only before using them.
4483
4484 this.$('#tab-content-customize_colors-custom .sui-accordion-item').on('click', function (e) {
4485 return _this.initiateColorPickers(e);
4486 });
4487 this.$('.hustle-typography-elements-row .sui-accordion-item-header').on('click', function (e) {
4488 return _this.initiateFontFamilySelectOnAccordionClick(e);
4489 });
4490 this.setFontFamilyOptions();
4491 this.setVisibilityOnRender();
4492 },
4493 render: function render() {
4494 this.toggleDeviceTabs();
4495 this.toggleCtaButtonsTextAlignment();
4496 this.setImageAligmentOptions();
4497 this.toggleFeatureImageSizeSettingRow();
4498 this.toggleFeatureImageSizeRows();
4499 this.cssEditor = this.createEditor('hustle_custom_css');
4500 this.setVanillaThemeVisibility(); // Hide other Options for Mobile Feature Image.
4501
4502 this.hideOtherOptionsInAcordionItem('feature_image_hide_on_mobile', '1' === this.model.get('feature_image_hide_on_mobile'));
4503 this.hideOtherOptionsInAcordionItem('feature_image_position', !this.contentPropIsShown.feature_image);
4504
4505 if (optinVars.current.is_optin) {
4506 this.setSucccessfulMessageOptionVisibility(optinVars.current.emails);
4507 this.formFieldsUpdated(optinVars.current.emails);
4508 this.updateMailchimpRelatedAccordions(optinVars.current.integrations_settings);
4509 } else {
4510 this.handleStyleChange();
4511 }
4512
4513 var self = this;
4514 $.each(['title', 'sub_title', 'feature_image', 'background_image', 'show_cta', 'show_never_see_link', 'main_content'], function (index, key) {
4515 self.updateElementsRow(key);
4516 });
4517 },
4518 // ============================================================
4519 // Font-family.
4520 setFontFamilyOptions: function setFontFamilyOptions() {
4521 var _this2 = this;
4522
4523 var optionsPromise = this.fetchFontFamilyOptions();
4524 optionsPromise.done(function (res) {
4525 _this2.fontFamilies = res.data;
4526 $.each(_this2.fontFamilies, function (id, data) {
4527 _this2.fontFamiliesOptions.push({
4528 id: id,
4529 text: data.label
4530 });
4531 });
4532
4533 var $globalFontFamilySelect = _this2.$('.hustle-font-family-select[name="global_font_family"]');
4534
4535 _this2.initiateFontFamilySelects($globalFontFamilySelect, true);
4536
4537 _this2.toggleCustomFontInput($globalFontFamilySelect);
4538 });
4539 },
4540 fetchFontFamilyOptions: function fetchFontFamilyOptions() {
4541 var data = {
4542 action: 'hustle_fetch_font_families',
4543 _ajax_nonce: optinVars.typography.fetch_nonce
4544 };
4545 return $.post({
4546 url: ajaxurl,
4547 type: 'post',
4548 data: data
4549 });
4550 },
4551 initiateFontFamilySelectOnAccordionClick: function initiateFontFamilySelectOnAccordionClick(e) {
4552 var self = this;
4553 $(e.currentTarget).siblings('.sui-accordion-item-body').find('.hustle-font-family-select.sui-disabled').each(function () {
4554 self.initiateFontFamilySelects($(this));
4555 self.toggleCustomFontInput($(this));
4556 });
4557 },
4558 initiateFontFamilySelects: function initiateFontFamilySelects($selects) {
4559 var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
4560
4561 if ($selects.data('fonts-loaded') === false || force) {
4562 $selects.SUIselect2('destroy');
4563 $selects.SUIselect2({
4564 data: this.fontFamiliesOptions
4565 });
4566 $selects.removeClass('sui-disabled');
4567 $selects.prop('disabled', false);
4568 $selects.data('fonts-loaded', true);
4569 }
4570 },
4571 fontFamilyUpdated: function fontFamilyUpdated(e) {
4572 var $select = $(e.currentTarget),
4573 weightName = $select.data('weight'),
4574 $weightSelect = this.$("[name=\"".concat(weightName, "\"]")),
4575 $weightSelectMobile = this.$("[name=\"".concat(weightName, "_mobile\"]")),
4576 selectedFamily = $select.val(),
4577 weightSelectOptions = [];
4578 var availableVariants;
4579
4580 if (!!selectedFamily) {
4581 availableVariants = this.fontFamilies[selectedFamily].variants;
4582 }
4583
4584 var selected = true;
4585
4586 if ('undefined' !== typeof availableVariants) {
4587 var _iterator = _createForOfIteratorHelper(availableVariants),
4588 _step;
4589
4590 try {
4591 for (_iterator.s(); !(_step = _iterator.n()).done;) {
4592 var variant = _step.value;
4593 weightSelectOptions.push({
4594 id: variant,
4595 text: variant,
4596 selected: selected
4597 });
4598
4599 if (selected === true) {
4600 selected = false;
4601 }
4602 }
4603 } catch (err) {
4604 _iterator.e(err);
4605 } finally {
4606 _iterator.f();
4607 }
4608
4609 $weightSelect.html(weightSelectOptions);
4610 $weightSelect.SUIselect2('destroy');
4611 $weightSelect.SUIselect2({
4612 data: weightSelectOptions
4613 });
4614 $weightSelectMobile.html(weightSelectOptions);
4615 $weightSelectMobile.SUIselect2('destroy');
4616 $weightSelectMobile.SUIselect2({
4617 data: weightSelectOptions
4618 });
4619 }
4620
4621 this.toggleCustomFontInput($select);
4622 },
4623 applyGlobalFontClicked: function applyGlobalFontClicked(e) {
4624 var _this3 = this;
4625
4626 var $applyButton = $(e.currentTarget);
4627 $applyButton.addClass('sui-button-onload');
4628 setTimeout(function () {
4629 _this3.applyGlobalFont();
4630
4631 $applyButton.removeClass('sui-button-onload');
4632 Module.Notification.open('success', optinVars.typography.global_font_applied, 4000);
4633 }, 0);
4634 },
4635 applyGlobalFont: function applyGlobalFont() {
4636 var self = this,
4637 $selects = this.$('.hustle-font-family-select:not([name="global_font_family"])'),
4638 globalFont = this.model.get('global_font_family'),
4639 isCustom = 'custom' === globalFont,
4640 customGlobalFont = this.model.get('global_custom_font_family');
4641 var option;
4642 $selects.each(function () {
4643 var $select = $(this);
4644
4645 if ($select.find('option[value="' + globalFont + '"]').length === 0) {
4646 option = new Option(globalFont, globalFont, true, false);
4647 $select.empty().val(null).append(option);
4648 } else {
4649 $select.val(globalFont);
4650 }
4651
4652 $select.trigger('change');
4653
4654 if (isCustom) {
4655 var customName = $select.data('custom'),
4656 $customField = self.$("input[name=\"".concat(customName, "\"]"));
4657 $customField.val(customGlobalFont).trigger('change');
4658 }
4659
4660 self.toggleCustomFontInput($select);
4661 });
4662 this.globalFontVariantsUpdated(globalFont);
4663 },
4664 globalFontVariantsUpdated: function globalFontVariantsUpdated(fontFamily) {
4665 var $weightSelect = this.$('.hustle-font-weight'),
4666 availableVariants = this.fontFamilies[fontFamily].variants,
4667 weightSelectOptions = [];
4668 var selected = true;
4669
4670 if ('undefined' !== typeof availableVariants) {
4671 var _iterator2 = _createForOfIteratorHelper(availableVariants),
4672 _step2;
4673
4674 try {
4675 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
4676 var variant = _step2.value;
4677 weightSelectOptions.push({
4678 id: variant,
4679 text: variant,
4680 selected: selected
4681 });
4682
4683 if (selected === true) {
4684 selected = false;
4685 }
4686 }
4687 } catch (err) {
4688 _iterator2.e(err);
4689 } finally {
4690 _iterator2.f();
4691 }
4692
4693 $weightSelect.html(weightSelectOptions);
4694 $weightSelect.SUIselect2('destroy');
4695 $weightSelect.SUIselect2({
4696 data: weightSelectOptions
4697 });
4698 }
4699 },
4700 toggleCustomFontInput: function toggleCustomFontInput($select) {
4701 var selectedFamily = $select.val(),
4702 customName = $select.data('custom'),
4703 $customFieldWrapper = this.$("input[name=\"".concat(customName, "\"]")).closest('.sui-form-field');
4704
4705 if ('custom' === selectedFamily) {
4706 Module.Utils.accessibleShow($customFieldWrapper);
4707 } else {
4708 Module.Utils.accessibleHide($customFieldWrapper);
4709 }
4710 },
4711 toggleDeviceTabs: function toggleDeviceTabs() {
4712 var $deviceTabsMenu = this.$('#hustle-device_settings-tabs > .sui-tabs-menu'),
4713 $deviceTabsContent = this.$('#hustle-device_settings-tabs > .sui-tabs-content'),
4714 $deviceTabs = this.$('#hustle-device_settings-tabs'),
4715 isMobileEnabled = '1' === this.model.get('enable_mobile_settings');
4716
4717 if (!isMobileEnabled) {
4718 $deviceTabs.removeClass('hustle-mobile-enabled');
4719 $deviceTabsMenu.find('#tab-device_settings-desktop').trigger('click');
4720 $deviceTabsMenu.attr('aria-hidden', true);
4721 $deviceTabsMenu.attr('hidden', true);
4722 $deviceTabsContent.find('#tab-content-device_settings-desktop').removeAttr('role');
4723 $deviceTabsContent.find('#tab-content-device_settings-mobile').attr('aria-hidden', true);
4724 } else {
4725 $deviceTabs.addClass('hustle-mobile-enabled');
4726 $deviceTabsMenu.prop('aria-hidden', false);
4727 $deviceTabsMenu.prop('hidden', false);
4728 $deviceTabsContent.find('#tab-content-device_settings-desktop').attr('role', 'tabpanel');
4729 $deviceTabsContent.find('#tab-content-device_settings-mobile').prop('aria-hidden', false);
4730 }
4731 },
4732 // ============================================================
4733 // Color Pickers
4734 initiateColorPickers: function initiateColorPickers(e) {
4735 var $accordion = $(e.currentTarget);
4736 var initClass = 'hustle-colorpickers-initialized',
4737 pickers = $accordion.find('.sui-colorpicker-input');
4738
4739 if (!$accordion.hasClass(initClass) && pickers.length) {
4740 $accordion.addClass(initClass);
4741 this.createPickers(pickers);
4742 }
4743 },
4744 createPickers: function createPickers($suiPickerInputs) {
4745 var self = this;
4746 $suiPickerInputs.wpColorPicker({
4747 change: function change(event, ui) {
4748 var $this = $(this); // Prevent the model from being marked as changed on load.
4749
4750 if ($this.val() !== ui.color.toCSS()) {
4751 $this.val(ui.color.toCSS()).trigger('change');
4752 }
4753 },
4754 palettes: ['#333333', '#FFFFFF', '#17A8E3', '#E1F6FF', '#666666', '#AAAAAA', '#E6E6E6']
4755 });
4756
4757 if ($suiPickerInputs.hasClass('wp-color-picker')) {
4758 $suiPickerInputs.each(function () {
4759 var $suiPickerType = 'hex';
4760 var $suiPickerInput = $(this),
4761 $wpPicker = $suiPickerInput.closest('.wp-picker-container'),
4762 $wpPickerButton = $wpPicker.find('.wp-color-result'),
4763 $wpPickerAlpha = $wpPickerButton.find('.color-alpha'),
4764 $suiPicker = $suiPickerInput.closest('.sui-colorpicker-wrap'),
4765 $suiPickerColor = $suiPicker.find('.sui-colorpicker-value span[role=button]'),
4766 $suiPickerValue = $suiPicker.find('.sui-colorpicker-value'),
4767 $suiPickerClear = $suiPickerValue.find('button'),
4768 $shownInput = $suiPickerValue.find('.hustle-colorpicker-input'); // Check if alpha exists
4769
4770 if (true === $suiPickerInput.data('alpha')) {
4771 $suiPickerType = 'rgba'; // Listen to color change
4772
4773 $suiPickerInput.on('change', function (e, data) {
4774 // Change color preview
4775 $suiPickerColor.find('span').css({
4776 'background-color': $wpPickerAlpha.css('background')
4777 }); // We trigger this 'change' manually when the shown input changes.
4778 // Don't update its value again if this is the case.
4779
4780 if ('undefined' === typeof data) {
4781 // Change color value
4782 $shownInput.val($suiPickerInput.val());
4783 }
4784 });
4785 } else {
4786 // Listen to color change
4787 $suiPickerInput.on('change', function (e, data) {
4788 // Change color preview
4789 $suiPickerColor.find('span').css({
4790 'background-color': $wpPickerButton.css('background-color')
4791 }); // We trigger this 'change' manually when the shown input changes.
4792 // Don't update its value again if this is the case.
4793
4794 if ('undefined' === typeof data) {
4795 // Change color value
4796 $shownInput.val($suiPickerInput.val());
4797 }
4798 });
4799 } // Allow updating the colors without having to open the colorpicker.
4800
4801
4802 $shownInput.on('change', function () {
4803 // Change color value
4804 $suiPickerInput.val($shownInput.val());
4805 $suiPickerInput.trigger('change', [{
4806 triggeredByUs: true
4807 }]);
4808 }); // Add picker type class
4809
4810 $suiPicker.find('.sui-colorpicker').addClass('sui-colorpicker-' + $suiPickerType); // Open iris picker
4811
4812 $suiPicker.find('.sui-button, span[role=button]').on('click', function (e) {
4813 $wpPickerButton.click();
4814 e.preventDefault();
4815 e.stopPropagation();
4816 }); // Clear color value
4817
4818 $suiPickerClear.on('click', function (e) {
4819 return self.colorPickerCleared(e, $suiPickerInput, self);
4820 });
4821 });
4822 }
4823 },
4824 colorPickerCleared: function colorPickerCleared(e, parentSuiPickerInput, parentSelf) {
4825 var inputName = parentSuiPickerInput.data('attribute'),
4826 selectedStyle = parentSelf.model.get('color_palette'),
4827 resetValue = optinVars.palettes[selectedStyle][inputName],
4828 $suiPicker = parentSuiPickerInput.closest('.sui-colorpicker-wrap'),
4829 $suiPickerValue = $suiPicker.find('.sui-colorpicker-value'),
4830 $suiPickerColor = $suiPicker.find('.sui-colorpicker-value span[role=button]'),
4831 $wpPicker = parentSuiPickerInput.closest('.wp-picker-container'),
4832 $wpPickerClear = $wpPicker.find('.wp-picker-clear');
4833 $wpPickerClear.click();
4834 $suiPickerValue.find('input').val(resetValue);
4835 parentSuiPickerInput.val(resetValue).trigger('change');
4836 $suiPickerColor.find('span').css({
4837 'background-color': resetValue
4838 });
4839 e.preventDefault();
4840 e.stopPropagation();
4841 },
4842 updatePickers: function updatePickers(selectedStyle) {
4843 var self = this;
4844
4845 if ('undefined' !== typeof optinVars.palettes[selectedStyle]) {
4846 var colors = optinVars.palettes[selectedStyle]; // update color palettes
4847
4848 _.each(colors, function (color, key) {
4849 self.$('input[data-attribute="' + key + '"]').val(color).trigger('change');
4850 });
4851 } // TODO: else, display an error message.
4852
4853 },
4854 resetSettingsBlock: function resetSettingsBlock(e) {
4855 var $el = $(e.target);
4856 $el.addClass('sui-button-onload').prop('disabled', true);
4857
4858 if ($el.closest('#hustle-color-palette').length) {
4859 // Reset Pickers
4860 var style = $('select[data-attribute="color_palette"]').val();
4861 this.updatePickers(style);
4862 } else {
4863 // Reset other block types
4864 var parent = $el.closest('.sui-accordion');
4865 var ev = jQuery.Event("click");
4866 ev.currentTarget = parent;
4867 this.initiateFontFamilySelectOnAccordionClick(ev);
4868 var settings = parent.find('[data-attribute]');
4869 settings.each(function () {
4870 var $field = $(this);
4871 var fieldName = $field.attr('name');
4872
4873 if ('undefined' !== typeof optinVars.defaults[fieldName]) {
4874 var value = optinVars.defaults[fieldName],
4875 suiTabs = $field.parent('.sui-tabs');
4876 $field.val(value);
4877
4878 if ('radio' !== $field.prop('type') || !$field.parent('.sui-tabs')) {
4879 // other cases
4880 $field.trigger('sui:change').trigger('change');
4881 } else {
4882 // changing SUI tabs
4883 $('#tab-' + fieldName + '-' + value, suiTabs).trigger('click');
4884 }
4885 }
4886 });
4887 }
4888
4889 setTimeout(function () {
4890 $el.removeClass('sui-button-onload').prop('disabled', false);
4891 }, 500);
4892 },
4893
4894 /**
4895 * Add the "Create custom palette button" to the existing palettes dropdown.
4896 * The select's options are re-rendered every time they're opened.
4897 *
4898 * @since 4.0.3
4899 */
4900 addCreatePalettesLink: function addCreatePalettesLink() {
4901 var _this4 = this;
4902
4903 // Delay appending this so it's not overriden by select2's options.
4904 setTimeout(function () {
4905 var $link = _this4.$('#hustle-create-palette-link li'),
4906 $selectPaletteContainer = $('#select2-hustle-color-palettes-list-results'),
4907 $selectButton = $selectPaletteContainer.find('.hui-button');
4908
4909 if (!$selectButton.length) {
4910 $link.clone().appendTo($selectPaletteContainer);
4911 }
4912 }, 500);
4913 },
4914 // ============================================================
4915 // CSS Editor
4916 createEditor: function createEditor(id) {
4917 var cssEditor = ace.edit(id);
4918 cssEditor.getSession().setMode('ace/mode/css');
4919 cssEditor.$blockScrolling = Infinity;
4920 cssEditor.setTheme('ace/theme/sui');
4921 cssEditor.getSession().setUseWrapMode(true);
4922 cssEditor.getSession().setUseWorker(false);
4923 cssEditor.setShowPrintMargin(false);
4924 cssEditor.renderer.setShowGutter(true);
4925 cssEditor.setHighlightActiveLine(true);
4926 return cssEditor;
4927 },
4928 updateCustomCss: function updateCustomCss() {
4929 if (this.cssEditor) {
4930 this.model.set('custom_css', this.cssEditor.getValue());
4931 }
4932 },
4933 insertSelector: function insertSelector(e) {
4934 var $el = $(e.target),
4935 stylable = $el.data('stylable') + '{}',
4936 cssEditor = this.cssEditor;
4937 cssEditor.navigateFileEnd();
4938 cssEditor.insert(stylable);
4939 cssEditor.navigateLeft(1);
4940 cssEditor.focus();
4941 e.preventDefault();
4942 },
4943 // ============================================================
4944 // Adjust the view when the Design model is updated.
4945 modelUpdated: function modelUpdated() {
4946 this.addUpdatedProperty();
4947 this.updateViewOnModelUpdate();
4948 },
4949 addUpdatedProperty: function addUpdatedProperty() {
4950 _.extend(this.updatedProperties, this.model.changed);
4951 },
4952 updateViewOnModelUpdate: function updateViewOnModelUpdate() {
4953 var model = this.model,
4954 //changed = model.changed,
4955 changedkey = Object.keys(model.changed)[0],
4956 actionToDo = this.getActionOnModelUpdated(changedkey);
4957
4958 if ('undefined' !== typeof actionToDo) {
4959 actionToDo(changedkey);
4960 }
4961 },
4962 getActionOnModelUpdated: function getActionOnModelUpdated(changedKey) {
4963 var _this5 = this;
4964
4965 var functions = {
4966 color_palette: function color_palette() {
4967 return _this5.updatePickers(_this5.model.changed.color_palette);
4968 },
4969 cta_buttons_alignment: function cta_buttons_alignment() {
4970 return _this5.toggleCtaButtonsTextAlignment();
4971 },
4972 cta_buttons_alignment_mobile: function cta_buttons_alignment_mobile() {
4973 return _this5.toggleCtaButtonsTextAlignment();
4974 },
4975 enable_mobile_settings: function enable_mobile_settings() {
4976 return _this5.toggleDeviceTabs();
4977 },
4978 feature_image_hide_on_mobile: function feature_image_hide_on_mobile(prop) {
4979 return _this5.hideOtherOptionsInAcordionItem(prop, '1' === _this5.model.get(prop));
4980 },
4981 feature_image_fit: function feature_image_fit(prop) {
4982 return _this5.toggleFeatureImageSizeSettingRow(prop);
4983 },
4984 // Hide other Options for Mobile Feature Image.
4985 feature_image_fit_mobile: function feature_image_fit_mobile(prop) {
4986 return _this5.toggleFeatureImageSizeSettingRow(prop);
4987 },
4988 feature_image_position: function feature_image_position() {
4989 return _this5.toggleFeatureImageSizeRows();
4990 },
4991 form_layout: function form_layout() {
4992 _this5.setImageAligmentOptions();
4993
4994 _this5.toggleFeatureImageSizeRows();
4995 },
4996 style: function style() {
4997 return _this5.handleStyleChange();
4998 },
4999 use_vanilla: function use_vanilla() {
5000 return _this5.setVanillaThemeVisibility();
5001 }
5002 };
5003 return functions[changedKey];
5004 },
5005 toggleCtaButtonsTextAlignment: function toggleCtaButtonsTextAlignment() {
5006 var $ctaTypographyAccordionDesktop = this.$('#hustle-cta_alignment-form-field'),
5007 $ctaTypographyAccordionMobile = this.$('#hustle-cta_alignment_mobile-form-field');
5008
5009 if ('full' === this.model.get('cta_buttons_alignment')) {
5010 Module.Utils.accessibleShow($ctaTypographyAccordionDesktop);
5011 } else {
5012 Module.Utils.accessibleHide($ctaTypographyAccordionDesktop);
5013 }
5014
5015 if ('full' === this.model.get('cta_buttons_alignment_mobile')) {
5016 Module.Utils.accessibleShow($ctaTypographyAccordionMobile);
5017 } else {
5018 Module.Utils.accessibleHide($ctaTypographyAccordionMobile);
5019 }
5020 },
5021 hideOtherOptionsInAcordionItem: function hideOtherOptionsInAcordionItem(inputName, hide) {
5022 var $this = this.$('[name="' + inputName + '"]'),
5023 $parent = $this.closest('.sui-box'),
5024 $parentRow = $parent.find('.sui-box-settings-row').slice(0, 1),
5025 $nextRows = $parent.find('.sui-box-settings-row').slice(1);
5026
5027 if (!hide) {
5028 $nextRows.removeClass('sui-hidden-important');
5029 $parentRow.removeClass('hustle-no-bottom-line');
5030 } else {
5031 $nextRows.addClass('sui-hidden-important');
5032 $parentRow.addClass('hustle-no-bottom-line');
5033 }
5034 },
5035 toggleFeatureImageSizeSettingRow: function toggleFeatureImageSizeSettingRow() {
5036 var changedKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
5037
5038 if (!changedKey || 'feature_image_fit' === changedKey) {
5039 var $sizeRow = this.$('#hustle-feature-image-size-settings-row'),
5040 value = this.model.get('feature_image_fit');
5041
5042 if ('none' !== value) {
5043 $sizeRow.show();
5044 } else {
5045 $sizeRow.hide();
5046 }
5047 }
5048
5049 if (!changedKey || 'feature_image_fit_mobile' === changedKey) {
5050 var _$sizeRow = this.$('#hustle-feature-image-size-mobile-settings-row'),
5051 _value = this.model.get('feature_image_fit_mobile');
5052
5053 if ('none' !== _value) {
5054 _$sizeRow.show();
5055 } else {
5056 _$sizeRow.hide();
5057 }
5058 }
5059 },
5060 handleStyleChange: function handleStyleChange() {
5061 var style = this.model.get('style'),
5062 $layoutMain = this.$('[data-name="module_cont"]'),
5063 $layoutHeader = this.$('[data-name="layout_header"]'),
5064 $layoutContent = this.$('[data-name="layout_content"]'),
5065 $layoutFooter = this.$('[data-name="layout_footer"]'); // Replace "Main Layout" classname on note field.
5066
5067 if ('cabriolet' === style) {
5068 $layoutMain.find('.sui-accordion-item-title .sui-accordion-note').text('.hustle-layout-body');
5069 } else {
5070 $layoutMain.find('.sui-accordion-item-title .sui-accordion-note').text('.hustle-layout');
5071 } // Hide "Layout Content" and show "Layout Footer" row for Default (minimal) style.
5072
5073
5074 if ('minimal' !== style) {
5075 Module.Utils.accessibleHide($layoutContent);
5076 Module.Utils.accessibleHide($layoutFooter);
5077 } else {
5078 Module.Utils.accessibleShow($layoutContent);
5079 Module.Utils.accessibleShow($layoutFooter);
5080 } // Hide the "Layout Header" row for Compact (simple) layout.
5081
5082
5083 if ('simple' !== style) {
5084 Module.Utils.accessibleShow($layoutHeader);
5085 } else {
5086 Module.Utils.accessibleHide($layoutHeader);
5087 }
5088 },
5089 setVanillaThemeVisibility: function setVanillaThemeVisibility() {
5090 var vanillaElements = this.$('[data-toggle-content="use-vanilla"]'),
5091 $nonVanillaElements = this.$('[data-toggle-content="not-use-vanilla"]');
5092
5093 if (this.model.get('use_vanilla') === '0') {
5094 Module.Utils.accessibleShow(vanillaElements, true);
5095 Module.Utils.accessibleHide($nonVanillaElements, true);
5096 } else {
5097 Module.Utils.accessibleHide(vanillaElements, true);
5098 Module.Utils.accessibleShow($nonVanillaElements, true);
5099 }
5100 },
5101 // Update linked fields values when re-linked.
5102 linkFieldsChanged: function linkFieldsChanged(e) {
5103 var $input = $(e.currentTarget); // Fields were unlinked. No need to do anything.
5104
5105 if ('0' === $input.val()) {
5106 return;
5107 } // Fields were linked. Update their values using the first field's value.
5108
5109
5110 var linkName = $input.attr('name'),
5111 $linkedFields = this.$("[data-linked-fields=".concat(linkName, "]")),
5112 firstFieldVal = $linkedFields[0].value;
5113 $linkedFields.val(firstFieldVal).trigger('change', {
5114 updatedByUs: true
5115 });
5116 },
5117 // Keep linked fields linked on change.
5118 linkedFieldsChanged: function linkedFieldsChanged(e, data) {
5119 // The fields were updated manually by us. Bail out and don't trigger that infinite loop.
5120 if (data) {
5121 return;
5122 }
5123
5124 var $input = $(e.currentTarget),
5125 linkName = $input.data('linked-fields'); // The fields are unlinked. Nothing to do here.
5126
5127 if ('1' !== this.model.get(linkName)) {
5128 return;
5129 }
5130
5131 var $linkedFields = this.$("[data-linked-fields=".concat(linkName, "]"));
5132 $linkedFields.val($input.val()).trigger('change', {
5133 updatedByUs: true
5134 });
5135 },
5136 requiredFieldChanged: function requiredFieldChanged(e) {
5137 var $field = $(e.currentTarget),
5138 isEmpty = 0 === $field.val().trim().length;
5139
5140 if (isEmpty) {
5141 var fieldName = $field.attr('name');
5142
5143 if ('undefined' !== typeof optinVars.defaults[fieldName]) {
5144 $field.val(optinVars.defaults[fieldName]);
5145 }
5146 }
5147 },
5148 // Show or hide the positions available for each form layout.
5149 setImageAligmentOptions: function setImageAligmentOptions() {
5150 var $targetAbove = this.$('#tab-feature_image_position-alignment-above'),
5151 $targetBelow = this.$('#tab-feature_image_position-alignment-below');
5152
5153 if ('one' === this.model.get('form_layout')) {
5154 Module.Utils.accessibleShow($targetAbove, true);
5155 Module.Utils.accessibleShow($targetBelow, true);
5156 } else {
5157 var imgPosition = this.model.get('feature_image_position');
5158
5159 if ('left' !== imgPosition && 'right' !== imgPosition) {
5160 this.$('#tab-feature_image_position-alignment-left').trigger('click'); // The model's isn't triggering a change of feature_image_position for some reason.
5161 // TODO: Find and fix that. Then remove this function call.
5162
5163 this.toggleFeatureImageSizeRows();
5164 }
5165
5166 Module.Utils.accessibleHide($targetAbove, true);
5167 Module.Utils.accessibleHide($targetBelow, true);
5168 }
5169 },
5170 toggleFeatureImageSizeRows: function toggleFeatureImageSizeRows() {
5171 var $widthRow = this.$('#hustle-feature_image_width-row'),
5172 $widthDescription = this.$('#hustle-feature-image-desktop-width-description'),
5173 $heightRow = this.$('#hustle-feature_image_height-row'),
5174 $heightDescription = this.$('#hustle-feature-image-desktop-height-description'),
5175 contentDependentProps = ['title', 'sub_title', 'show_cta', 'main_content'],
5176 formLayout = this.model.get('form_layout');
5177
5178 var showHeight = function showHeight() {
5179 $heightRow.show();
5180 $heightDescription.show();
5181 $widthRow.hide();
5182 $widthDescription.hide();
5183 },
5184 showWidth = function showWidth() {
5185 $heightRow.hide();
5186 $heightDescription.hide();
5187 $widthRow.show();
5188 $widthDescription.show();
5189 },
5190 showBoth = function showBoth() {
5191 $heightRow.show();
5192 $heightDescription.hide();
5193 $widthRow.show();
5194 $widthDescription.show();
5195 }; // Use only the height field when there's no title, subtitle, cta, nor main content.
5196
5197
5198 var isFeatureImageOnly = true;
5199
5200 for (var _i = 0, _contentDependentProp = contentDependentProps; _i < _contentDependentProp.length; _i++) {
5201 var prop = _contentDependentProp[_i];
5202
5203 if (this.contentPropIsShown[prop]) {
5204 isFeatureImageOnly = false;
5205 }
5206 }
5207
5208 if (isFeatureImageOnly && 'two' !== formLayout && 'four' !== formLayout) {
5209 showHeight();
5210 return;
5211 } // Informational modules never use the height other than in the case above.
5212
5213
5214 if (!optinVars.current.is_optin) {
5215 showWidth();
5216 return;
5217 }
5218
5219 if ('three' === formLayout) {
5220 showHeight();
5221 return;
5222 }
5223
5224 if ('four' === formLayout) {
5225 if (isFeatureImageOnly) {
5226 showWidth();
5227 } else {
5228 showBoth();
5229 }
5230
5231 return;
5232 }
5233
5234 var imageAlignment = this.model.get('feature_image_position');
5235
5236 if ('below' === imageAlignment || 'above' === imageAlignment) {
5237 showHeight();
5238 } else {
5239 showWidth();
5240 }
5241 },
5242 updateFeatureImageWidth: function updateFeatureImageWidth(e) {
5243 var $predefinedSelect = $(e.currentTarget),
5244 predefinedValue = $predefinedSelect.val(),
5245 $widthValueInput = this.$('input[name="feature_image_width"]'); // TODO: implement the global handler for disable/enable.
5246
5247 if ('custom' !== predefinedValue) {
5248 var $widthUnitSelect = this.$('select[name="feature_image_width_unit"]');
5249 $widthUnitSelect.val('%').trigger('sui:change').trigger('change');
5250 $widthValueInput.prop('disabled', true);
5251 $widthValueInput.val(predefinedValue).trigger('change');
5252 } else {
5253 $widthValueInput.prop('disabled', false);
5254 }
5255 },
5256 // ============================================================
5257 // Adjust the view according to the Content model.
5258 contentModelUpdated: function contentModelUpdated(changed) {
5259 var changedKey = Object.keys(changed)[0],
5260 actionToDo = this.getActionOnContentModelUpdated(changedKey);
5261
5262 if ('undefined' !== typeof actionToDo) {
5263 actionToDo(changed, changedKey);
5264 this.toggleFeatureImageSizeRows();
5265 this.updateElementsRow(changedKey);
5266 }
5267 },
5268 setVisibilityOnRender: function setVisibilityOnRender() {
5269 this.contentPropIsShown.feature_image = '' !== optinVars.current.content.feature_image;
5270 this.contentPropIsShown.background_image = '' !== optinVars.current.content.background_image;
5271 this.contentPropIsShown.show_cta = '0' !== optinVars.current.content.show_cta;
5272 this.contentPropIsShown.title = '' !== optinVars.current.content.title;
5273 this.contentPropIsShown.sub_title = '' !== optinVars.current.content.sub_title;
5274 this.contentPropIsShown.show_never_see_link = '0' !== optinVars.current.content.show_never_see_link;
5275 this.contentPropIsShown.optin_form = optinVars.current.is_optin;
5276 this.contentPropIsShown.main_content = '' !== optinVars.current.content.main_content;
5277 },
5278 getActionOnContentModelUpdated: function getActionOnContentModelUpdated(changedKey) {
5279 var _this6 = this;
5280
5281 var functions = {
5282 // Uploading a featured image makes the "Featured Image settings" show up in the "Appearance" tab.
5283 background_image: function background_image(changed) {
5284 return _this6.contentPropIsShown.background_image = '' !== changed.background_image;
5285 },
5286 // Update this view when "Feature image" is changed in the Content tab.
5287 feature_image: function feature_image(changed) {
5288 _this6.contentPropIsShown.feature_image = '' !== changed.feature_image;
5289
5290 _this6.hideOtherOptionsInAcordionItem('feature_image_position', !_this6.contentPropIsShown.feature_image);
5291 },
5292 main_content: function main_content(changed) {
5293 return _this6.contentPropIsShown.main_content = '' !== changed.main_content;
5294 },
5295 show_cta: function show_cta(changed) {
5296 return _this6.contentPropIsShown.show_cta = '0' !== changed.show_cta;
5297 },
5298 show_never_see_link: function show_never_see_link(changed) {
5299 return _this6.contentPropIsShown.show_never_see_link = '0' !== changed.show_never_see_link;
5300 },
5301 sub_title: function sub_title(changed) {
5302 return _this6.contentPropIsShown.sub_title = '' !== changed.sub_title;
5303 },
5304 title: function title(changed) {
5305 return _this6.contentPropIsShown.title = '' !== changed.title;
5306 }
5307 };
5308 return functions[changedKey];
5309 },
5310 updateElementsRow: function updateElementsRow(changedKey) {
5311 var $row = this.$('#hustle-wizard-appearance-desktop, #hustle-wizard-appearance-mobiles');
5312 var $accordion = $row.find(".sui-accordion-item[data-name=\"".concat(changedKey, "\"]")),
5313 doShow = this.contentPropIsShown[changedKey];
5314
5315 if (doShow) {
5316 $accordion.show();
5317 } else {
5318 $accordion.hide();
5319 } // The Advanced and Typography rows (the ones checked in the function)
5320 // always remain displayed for optin modules.
5321
5322
5323 if (!optinVars.current.is_optin) {
5324 this.updateRow(changedKey);
5325 } else {
5326 this.handleTypographyTabs(changedKey);
5327 }
5328 },
5329 updateRow: function updateRow(changedKey) {
5330 var rows = {
5331 'hustle-typography-elements-row': ['show_cta', 'title', 'sub_title', 'main_content'],
5332 'hustle-appearance-customize-elements-row': ['feature_image', 'background_image', 'show_cta']
5333 };
5334 var self = this;
5335 $.each(rows, function (rowClass, dependentProps) {
5336 if (dependentProps.includes(changedKey)) {
5337 var doShowRow = false;
5338
5339 var _iterator3 = _createForOfIteratorHelper(dependentProps),
5340 _step3;
5341
5342 try {
5343 for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
5344 var prop = _step3.value;
5345
5346 if (self.contentPropIsShown[prop]) {
5347 doShowRow = true;
5348 }
5349 }
5350 } catch (err) {
5351 _iterator3.e(err);
5352 } finally {
5353 _iterator3.f();
5354 }
5355
5356 if (doShowRow) {
5357 $('.' + rowClass).show();
5358 } else {
5359 $('.' + rowClass).hide();
5360 }
5361 }
5362 });
5363 },
5364 handleTypographyTabs: function handleTypographyTabs(changedKey) {
5365 var dependentProps = ['show_cta', 'title', 'sub_title', 'main_content'];
5366
5367 if ('embedded' !== optinVars.current.data.module_type) {
5368 dependentProps.push('show_never_see_link');
5369 }
5370
5371 if (dependentProps.includes(changedKey)) {
5372 var $tabs = this.$('.hustle-typography-tabs'),
5373 $tabsMenu = $tabs.find('.sui-tabs-menu');
5374 var doShowGeneralTab = false;
5375
5376 var _iterator4 = _createForOfIteratorHelper(dependentProps),
5377 _step4;
5378
5379 try {
5380 for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
5381 var prop = _step4.value;
5382
5383 if (this.contentPropIsShown[prop]) {
5384 doShowGeneralTab = true;
5385 }
5386 }
5387 } catch (err) {
5388 _iterator4.e(err);
5389 } finally {
5390 _iterator4.f();
5391 }
5392
5393 if (doShowGeneralTab) {
5394 Module.Utils.accessibleShow($tabsMenu);
5395 } else {
5396 Module.Utils.accessibleHide($tabsMenu);
5397 $tabs.find('#tab-custom-typography-optin').trigger('click');
5398 $tabs.find('#tab-custom-typography_mobile-optin').trigger('click');
5399 }
5400 }
5401 },
5402 // ============================================================
5403 // Adjust the view according to the Emails model.
5404 emailsModelUpdated: function emailsModelUpdated(changed) {
5405 var changedKey = Object.keys(changed)[0],
5406 actionToDo = this.getActionOnEmailsModelUpdated(changedKey);
5407
5408 if ('undefined' !== typeof actionToDo) {
5409 actionToDo(changed, changedKey); //this.updateElementsRow( changedKey );
5410 }
5411 },
5412 getActionOnEmailsModelUpdated: function getActionOnEmailsModelUpdated(changedKey) {
5413 var _this7 = this;
5414
5415 var functions = {
5416 form_elements: function form_elements(changed) {
5417 return _this7.formFieldsUpdated(changed);
5418 },
5419 after_successful_submission: function after_successful_submission(changed) {
5420 return _this7.setSucccessfulMessageOptionVisibility(changed);
5421 }
5422 };
5423 return functions[changedKey];
5424 },
5425 // TODO: Fix not working for colors.
5426 setSucccessfulMessageOptionVisibility: function setSucccessfulMessageOptionVisibility(model) {
5427 var $divSettings = this.$('[data-name="success_message"]');
5428
5429 if ($divSettings.length > 0) {
5430 if ('show_success' === model.after_successful_submission) {
5431 $divSettings.show();
5432 } else {
5433 $divSettings.hide();
5434 }
5435 }
5436 },
5437 formFieldsUpdated: function formFieldsUpdated(model) {
5438 this.handleRecaptcha(model.form_elements);
5439 this.handleGdpr(model.form_elements);
5440 this.handleCalendar(model.form_elements);
5441 },
5442
5443 /**
5444 * Triggered when 'form_elements' in the emails model is updated.
5445 *
5446 * @since 4.3.0
5447 *
5448 * @param {Object} formFields Current form field elements.
5449 */
5450 handleRecaptcha: function handleRecaptcha(formFields) {
5451 var $recaptchaSettings = this.$('[data-name="recaptcha"]');
5452 var recaptcha = false;
5453
5454 if ('undefined' !== typeof formFields.recaptcha) {
5455 recaptcha = 'v3_recaptcha' === formFields.recaptcha.version && '0' === formFields.recaptcha.v3_recaptcha_show_badge || 'v2_invisible' === formFields.recaptcha.version && '0' === formFields.recaptcha.v2_invisible_show_badge;
5456 }
5457
5458 if (recaptcha) {
5459 $recaptchaSettings.show();
5460 } else {
5461 $recaptchaSettings.hide();
5462 }
5463 },
5464 handleGdpr: function handleGdpr(formFields) {
5465 var $gdprSettings = this.$('[data-name="gdpr"]');
5466
5467 if ('undefined' !== typeof formFields.gdpr) {
5468 $gdprSettings.show();
5469 } else {
5470 $gdprSettings.hide();
5471 }
5472 },
5473 handleCalendar: function handleCalendar(formFields) {
5474 var hasCalendar = false;
5475
5476 for (var fieldSlug in formFields) {
5477 var field = formFields[fieldSlug];
5478
5479 if ('calendar' === field.type) {
5480 hasCalendar = true;
5481 break;
5482 }
5483 }
5484
5485 if (hasCalendar) {
5486 this.$('[data-name="calendar"]').show();
5487 } else {
5488 this.$('[data-name="calendar"]').hide();
5489 }
5490 },
5491 // ============================================================
5492 // Adjust the view according to the Integrations model.
5493 integrationsModelUpdate: function integrationsModelUpdate(model) {
5494 if ('active_integrations' in model.changed) {
5495 this.updateMailchimpRelatedAccordions(model.changed);
5496 }
5497 },
5498 updateMailchimpRelatedAccordions: function updateMailchimpRelatedAccordions(model) {
5499 var activeIntegrations = model.active_integrations.split(','),
5500 hasMailchimp = activeIntegrations.includes('mailchimp'),
5501 dependentProps = ['form_extras', 'checkbox', 'dropdown', 'select'];
5502
5503 for (var _i2 = 0, _dependentProps = dependentProps; _i2 < _dependentProps.length; _i2++) {
5504 var prop = _dependentProps[_i2];
5505
5506 if (hasMailchimp) {
5507 this.$("[data-name=\"".concat(prop, "\"]")).show();
5508 } else {
5509 this.$("[data-name=\"".concat(prop, "\"]")).hide();
5510 }
5511 }
5512 }
5513 });
5514 });
5515 Hustle.define('Mixins.Module_Display', function () {
5516 'use strict';
5517
5518 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
5519 el: '#hustle-wizard-display',
5520 events: {},
5521 init: function init(opts) {
5522 this.model = new opts.BaseModel(optinVars.current.display || {});
5523 this.moduleType = optinVars.current.data.module_type;
5524 this.listenTo(this.model, 'change', this.viewChanged); // Called just to trigger the "view.rendered" action.
5525
5526 this.render();
5527 },
5528 render: function render() {},
5529 viewChanged: function viewChanged() {}
5530 });
5531 });
5532 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
5533
5534 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
5535
5536 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
5537
5538 Hustle.define('Mixins.Module_Emails', function ($) {
5539 'use strict';
5540
5541 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
5542 el: '#hustle-wizard-emails',
5543 events: {
5544 'click .hustle-optin-field--add': 'addFields',
5545 'click .hustle-optin-field--edit': 'editField',
5546 'click .sui-builder-field': 'maybeEditField',
5547 'click .hustle-optin-field--delete': 'deleteFieldOnClick',
5548 'click ul.list-results li': 'setFieldOption',
5549 'click .hustle-optin-field--copy': 'duplicateField'
5550 },
5551 init: function init(opts) {
5552 this.model = new opts.BaseModel(optinVars.current.emails || {});
5553 this.listenTo(this.model, 'change', this.modelUpdated);
5554 this.render();
5555 },
5556 render: function render() {
5557 var self = this,
5558 formElements = this.model.get('form_elements'); // Add the already stored form fields to the panel.
5559
5560 for (var fieldId in formElements) {
5561 var field = formElements[fieldId]; // Assign the defaults for the field, in case there's anything missing.
5562
5563 formElements[fieldId] = _.extend({}, this.getFieldDefaults(field.type), field); // Submit is already at the bottom of the panel. We don't want to add it again.
5564
5565 if ('submit' === fieldId) {
5566 continue;
5567 }
5568
5569 self.addFieldToPanel(formElements[fieldId]);
5570 } // update form_elements for having default properties if they were lost for some reason
5571
5572
5573 this.model.set('form_elements', formElements, {
5574 silent: true
5575 }); // Initiate the sortable functionality to sort form fields' order.
5576
5577 var sortableContainer = this.$('#hustle-form-fields-container').sortable({
5578 axis: 'y',
5579 containment: '.sui-box-builder'
5580 });
5581 sortableContainer.on('sortupdate', $.proxy(self.fieldsOrderChanged, self, sortableContainer));
5582 this.$('#hustle-email-day').datepicker({
5583 beforeShow: function beforeShow() {
5584 $('#ui-datepicker-div').addClass('sui-calendar');
5585 },
5586 dateFormat: 'MM dd, yy'
5587 });
5588 this.$('#hustle-email-time').timepicker({
5589 timeFormat: 'h:mm p',
5590 interval: '1',
5591 minTime: '0',
5592 maxTime: '11:59pm',
5593 defaultTime: null,
5594 startTime: '00:00',
5595 dynamic: false,
5596 dropdown: true,
5597 scrollbar: true,
5598 change: function change() {
5599 $('#hustle-email-time').trigger('change');
5600 }
5601 });
5602 this.updateDynamicValueFields();
5603 return this;
5604 },
5605
5606 /**
5607 * Handle the changes in the view when the model is updated.
5608 *
5609 * @since 4.3.0
5610 *
5611 * @param {Object} model The model for this view.
5612 */
5613 modelUpdated: function modelUpdated(model) {
5614 var changed = model.changed,
5615 changedKey = Object.keys(changed)[0],
5616 actionToDo = this.getActionOnModelUpdated(changedKey);
5617
5618 if ('undefined' !== typeof actionToDo) {
5619 actionToDo(changed);
5620 }
5621
5622 Hustle.Events.trigger('modules.view.emailsUpdate', changed);
5623 },
5624
5625 /**
5626 * Launches events when some actions are made.
5627 *
5628 * @since 4.3.0
5629 *
5630 * @param {string} changedKey Action name.
5631 */
5632 getActionOnModelUpdated: function getActionOnModelUpdated(changedKey) {
5633 var _this = this;
5634
5635 var functions = {
5636 auto_close_success_message: function auto_close_success_message() {
5637 return _this.autoCloseSuccessMessageUpdated();
5638 },
5639 form_elements: function form_elements() {
5640 return _this.updateDynamicValueFields();
5641 }
5642 };
5643 return functions[changedKey];
5644 },
5645 autoCloseSuccessMessageUpdated: function autoCloseSuccessMessageUpdated() {
5646 var $targetDiv = this.$('#section-auto-close-success-message .sui-row');
5647
5648 if ('1' === this.model.get('auto_close_success_message')) {
5649 $targetDiv.removeClass('sui-hidden');
5650 } else {
5651 $targetDiv.addClass('sui-hidden');
5652 }
5653 },
5654 //reset all field selects
5655 resetDynamicValueFieldsPlaceholders: function resetDynamicValueFieldsPlaceholders() {
5656 this.$('select.hustle-field-options').html('');
5657
5658 if (this.$('.hustle-fields-placeholders-options').length) {
5659 this.$('.hustle-fields-placeholders-options').html('');
5660 }
5661 },
5662 //update all field selects
5663 updateDynamicValueFields: function updateDynamicValueFields() {
5664 var formElements = this.model.get('form_elements');
5665 this.resetDynamicValueFieldsPlaceholders();
5666
5667 for (var fieldId in formElements) {
5668 if ('submit' === fieldId || 'recaptcha' === fieldId || 'gdpr' === fieldId) {
5669 continue;
5670 }
5671
5672 this.addFieldToDynamicValueFields(formElements[fieldId]);
5673 this.$('select.hustle-field-options').trigger('sui:change');
5674 } //set info notice for empty dynamic fields select
5675
5676
5677 this.$('div.select-list-container .list-results:empty').each(function () {
5678 var fieldType = $(this).closest('.select-container').find('select.hustle-field-options').data('type');
5679 $(this).html('<li style="cursor: default; pointer-events: none;">' + optinVars.form_fields.no_fields_of_type_notice.replace('{field_type}', fieldType) + '</li>');
5680 });
5681 },
5682
5683 /**
5684 * Assign the new field order to the model. Triggered when the fields are sorted.
5685 *
5686 * @since 4.0.0
5687 * @param {Object} sortable jQuery sortable object.
5688 */
5689 fieldsOrderChanged: function fieldsOrderChanged(sortable) {
5690 var formElements = this.model.get('form_elements'),
5691 newOrder = sortable.sortable('toArray', {
5692 attribute: 'data-field-id'
5693 });
5694 var orderedFields = {};
5695
5696 var _iterator = _createForOfIteratorHelper(newOrder),
5697 _step;
5698
5699 try {
5700 for (_iterator.s(); !(_step = _iterator.n()).done;) {
5701 var id = _step.value;
5702 orderedFields[id] = formElements[id];
5703 }
5704 } catch (err) {
5705 _iterator.e(err);
5706 } finally {
5707 _iterator.f();
5708 }
5709
5710 orderedFields = _.extend({}, orderedFields, formElements);
5711 this.model.set('form_elements', orderedFields);
5712 },
5713
5714 /**
5715 * Open the "Add new fields" modal.
5716 *
5717 * @since 4.0
5718 * @param {Event} e Event.
5719 */
5720 addFields: function addFields(e) {
5721 // Show dialog
5722 SUI.openModal('hustle-dialog--optin-fields', $(e.currentTarget)[0], this.$('#hustle-dialog--optin-fields .sui-box-header .sui-button-icon')[0], true);
5723 var OptinFieldsModalView = Hustle.get('Modals.Optin_Fields'),
5724 newFieldModal = new OptinFieldsModalView({
5725 model: this.model
5726 }); // Create the fields and append them to panel.
5727
5728 newFieldModal.on('fields:added', $.proxy(this.addNewFields, this));
5729 },
5730 maybeEditField: function maybeEditField(e) {
5731 var $ct = $(e.target);
5732
5733 if (!$ct.closest('.sui-dropdown').length) {
5734 this.editField(e);
5735 }
5736 },
5737
5738 /**
5739 * Open the "edit field" modal.
5740 *
5741 * @since 4.0.0
5742 * @param {event} e Event.
5743 */
5744 editField: function editField(e) {
5745 var $button = $(e.target),
5746 fieldId = $button.closest('.sui-builder-field').data('field-id'),
5747 existingFields = this.model.get('form_elements'),
5748 field = existingFields[fieldId],
5749 fieldData = Object.assign({}, this.getFieldViewDefaults(field.type), field),
5750 EditFieldModalView = Hustle.get('Modals.Edit_Field'),
5751 editModalView = new EditFieldModalView({
5752 field: field,
5753 fieldData: fieldData,
5754 model: this.model
5755 });
5756 editModalView.on('field:updated', $.proxy(this.formFieldUpdated, this)); // Show dialog.
5757
5758 SUI.openModal('hustle-dialog--edit-field', $button[0], this.$('#hustle-dialog--edit-field .sui-box-header .sui-button-icon')[0], true);
5759 },
5760
5761 /**
5762 * Update the appearance of the form field row of the field that was updated.
5763 *
5764 * @since 4.0.0
5765 *
5766 * @param {Object} updatedField Field properties after the update.
5767 * @param {Object} changed Field properties that were updated.
5768 * @param {Object} oldField Field properties before the update.
5769 */
5770 formFieldUpdated: function formFieldUpdated(updatedField, changed, oldField) {
5771 if (!Object.keys(changed).length) {
5772 return;
5773 } // Name is the unique identifier.
5774 // If it changed, update the existing fields removing the old one and creating a new one.
5775
5776
5777 if ('name' in changed) {
5778 this.addNewFields(updatedField.type, updatedField, oldField.name);
5779 this.deleteField(oldField.name);
5780 return;
5781 }
5782
5783 var $fieldRow = this.$('#hustle-optin-field--' + updatedField.name);
5784
5785 if ('required' in changed) {
5786 var $requiredTag = $fieldRow.find('.sui-error'),
5787 isRequired = updatedField.required; // Show the "required" asterisk to this field's row.
5788
5789 if (_.isTrue(isRequired)) {
5790 $requiredTag.show();
5791 } else if (_.isFalse(isRequired)) {
5792 // Hide the "required" asterisk to this field's row.
5793 $requiredTag.hide();
5794 }
5795 }
5796
5797 if ('label' in changed) {
5798 this.updateDynamicValueFields();
5799 var $labelWrapper = $fieldRow.find('.hustle-field-label-text');
5800 $labelWrapper.text(updatedField.label);
5801 }
5802 },
5803 deleteFieldOnClick: function deleteFieldOnClick(e) {
5804 var $button = $(e.target),
5805 fieldName = $button.closest('.sui-builder-field').data('field-id');
5806 this.deleteField(fieldName);
5807 },
5808 setFieldOption: function setFieldOption(e) {
5809 var $li = $(e.target),
5810 val = $li.find('span:eq(1)').text(),
5811 $input = $li.closest('.sui-insert-variables').find('input[type="text"]');
5812 $input.val(val).trigger('change');
5813 },
5814 deleteField: function deleteField(fieldName) {
5815 var $fieldRow = this.$('#hustle-optin-field--' + fieldName),
5816 formElements = Object.assign({}, this.model.get('form_elements'));
5817 delete formElements[fieldName];
5818 this.model.set('form_elements', formElements);
5819
5820 if (-1 !== jQuery.inArray(fieldName, ['gdpr', 'recaptcha'])) {
5821 $fieldRow.addClass('sui-hidden');
5822 $('#hustle-optin-insert-field--' + fieldName).prop('disabled', false).prop('checked', false);
5823 } else {
5824 $fieldRow.remove();
5825 }
5826 },
5827 duplicateField: function duplicateField(e) {
5828 var $button = $(e.target),
5829 fieldId = $button.closest('.sui-builder-field').data('field-id'),
5830 formElements = Object.assign({}, this.model.get('form_elements')),
5831 duplicatedField = Object.assign({}, formElements[fieldId]); // Remove 'name' because it should be an unique identifier. Will be added in 'add_new_fields'.
5832
5833 delete duplicatedField.name; // Make the field deletable because it can't be deleted otherwise, and you'll have it stuck forevah.
5834
5835 duplicatedField.can_delete = true; // eslint-disable-line camelcase
5836
5837 this.addNewFields(duplicatedField.type, duplicatedField);
5838 },
5839
5840 /**
5841 * Used to add new fields.
5842 * When using form_fields, make sure only 1 type of each field is added.
5843 * In other words, use field.type as an unique identifier.
5844 *
5845 * @since 4.0.0
5846 *
5847 * @param {Array|string} formFields Field or set of fields to add.
5848 * @param {Object} formFieldsData Field data to include in the new field.
5849 * @param {string|null} oldFieldName Field name before the update.
5850 */
5851 addNewFields: function addNewFields(formFields, formFieldsData) {
5852 var oldFieldName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
5853 var self = this;
5854 var existingFields = Object.assign({}, this.model.get('form_elements'));
5855
5856 if (Array.isArray(formFields)) {
5857 var _iterator2 = _createForOfIteratorHelper(formFields),
5858 _step2;
5859
5860 try {
5861 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
5862 var field = _step2.value;
5863 var fieldData = self.getFieldDefaults(field);
5864
5865 if (formFieldsData && field in formFieldsData) {
5866 _.extend(fieldData, formFieldsData[field]);
5867 }
5868
5869 self.addFieldToPanel(fieldData);
5870 existingFields[fieldData.name] = fieldData;
5871 }
5872 } catch (err) {
5873 _iterator2.e(err);
5874 } finally {
5875 _iterator2.f();
5876 }
5877 } else {
5878 var _fieldData = self.getFieldDefaults(formFields);
5879
5880 if (formFieldsData) {
5881 _.extend(_fieldData, formFieldsData);
5882 }
5883
5884 self.addFieldToPanel(_fieldData, oldFieldName);
5885
5886 if (null === oldFieldName) {
5887 existingFields[_fieldData.name] = _fieldData;
5888 } else {
5889 var reorderExistingFields = [];
5890 jQuery.each(existingFields, function (index, data) {
5891 reorderExistingFields[index] = data;
5892
5893 if (index === oldFieldName) {
5894 reorderExistingFields[_fieldData.name] = _fieldData;
5895 }
5896 });
5897 existingFields = reorderExistingFields;
5898 }
5899 }
5900
5901 this.model.set('form_elements', existingFields);
5902 },
5903
5904 /**
5905 * Add a field to the fields with dynamic values for the automated emails.
5906 * The field object must have all its core prop assigned. The views prop are assigned here.
5907 *
5908 * @since 4.0
5909 * @param {Object} field Properties of the field.
5910 */
5911 addFieldToDynamicValueFields: function addFieldToDynamicValueFields(field) {
5912 // escape name and label.
5913 var temp;
5914 temp = $('<div>' + field.name + '</div>');
5915 temp.find('script').remove();
5916 field.name = temp.html();
5917 temp = $('<div>' + field.label + '</div>');
5918 temp.find('script').remove();
5919 field.label = temp.html();
5920 var option = $('<option/>', {
5921 value: field.name,
5922 'data-content': '{' + field.name + '}'
5923 }).text(field.label),
5924 listOption = "<li><button value=\"{".concat(field.name, "}\">").concat(field.label, "</button></li>");
5925 this.$('select.hustle-field-options:not([data-type]), select.hustle-field-options[data-type="' + field.type + '"]').append(option);
5926
5927 if (this.$('.hustle-fields-placeholders-options').length) {
5928 this.$('.hustle-fields-placeholders-options').append(listOption);
5929 }
5930 },
5931
5932 /**
5933 * Add a field to the fields pannel.
5934 * The field object must have all its core prop assigned. The views prop are assigned here.
5935 *
5936 * @since 4.0.0
5937 *
5938 * @param {Object} field Field properties to add.
5939 * @param {string|null} oldFieldName Old field name if we're updating a field's name.
5940 */
5941 addFieldToPanel: function addFieldToPanel(field) {
5942 var oldFieldName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
5943 var template = Optin.template('hustle-form-field-row-tpl'),
5944 $fieldsContainer = this.$('#hustle-form-fields-container');
5945 field = _.extend({}, this.getFieldViewDefaults(field.type), field);
5946
5947 if (-1 !== jQuery.inArray(field.type, ['gdpr', 'recaptcha'])) {
5948 this.$('#hustle-optin-field--' + field.type).removeClass('sui-hidden');
5949 $('#hustle-optin-insert-field--' + field.type).prop('checked', true).prop('disabled', true);
5950 } else if (null === oldFieldName) {
5951 $fieldsContainer.append(template(field));
5952 } else {
5953 var $el = this.$('#hustle-optin-field--' + oldFieldName);
5954
5955 if (0 < $el.length) {
5956 $el.after(template(field));
5957 } else {
5958 $fieldsContainer.append(template(field));
5959 }
5960 }
5961 },
5962 getNewFieldId: function getNewFieldId(fieldName) {
5963 var existingFields = Object.assign({}, this.model.get('form_elements'));
5964 var fieldId = fieldName;
5965
5966 while (fieldId in existingFields && -1 === jQuery.inArray(fieldId, ['gdpr', 'recaptcha', 'submit'])) {
5967 fieldId = fieldName + '-' + Math.floor(Math.random() * 99);
5968 }
5969
5970 return fieldId;
5971 },
5972
5973 /**
5974 * Retrieve the default settings for each field type.
5975 * These are going to be stored.
5976 *
5977 * @since 4.0.0
5978 *
5979 * @param {string} fieldType The type of the field we're getting the defaults for.
5980 */
5981 getFieldDefaults: function getFieldDefaults(fieldType) {
5982 var fieldId = this.getNewFieldId(fieldType),
5983 defaults = {
5984 label: optinVars.form_fields.label[fieldType + '_label'],
5985 required: 'false',
5986 css_classes: '',
5987 type: fieldType,
5988 name: fieldId,
5989 required_error_message: optinVars.form_fields.required_error_message.replace('{field}', fieldType),
5990 validation_message: optinVars.form_fields.validation_message.replace('{field}', fieldType),
5991 placeholder: ''
5992 };
5993
5994 switch (fieldType) {
5995 case 'timepicker':
5996 defaults.time_format = '12'; // eslint-disable-line camelcase
5997
5998 defaults.time_hours = '9'; // eslint-disable-line camelcase
5999
6000 defaults.time_minutes = '30'; // eslint-disable-line camelcase
6001
6002 defaults.time_period = 'am'; // eslint-disable-line camelcase
6003
6004 defaults.validation_message = optinVars.form_fields.time_validation_message; // eslint-disable-line camelcase
6005
6006 defaults.required_error_message = optinVars.form_fields.is_required.replace('{field}', defaults.label); // eslint-disable-line camelcase
6007
6008 defaults.validate = 'false';
6009 break;
6010
6011 case 'datepicker':
6012 defaults.date_format = 'mm/dd/yy'; // eslint-disable-line camelcase
6013
6014 defaults.validation_message = optinVars.form_fields.date_validation_message; // eslint-disable-line camelcase
6015
6016 defaults.required_error_message = optinVars.form_fields.is_required.replace('{field}', defaults.label); // eslint-disable-line camelcase
6017
6018 defaults.validate = 'false';
6019 break;
6020
6021 case 'recaptcha':
6022 defaults.threshold = '0.5'; // eslint-disable-line camelcase
6023
6024 defaults.version = 'v2_checkbox'; // eslint-disable-line camelcase
6025
6026 defaults.recaptcha_type = 'compact'; // eslint-disable-line camelcase
6027
6028 defaults.recaptcha_theme = 'light'; // eslint-disable-line camelcase
6029
6030 defaults.v2_invisible_theme = 'light'; // eslint-disable-line camelcase
6031
6032 defaults.recaptcha_language = 'automatic'; // eslint-disable-line camelcase
6033
6034 defaults.v2_invisible_show_badge = '1'; // eslint-disable-line camelcase
6035
6036 defaults.v2_invisible_badge_replacement = optinVars.form_fields.recaptcha_badge_replacement; // eslint-disable-line camelcase
6037
6038 defaults.v3_recaptcha_show_badge = '1'; // eslint-disable-line camelcase
6039
6040 defaults.v3_recaptcha_badge_replacement = optinVars.form_fields.recaptcha_badge_replacement; // eslint-disable-line camelcase
6041
6042 defaults.validation_message = optinVars.form_fields.recaptcha_validation_message; // eslint-disable-line camelcase
6043
6044 defaults.error_message = optinVars.form_fields.recaptcha_error_message; // eslint-disable-line camelcase
6045
6046 break;
6047
6048 case 'gdpr':
6049 defaults.gdpr_message = optinVars.form_fields.gdpr_message; // eslint-disable-line camelcase
6050
6051 defaults.required = 'true';
6052 defaults.required_error_message = optinVars.form_fields.gdpr_required_error_message; // eslint-disable-line camelcase
6053
6054 break;
6055
6056 case 'email':
6057 defaults.validate = 'true';
6058 break;
6059
6060 case 'url':
6061 defaults.required_error_message = optinVars.form_fields.url_required_error_message; // eslint-disable-line camelcase
6062
6063 defaults.validate = 'true';
6064 break;
6065
6066 case 'phone':
6067 defaults.validate = 'false';
6068 break;
6069
6070 case 'hidden':
6071 defaults.default_value = 'user_ip'; // eslint-disable-line camelcase
6072
6073 defaults.custom_value = ''; // eslint-disable-line camelcase
6074
6075 break;
6076
6077 case 'number':
6078 case 'text':
6079 defaults.required_error_message = optinVars.form_fields.cant_empty; // eslint-disable-line camelcase
6080
6081 break;
6082 }
6083
6084 return defaults;
6085 },
6086
6087 /**
6088 * Retrieve the defaults for each field type's setting view.
6089 * These settings are intended to display the proper content of each field
6090 * in the wizard settings. These won't be stored.
6091 *
6092 * @since 4.0.0
6093 * @param {string} fieldType The field type.
6094 */
6095 getFieldViewDefaults: function getFieldViewDefaults(fieldType) {
6096 var defaults = {
6097 required: 'false',
6098 validated: 'false',
6099 placeholder_placeholder: optinVars.form_fields.label.placeholder,
6100 label_placeholder: '',
6101 name_placeholder: '',
6102 icon: 'send',
6103 css_classes: '',
6104 type: fieldType,
6105 name: fieldType,
6106 placeholder: optinVars.form_fields.label[fieldType + '_placeholder'],
6107 can_delete: true,
6108 fieldId: this.getNewFieldId(fieldType)
6109 };
6110
6111 switch (fieldType) {
6112 case 'email':
6113 defaults.icon = 'mail';
6114 break;
6115
6116 case 'name':
6117 defaults.icon = 'profile-male';
6118 break;
6119
6120 case 'phone':
6121 defaults.icon = 'phone';
6122 break;
6123
6124 case 'address':
6125 defaults.icon = 'pin';
6126 break;
6127
6128 case 'url':
6129 defaults.icon = 'web-globe-world';
6130 break;
6131
6132 case 'text':
6133 defaults.icon = 'style-type';
6134 break;
6135
6136 case 'number':
6137 defaults.icon = 'element-number';
6138 break;
6139
6140 case 'timepicker':
6141 defaults.icon = 'clock';
6142 break;
6143
6144 case 'datepicker':
6145 defaults.icon = 'calendar';
6146 break;
6147
6148 case 'recaptcha':
6149 defaults.icon = 'recaptcha';
6150 break;
6151
6152 case 'gdpr':
6153 defaults.icon = 'gdpr';
6154 break;
6155
6156 case 'hidden':
6157 defaults.icon = 'eye-hide';
6158 break;
6159 }
6160
6161 return defaults;
6162 }
6163 });
6164 });
6165 Hustle.define('Module.IntegrationsView', function ($) {
6166 'use strict';
6167
6168 var integrationsView = Hustle.View.extend(_.extend({}, Hustle.get('Mixins.Model_Updater'), {
6169 el: '#hustle-box-section-integrations',
6170 events: {
6171 'click .connect-integration': 'connectIntegration',
6172 'keypress .connect-integration': 'preventEnterKeyFromDoingThings'
6173 },
6174 init: function init(opts) {
6175 this.model = new opts.BaseModel(optinVars.current.integrations_settings || {});
6176 this.moduleId = optinVars.current.data.module_id;
6177 this.listenTo(this.model, 'change', function (model) {
6178 return Hustle.Events.trigger('modules.view.integrationsUpdate', model);
6179 });
6180 this.listenTo(Hustle.Events, 'hustle:providers:reload', this.renderProvidersTables);
6181 this.render();
6182 },
6183 render: function render() {
6184 var $notConnectedWrapper = this.$el.find('#hustle-not-connected-providers-section'),
6185 $connectedWrapper = this.$el.find('#hustle-connected-providers-section');
6186
6187 if (0 < $notConnectedWrapper.length && 0 < $connectedWrapper.length) {
6188 this.renderProvidersTables();
6189 }
6190 },
6191 renderProvidersTables: function renderProvidersTables() {
6192 var self = this,
6193 data = {}; // Add preloader.
6194
6195 this.$el.find('.hustle-integrations-display').html("<div class=\"sui-notice hustle-integration-loading-notice\">\n\t\t\t\t\t\t<div class=\"sui-notice-content\">\n\t\t\t\t\t\t\t<div class=\"sui-notice-message\">\n\n\t\t\t\t\t\t\t\t<span class=\"sui-notice-icon sui-icon-loader sui-loading sui-md\" aria-hidden=\"true\"></span>\n\t\t\t\t\t\t\t\t<p>".concat(optinVars.integrations.fetching_list, "</p>\n\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>"));
6196 data.action = 'hustle_provider_get_form_providers';
6197 data._ajax_nonce = optinVars.integrations.action_nonce; // eslint-disable-line camelcase
6198
6199 data.data = {
6200 moduleId: this.moduleId
6201 };
6202 var ajax = $.post({
6203 url: ajaxurl,
6204 type: 'post',
6205 data: data
6206 }).done(function (result) {
6207 if (result && result.success) {
6208 var $activeIntegrationsInput = self.$el.find('#hustle-integrations-active-integrations'),
6209 $activeIntegrationsCount = self.$el.find('#hustle-integrations-active-count');
6210 self.$el.find('#hustle-not-connected-providers-section').html(result.data.not_connected);
6211 self.$el.find('#hustle-connected-providers-section').html(result.data.connected); // Prevent marking the model as changed on load.
6212
6213 if ($activeIntegrationsInput.val() !== result.data.list_connected) {
6214 $activeIntegrationsInput.val(result.data.list_connected).trigger('change');
6215 } // Prevent marking the model as changed on load.
6216
6217
6218 if ($activeIntegrationsCount.val() !== String(result.data.list_connected_total)) {
6219 $activeIntegrationsCount.val(result.data.list_connected_total).trigger('change');
6220 }
6221 }
6222 }); // Remove preloader
6223
6224 ajax.always(function () {
6225 self.$el.find('.sui-box-body').removeClass('sui-block-content-center');
6226 self.$el.find('.hustle-integration-loading-notice').remove();
6227 });
6228 },
6229 // Prevent the enter key from opening integrations modals and breaking the page.
6230 preventEnterKeyFromDoingThings: function preventEnterKeyFromDoingThings(e) {
6231 if (13 === e.which) {
6232 // the enter key code
6233 e.preventDefault();
6234 }
6235 },
6236 connectIntegration: function connectIntegration(e) {
6237 Module.integrationsModal.open(e);
6238 }
6239 }));
6240 return integrationsView;
6241 });
6242 function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
6243
6244 Hustle.define('Mixins.Module_Visibility', function ($) {
6245 'use strict';
6246
6247 return _.extend({}, Hustle.get('Mixins.Model_Updater'), {
6248 el: '#hustle-conditions-group',
6249 events: {
6250 'click .hustle-add-new-visibility-group': 'addNewGroup',
6251 'click .hustle-choose-conditions': 'openConditionsModal',
6252 'click .hustle-remove-visibility-group': 'removeGroup',
6253 'change .visibility-group-filter-type': 'updateAttribute',
6254 'change .visibility-group-show-hide': 'updateAttribute',
6255 'change .visibility-group-apply-on': 'updateGroupApplyOn'
6256 },
6257 init: function init(opts) {
6258 var Model = opts.BaseModel.extend({
6259 defaults: {
6260 conditions: ''
6261 },
6262 initialize: function initialize(data) {
6263 _.extend(this, data);
6264
6265 if (!(this.get('conditions') instanceof Backbone.Model)) {
6266 /**
6267 * Make sure conditions is not an array
6268 */
6269 if (_.isEmpty(this.get('conditions')) && _.isArray(this.get('conditions'))) {
6270 this.conditions = {};
6271 }
6272
6273 var hModel = Hustle.get('Model');
6274 this.set('conditions', new hModel(this.conditions), {
6275 silent: true
6276 });
6277 }
6278 }
6279 });
6280 this.model = new Model(optinVars.current.visibility || {});
6281 this.moduleType = optinVars.current.data.module_type;
6282 this.activeConditions = {};
6283 this.render();
6284 $('#hustle-general-conditions').on('click', $.proxy(this.switchConditions, this));
6285 $('#hustle-wc-conditions').on('click', $.proxy(this.switchConditions, this));
6286 this.groupId = '';
6287 },
6288 render: function render() {
6289 var self = this,
6290 groups = this.model.get('conditions').toJSON();
6291
6292 if (!$.isEmptyObject(groups)) {
6293 for (var groupId in groups) {
6294 var group = this.model.get('conditions.' + groupId);
6295
6296 if (!(group instanceof Backbone.Model)) {
6297 // Make sure it's not an array
6298 if (_.isEmpty(group) && _.isArray(group)) {
6299 group = {};
6300 }
6301
6302 group = this.getConditionsGroupModel(group);
6303 self.model.set('conditions.' + groupId, group, {
6304 silent: true
6305 });
6306 }
6307
6308 this.addGroupToPanel(group, 'render');
6309 }
6310
6311 this.maybeToggleGroupsBin();
6312 } else {
6313 this.addNewGroup();
6314 }
6315 },
6316 afterRender: function afterRender() {
6317 this.bindRemoveConditions();
6318 },
6319 bindRemoveConditions: function bindRemoveConditions() {
6320 // Remove condition
6321 $('#hustle-conditions-group .hustle-remove-visibility-condition').off('click').on('click', $.proxy(this.removeCondition, this));
6322 },
6323 openConditionsModal: function openConditionsModal(e) {
6324 var self = this,
6325 $this = $(e.currentTarget),
6326 groupId = $this.data('group-id'),
6327 savedConditions = this.model.get('conditions.' + groupId),
6328 groupConditions = 'undefined' !== typeof savedConditions ? Object.keys(savedConditions.toJSON()) : [],
6329 VisibilityModalView = Hustle.get('Modals.Visibility_Conditions'),
6330 visibilityModal = new VisibilityModalView({
6331 groupId: groupId,
6332 conditions: groupConditions
6333 });
6334 visibilityModal.on('conditions:added', $.proxy(self.addNewConditions, self));
6335 this.groupId = groupId; // Show dialog.
6336
6337 SUI.openModal('hustle-dialog--visibility-options', $this[0], this.$('#hustle-dialog--visibility-options .sui-box-header .sui-button-icon')[0], true);
6338 },
6339 addNewConditions: function addNewConditions(args) {
6340 var self = this,
6341 groupId = args.groupId,
6342 conditions = args.conditions,
6343 group = this.model.get('conditions.' + groupId);
6344 $.each(conditions, function (i, id) {
6345 if (group.get(id)) {
6346 // If this condition is already set for this group, abort. Prevent duplicated conditions in a group.
6347 return true;
6348 }
6349
6350 self.addConditionToPanel(id, {}, groupId, group, 'new');
6351 });
6352 this.bindRemoveConditions();
6353 Hustle.Events.trigger('view.rendered', this);
6354 },
6355 addGroupToPanel: function addGroupToPanel(group, source) {
6356 // Render this group container.
6357 var groupId = group.get('group_id'),
6358 targetContainer = $('#hustle-visibility-conditions-box'),
6359 _template = Optin.template('hustle-visibility-group-box-tpl'),
6360 html = _template(_.extend({}, {
6361 groupId: groupId,
6362 apply_on_floating: group.get('apply_on_floating'),
6363 // eslint-disable-line camelcase
6364 apply_on_inline: group.get('apply_on_inline'),
6365 // eslint-disable-line camelcase
6366 apply_on_widget: group.get('apply_on_widget'),
6367 // eslint-disable-line camelcase
6368 apply_on_shortcode: group.get('apply_on_shortcode'),
6369 // eslint-disable-line camelcase
6370 show_or_hide_conditions: group.get('show_or_hide_conditions'),
6371 // eslint-disable-line camelcase
6372 filter_type: group.get('filter_type') // eslint-disable-line camelcase
6373
6374 }));
6375
6376 var $group = $(html);
6377 $group.insertBefore(targetContainer.find('.hustle-add-new-visibility-group'));
6378 SUI.select.init($group.find('.sui-select'));
6379 this.activeConditions[groupId] = {}; // Render each of this group's conditions.
6380
6381 var self = this,
6382 conditions = group.toJSON();
6383 $.each(conditions, function (id, condition) {
6384 if ('object' !== _typeof(condition)) {
6385 // If this property is not an actual condition, like "group_id", or "filter_type",
6386 // continue. Check the next property as this isn't the condition we want to render.
6387 return true;
6388 }
6389
6390 self.addConditionToPanel(id, condition, groupId, group, source);
6391 });
6392 },
6393 addConditionToPanel: function addConditionToPanel(id, condition, groupId, group, source) {
6394 if ('undefined' === typeof Optin.View.Conditions[id]) {
6395 return;
6396 }
6397
6398 var thisCondition = new Optin.View.Conditions[id]({
6399 type: this.moduleType,
6400 model: group,
6401 groupId: groupId,
6402 source: source
6403 });
6404
6405 if (!thisCondition) {
6406 return;
6407 }
6408
6409 var $conditionsContainer = this.$('#hustle-visibility-group-' + groupId + ' .sui-box-builder-body'); // If there aren't other conditions rendered within the group, empty it for adding new conditions.
6410
6411 if (!$conditionsContainer.find('.sui-builder-field').length) {
6412 $conditionsContainer.find('.sui-box-builder-message-block').hide();
6413 $conditionsContainer.find('.sui-button-dashed').show();
6414 }
6415
6416 if ($.isEmptyObject(condition)) {
6417 group.set(id, thisCondition.getConfigs());
6418 } else {
6419 group.set(id, condition);
6420 }
6421
6422 this.activeConditions[groupId][id] = thisCondition;
6423 $(thisCondition.$el).appendTo($conditionsContainer.find('.sui-builder-fields'));
6424 return thisCondition;
6425 },
6426 addNewGroup: function addNewGroup() {
6427 var group = this.getConditionsGroupModel(),
6428 groupId = group.get('group_id');
6429 this.model.set('conditions.' + groupId, group);
6430 this.addGroupToPanel(group, 'new');
6431 this.maybeToggleGroupsBin();
6432 Hustle.Events.trigger('view.rendered', this);
6433 },
6434 switchConditions: function switchConditions(e) {
6435 e.preventDefault();
6436 var $this = $(e.currentTarget),
6437 currentId = $this.prop('id');
6438
6439 if ('hustle-wc-conditions' === currentId) {
6440 $('#hustle-dialog--visibility-options .general_condition').hide();
6441 $('#hustle-dialog--visibility-options .wc_condition').show();
6442 } else {
6443 $('#hustle-dialog--visibility-options .wc_condition').hide();
6444 $('#hustle-dialog--visibility-options .general_condition').show();
6445 }
6446 },
6447 removeGroup: function removeGroup(e) {
6448 var groupId = $(e.currentTarget).data('group-id'),
6449 $groupContainer = this.$('#hustle-visibility-group-' + groupId); // Remove the group from the model.
6450
6451 delete this.activeConditions[groupId];
6452 this.model.get('conditions').unset(groupId); // Remove the group container from the page.
6453
6454 $groupContainer.remove(); // If the last group was removed, add a new group so the page is not empty.
6455
6456 if (!Object.keys(this.activeConditions).length) {
6457 this.addNewGroup();
6458 }
6459
6460 this.maybeToggleGroupsBin();
6461 },
6462 removeCondition: function removeCondition(e) {
6463 var $this = $(e.currentTarget),
6464 conditionId = $this.data('condition-id'),
6465 groupId = $this.data('group-id'),
6466 $conditionsContainer = this.$('#hustle-visibility-group-' + groupId + ' .sui-box-builder-body'),
6467 thisCondition = this.activeConditions[groupId][conditionId];
6468 thisCondition.remove();
6469 delete this.activeConditions[groupId][conditionId];
6470 this.model.get('conditions.' + groupId).unset(conditionId);
6471
6472 if (!$conditionsContainer.find('.sui-builder-field').length) {
6473 $conditionsContainer.find('.sui-box-builder-message-block').show();
6474 }
6475
6476 this.bindRemoveConditions();
6477 },
6478 updateAttribute: function updateAttribute(e) {
6479 e.stopPropagation();
6480 var $this = $(e.target),
6481 groupId = $this.data('group-id'),
6482 attribute = $this.data('group-attribute'),
6483 value = $this.val(),
6484 group = this.model.get('conditions.' + groupId);
6485 group.set(attribute, value);
6486 },
6487 updateGroupApplyOn: function updateGroupApplyOn(e) {
6488 e.stopPropagation();
6489 var $this = $(e.target),
6490 groupId = $this.data('group-id'),
6491 attribute = $this.data('property'),
6492 value = $this.is(':checked'),
6493 group = this.model.get('conditions.' + groupId);
6494
6495 if ('embedded' === this.moduleType && -1 !== $.inArray(attribute, ['apply_on_inline', 'apply_on_widget', 'apply_on_shortcode']) || 'social_sharing' === this.moduleType && -1 !== $.inArray(attribute, ['apply_on_floating', 'apply_on_inline', 'apply_on_widget', 'apply_on_shortcode'])) {
6496 group.set(attribute, value);
6497 }
6498 },
6499 getConditionsGroupModel: function getConditionsGroupModel(group) {
6500 if (!group) {
6501 var groupId = new Date().getTime().toString(16);
6502
6503 if ('undefined' !== typeof this.model.get('conditions.' + groupId)) {// TODO: create another group_id while the group id exists.
6504 }
6505
6506 group = {
6507 group_id: groupId,
6508 // eslint-disable-line camelcase
6509 show_or_hide_conditions: 'show',
6510 // eslint-disable-line camelcase
6511 filter_type: 'all' // eslint-disable-line camelcase
6512
6513 };
6514
6515 if ('embedded' === this.moduleType) {
6516 group.apply_on_inline = true; // eslint-disable-line camelcase
6517
6518 group.apply_on_widget = true; // eslint-disable-line camelcase
6519
6520 group.apply_on_shortcode = false; // eslint-disable-line camelcase
6521 } else if ('social_sharing' === this.moduleType) {
6522 group.apply_on_floating = true; // eslint-disable-line camelcase
6523
6524 group.apply_on_inline = true; // eslint-disable-line camelcase
6525
6526 group.apply_on_widget = true; // eslint-disable-line camelcase
6527
6528 group.apply_on_shortcode = false; // eslint-disable-line camelcase
6529 }
6530 } else if ('embedded' === this.moduleType && (!group.apply_on_inline || !group.apply_on_widget || !group.apply_on_shortcode)) {
6531 if (!group.apply_on_inline) {
6532 group.apply_on_inline = true; // eslint-disable-line camelcase
6533 }
6534
6535 if (!group.apply_on_widget) {
6536 group.apply_on_widget = true; // eslint-disable-line camelcase
6537 }
6538
6539 if (!group.apply_on_shortcode) {
6540 group.apply_on_shortcode = false; // eslint-disable-line camelcase
6541 }
6542 } else if ('social_sharing' === this.moduleType && (!group.apply_on_floating || !group.apply_on_inline || !group.apply_on_widget || !group.apply_on_shortcode)) {
6543 if (!group.apply_on_floating) {
6544 group.apply_on_floating = true; // eslint-disable-line camelcase
6545 }
6546
6547 if (!group.apply_on_inline) {
6548 group.apply_on_inline = true; // eslint-disable-line camelcase
6549 }
6550
6551 if (!group.apply_on_widget) {
6552 group.apply_on_widget = true; // eslint-disable-line camelcase
6553 }
6554
6555 if (!group.apply_on_shortcode) {
6556 group.apply_on_shortcode = false; // eslint-disable-line camelcase
6557 }
6558 }
6559
6560 var hModel = Hustle.get('Model'),
6561 groupModel = new hModel(group);
6562 return groupModel;
6563 },
6564
6565 /**
6566 * Prevent the last standing group from being removable
6567 * Enable again the "bin" icons to remove if there's more than 1 group.
6568 *
6569 * @since 4.1.0
6570 */
6571 maybeToggleGroupsBin: function maybeToggleGroupsBin() {
6572 var groups = this.model.get('conditions'),
6573 $groupsBin = $('#hustle-conditions-group .sui-box-builder-header .hustle-remove-visibility-group');
6574
6575 if (1 === Object.keys(groups.toJSON()).length) {
6576 Module.Utils.accessibleHide($groupsBin);
6577 } else {
6578 Module.Utils.accessibleShow($groupsBin);
6579 }
6580 }
6581 });
6582 });
6583 /* global tinyMCE */
6584 Hustle.define('Mixins.Wizard_View', function ($, doc, win) {
6585 'use strict';
6586
6587 return {
6588 moduleType: '',
6589 el: '.sui-wrap-hustle',
6590 publishModal: {},
6591 previewView: null,
6592 events: {
6593 'click .sui-sidenav .sui-vertical-tab a': 'sidenav',
6594 'change select.sui-mobile-nav': 'sidenavMobile',
6595 'click a.hustle-go-to-tab': 'sidenav',
6596 'click a.notify-error-tab': 'sidenav',
6597 'click .hustle-action-save': 'saveChanges',
6598 'click .wpmudev-button-navigation': 'doButtonNavigation',
6599 'change #hustle-module-name': 'updateModuleName',
6600 'click #hustle-preview-module': 'previewModule',
6601 'blur input.sui-form-control': 'removeErrorMessage',
6602 // Module's actions.
6603 'click .hustle-single-module-button-action': 'handleSingleModuleAction'
6604 },
6605 // ============================================================
6606 // Initialize Wizard
6607 init: function init(opts) {
6608 var _this = this;
6609
6610 this.setTabsViews(opts);
6611 Hustle.Events.on('modules.view.switch_status', function (switchTo) {
6612 return _this.switchStatusTo(switchTo);
6613 });
6614 $(win).off('popstate', $.proxy(this.updateTabOnPopstate, this));
6615 $(win).on('popstate', $.proxy(this.updateTabOnPopstate, this));
6616 $(document).off('tinymce-editor-init', $.proxy(this.tinymceReady, this));
6617 $(document).on('tinymce-editor-init', $.proxy(this.tinymceReady, this));
6618
6619 if ('undefined' !== typeof this._events) {
6620 this.events = $.extend(true, {}, this.events, this._events);
6621 this.delegateEvents();
6622 }
6623
6624 var publishModal = Hustle.get('Modals.PublishFlow');
6625 this.publishModal = new publishModal();
6626 this.renderTabs();
6627 return this;
6628 },
6629
6630 /**
6631 * Assign the tabs views to the object.
6632 * Overridden by social share.
6633 *
6634 * @param {Object} opts Views for each tab.
6635 */
6636 setTabsViews: function setTabsViews(opts) {
6637 this.contentView = opts.contentView;
6638 this.emailsView = opts.emailsView;
6639 this.designView = opts.designView;
6640 this.integrationsView = opts.integrationsView;
6641 this.visibilityView = opts.visibilityView;
6642 this.settingsView = opts.settingsView;
6643 this.moduleType = this.model.get('module_type');
6644
6645 if ('embedded' === this.moduleType) {
6646 this.displayView = opts.displayView;
6647 }
6648 },
6649 // ============================================================
6650 // Render content
6651
6652 /**
6653 * Render the tabs.
6654 * Overridden by social share.
6655 */
6656 renderTabs: function renderTabs() {
6657 // Content view
6658 this.contentView.delegateEvents(); // Emails view
6659
6660 this.emailsView.delegateEvents(); // Integrations view
6661
6662 this.integrationsView.delegateEvents(); // Appearance view
6663
6664 this.designView.delegateEvents(); // Display Options View
6665
6666 if ('embedded' === this.moduleType) {
6667 this.displayView.delegateEvents();
6668 } // Visibility view
6669
6670
6671 this.visibilityView.delegateEvents();
6672 this.visibilityView.afterRender(); // Behavior view
6673
6674 this.settingsView.delegateEvents();
6675 },
6676 // ============================================================
6677 // Side Navigation
6678 sidenav: function sidenav(e) {
6679 e.preventDefault();
6680 var tabName = $(e.target).data('tab');
6681
6682 if (tabName) {
6683 this.goToTab(tabName, true);
6684 }
6685 },
6686 sidenavMobile: function sidenavMobile(e) {
6687 var tabName = $(e.currentTarget).val();
6688
6689 if (tabName) {
6690 this.goToTab(tabName, true);
6691 }
6692 },
6693 goToTab: function goToTab(tabName, updateHistory) {
6694 var $tab = this.$el.find('a[data-tab="' + tabName + '"]'),
6695 $sidenav = $tab.closest('.sui-vertical-tabs'),
6696 $tabs = $sidenav.find('.sui-vertical-tab a'),
6697 $content = this.$el.find('.sui-box[data-tab]'),
6698 $current = this.$el.find('.sui-box[data-tab="' + tabName + '"]');
6699
6700 if (updateHistory) {
6701 // The module id must be defined at this point.
6702 // If it's not, the user should be redirected to the listing page to properly create a module before reaching this.
6703 var state = {
6704 tabName: tabName
6705 },
6706 moduleId = this.model.get('module_id');
6707 history.pushState(state, 'Hustle ' + this.moduleType + ' wizard', 'admin.php?page=' + optinVars.current.wizard_page + '&id=' + moduleId + '&section=' + tabName);
6708 }
6709
6710 $tabs.removeClass('current');
6711 $content.hide();
6712 $tab.addClass('current');
6713 $current.show();
6714 $('.sui-wrap-hustle')[0].scrollIntoView();
6715 },
6716 // Keep the sync of the shown tab and the URL when going "back" with the browser.
6717 updateTabOnPopstate: function updateTabOnPopstate(e) {
6718 var state = e.originalEvent.state;
6719
6720 if (state) {
6721 this.goToTab(state.tabName);
6722 }
6723 },
6724 // Go to he "next" and "previous" tab when using the buttons at the bottom of the wizard.
6725 doButtonNavigation: function doButtonNavigation(e) {
6726 e.preventDefault();
6727 var $button = $(e.target),
6728 direction = 'prev' === $button.data('direction') ? 'prev' : 'next',
6729 nextTabName = this.getNextOrPrevTabName(direction);
6730 this.goToTab(nextTabName, true);
6731 },
6732 // Get the name of the previous or next tab.
6733 getNextOrPrevTabName: function getNextOrPrevTabName(direction) {
6734 var current = $('#hustle-module-wizard-view .sui-sidenav ul li a.current');
6735 var tab = current.data('tab');
6736
6737 if ('prev' === direction) {
6738 tab = current.parent().prev().find('a').data('tab');
6739 } else {
6740 tab = current.parent().next().find('a').data('tab');
6741 }
6742
6743 return tab;
6744 },
6745 // ============================================================
6746 // TinyMCE
6747 // Set the editor content in their respective model on change.
6748 tinymceReady: function tinymceReady(e, editor) {
6749 var _this2 = this;
6750
6751 editor.on('change', function () {
6752 var model = 'main_content' === editor.id ? _this2.contentView.model : _this2.emailsView.model;
6753 model.set(editor.id, editor.getContent());
6754 });
6755 },
6756 setContentFromTinymce: function setContentFromTinymce() {
6757 var keepSilent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
6758
6759 if ('social_sharing' !== this.moduleType && 'undefined' !== typeof tinyMCE) {
6760 // main_content editor
6761 var mainContentEditor = tinyMCE.get('main_content'),
6762 $mainContentTextarea = this.$('textarea#main_content'),
6763 mainContent = 'true' === $mainContentTextarea.attr('aria-hidden') ? mainContentEditor.getContent() : $mainContentTextarea.val();
6764 this.contentView.model.set('main_content', mainContent, {
6765 silent: keepSilent
6766 }); // success_message editor
6767
6768 var successMessageEditor = tinyMCE.get('success_message'),
6769 $successMessageTextarea = this.$('textarea#success_message'),
6770 successMessage = 'true' === $successMessageTextarea.attr('aria-hidden') ? successMessageEditor.getContent() : $successMessageTextarea.val();
6771 this.emailsView.model.set('success_message', successMessage, {
6772 silent: keepSilent
6773 }); // email_body editor
6774
6775 var emailBodyEditor = tinyMCE.get('email_body'),
6776 $emailBodyTextarea = this.$('textarea#email_body'),
6777 emailBody = 'true' === $successMessageTextarea.attr('aria-hidden') ? emailBodyEditor.getContent() : $emailBodyTextarea.val();
6778 this.emailsView.model.set('email_body', emailBody, {
6779 silent: keepSilent
6780 });
6781 }
6782 },
6783 // ============================================================
6784 // Sanitize Data
6785 sanitizeData: function sanitizeData() {
6786 // Call to action
6787 var ctaUrl = this.contentView.model.get('cta_url');
6788
6789 if (0 !== ctaUrl.indexOf('mailto:') && 0 !== ctaUrl.indexOf('tel:')) {
6790 if (!/^(f|ht)tps?:\/\//i.test(ctaUrl)) {
6791 ctaUrl = 'https://' + ctaUrl;
6792 this.contentView.model.set('cta_url', ctaUrl, {
6793 silent: true
6794 });
6795 }
6796 }
6797 },
6798 // ============================================================
6799 // Save changes
6800 save: function save() {
6801 this.setContentFromTinymce(true);
6802 this.sanitizeData(); // Preparig the data
6803
6804 var $this = this.$el.find('#hustle-module-wizard-view'),
6805 id = $this.data('id'),
6806 nonce = $this.data('nonce'),
6807 module = this.model.toJSON();
6808 var data = {
6809 action: 'hustle_save_module',
6810 _ajax_nonce: nonce,
6811 id: id,
6812 module: module
6813 };
6814
6815 _.extend(data, this.getDataToSave()); // ajax save here
6816
6817
6818 return $.ajax({
6819 url: ajaxurl,
6820 type: 'POST',
6821 data: data,
6822 dataType: 'json'
6823 });
6824 },
6825 getDataToSave: function getDataToSave() {
6826 var data = {
6827 content: this.contentView.model.toJSON(),
6828 emails: this.emailsView.model.toJSON(),
6829 design: this.designView.updatedProperties,
6830 // this.designView.model.toJSON(),
6831 integrations_settings: this.integrationsView.model.toJSON(),
6832 // eslint-disable-line camelcase
6833 visibility: this.visibilityView.model.toJSON(),
6834 settings: JSON.stringify(this.settingsView.model.toJSON())
6835 }; // We overwrite the Custom CSS value straight from the editor.
6836
6837 data.design.custom_css = this.designView.cssEditor.getValue(); // eslint-disable-line camelcase
6838
6839 if ('embedded' === this.moduleType) {
6840 data.display = this.displayView.model.toJSON();
6841 }
6842
6843 return data;
6844 },
6845 saveChanges: function saveChanges(e) {
6846 var _this3 = this;
6847
6848 e.preventDefault();
6849 var currentActive = this.model.get('active'),
6850 $saveButton = $(e.currentTarget),
6851 setActiveTo = String($saveButton.data('active')),
6852 activeChanged = setActiveTo !== currentActive,
6853 wasPublished = '0' !== setActiveTo;
6854 this.disableButtonsOnSave($saveButton);
6855
6856 if (activeChanged && wasPublished) {
6857 this.publishModal.open();
6858 }
6859
6860 this.model.set('active', setActiveTo, {
6861 silent: true
6862 });
6863 var save = this.save(); // TODO: handle errors. Like when the nonce expired.
6864
6865 save.done(function (res) {
6866 if (true === res.success) {
6867 // The changes were already saved.
6868 Module.hasChanges = false; // Change the "Pending changes" label to "Saved".
6869
6870 _this3.switchStatusTo('saved');
6871
6872 if (activeChanged) {
6873 if (wasPublished) {
6874 // Ssharing modules don't have schedules.
6875 var isScheduled = 'social_sharing' !== _this3.model.get('module_type') ? '1' === _this3.settingsView.model.get('is_schedule') : false;
6876 var hasEnd = false; // Handle the 'published' modal messages according to the schedule.
6877
6878 if (isScheduled) {
6879 var scheduleSettings = _this3.settingsView.model.get('schedule');
6880
6881 hasEnd = '1' !== scheduleSettings.not_schedule_end;
6882 }
6883
6884 _this3.publishModal.setPublished(isScheduled, hasEnd);
6885 }
6886
6887 _this3.updateViewOnActiveChange();
6888 }
6889 } else {
6890 var errors = res.data;
6891 var errorMessage = ''; // When nonce expired or something we didn't thought about happened errors.data is not defined
6892 // so it's a place to inform the user that he should reload his browser.
6893
6894 if ('undefined' === typeof errors.data) {
6895 errorMessage = optinVars.messages.module_error_reload;
6896 } else {
6897 if ('undefined' !== typeof errors.data.icon_error) {
6898 _.each(errors.data.icon_error, function (error) {
6899 $('#hustle-platform-' + error).find('.sui-error-message').show();
6900 $('#hustle-platform-' + error + ' .hustle-social-url-field').addClass('sui-form-field-error');
6901 $('#hustle-platform-' + error).not('.sui-accordion-item--open').find('.sui-accordion-open-indicator').click();
6902 });
6903
6904 errorMessage = '<a href="#" data-tab="services" class="notify-error-tab"> ' + optinVars.module_tabs.services + ' </a>';
6905 } else if ('undefined' !== typeof errors.data.selector_error) {
6906 _.each(errors.data.selector_error, function (error) {
6907 $('input[name="' + error + '_css_selector"]').siblings('.sui-error-message').show();
6908 $('input[name="' + error + '_css_selector"]').parent('.sui-form-field').addClass('sui-form-field-error');
6909 });
6910
6911 if (!_.isEmpty(errorMessage)) {
6912 errorMessage = errorMessage + ' and ';
6913 }
6914
6915 errorMessage = errorMessage + '<a href="#" data-tab="display" class="notify-error-tab"> ' + optinVars.module_tabs.display + ' </a>';
6916 }
6917
6918 errorMessage = optinVars.messages.module_error.replace('{page}', errorMessage);
6919 }
6920
6921 _this3.switchStatusTo('unsaved');
6922
6923 Module.Notification.open('error', errorMessage, false);
6924 }
6925 }).always(function () {
6926 _this3.enableSaveButtons();
6927 });
6928 },
6929 // ============================================================
6930 // Update the view elements
6931
6932 /**
6933 * Update this module's name if the new value is not empty.
6934 *
6935 * @param {event} e Event.
6936 */
6937 updateModuleName: function updateModuleName(e) {
6938 var $input = $(e.target),
6939 moduleName = $input.val();
6940
6941 if (moduleName.length) {
6942 this.$('#hustle-module-name-wrapper').removeClass('sui-form-field-error');
6943 this.$('#hustle-module-name-error').hide();
6944 this.$('#hustle-module-name-error').html('');
6945 this.model.set('module_name', moduleName);
6946 } else {
6947 var message = this.$('#hustle-module-name-error').data('error-message');
6948 this.$('#hustle-module-name-wrapper').addClass('sui-form-field-error');
6949 this.$('#hustle-module-name-error').html(message);
6950 this.$('#hustle-module-name-error').show();
6951 }
6952 },
6953 // Disable the save buttons.
6954 disableButtonsOnSave: function disableButtonsOnSave($buttonOnLoad) {
6955 $buttonOnLoad.addClass('sui-button-onload');
6956 this.$('.hustle-action-save').prop('disabled', true);
6957 this.$('.wpmudev-button-navigation').prop('disabled', true);
6958 },
6959 // Enable the save buttons.
6960 enableSaveButtons: function enableSaveButtons() {
6961 this.$('.sui-button-onload').removeClass('sui-button-onload');
6962 this.$('.hustle-action-save').prop('disabled', false);
6963 this.$('.wpmudev-button-navigation').prop('disabled', false);
6964 },
6965 // Change the 'saved'/'unsaved' label.
6966 switchStatusTo: function switchStatusTo(switchTo) {
6967 if ('saved' === switchTo) {
6968 this.$el.find('#hustle-unsaved-changes-status').addClass('sui-hidden');
6969 this.$el.find('#hustle-saved-changes-status').removeClass('sui-hidden');
6970 } else {
6971 this.$el.find('#hustle-unsaved-changes-status').removeClass('sui-hidden');
6972 this.$el.find('#hustle-saved-changes-status').addClass('sui-hidden');
6973 }
6974 },
6975 // Change the 'Draft'/'Published' module status label, and update the save buttons for each case.
6976 updateViewOnActiveChange: function updateViewOnActiveChange() {
6977 var isActive = '1' === this.model.get('active'); // Update the module status tag. The one that says if the module is Published or a Draft.
6978
6979 var $statusTag = this.$('.sui-status-module .sui-tag'),
6980 newStatus = isActive ? optinVars.messages.commons.published : optinVars.messages.commons.draft;
6981 $statusTag.text(newStatus);
6982
6983 if (isActive) {
6984 $statusTag.addClass('sui-tag-blue');
6985 } else {
6986 $statusTag.removeClass('sui-tag-blue');
6987 }
6988
6989 var saveDraftContent = this.$('#hustle-draft-button-save-draft-text'),
6990 unpublishContent = this.$('#hustle-draft-button-unpublish-text'); // Update the text of buttons for publishing and unpublishing the module.
6991
6992 if (isActive) {
6993 saveDraftContent.addClass('sui-hidden-important');
6994 unpublishContent.removeClass('sui-hidden-important');
6995 } else {
6996 saveDraftContent.removeClass('sui-hidden-important');
6997 unpublishContent.addClass('sui-hidden-important');
6998 } // Update the text within the Publish button.
6999
7000
7001 var $publishButton = this.$('.hustle-publish-button'),
7002 publishButtonText = isActive ? $publishButton.data('update') : $publishButton.data('publish');
7003 $publishButton.find('.button-text').text(publishButtonText);
7004 },
7005 removeErrorMessage: function removeErrorMessage(e) {
7006 if (e.target.value) {
7007 var parent = $(e.target).parent('.sui-form-field');
7008 parent.removeClass('sui-form-field-error');
7009 parent.find('.sui-error-message').hide();
7010 }
7011 },
7012 // ============================================================
7013 // Previewing
7014 previewModule: function previewModule(e) {
7015 e.preventDefault();
7016 var $button = $(e.currentTarget);
7017 this.setContentFromTinymce(true);
7018 this.sanitizeData();
7019
7020 var previewData = _.extend({}, this.model.toJSON(), this.getDataToSave()),
7021 moduleName = previewData.module_name.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
7022
7023 previewData.module_name = moduleName;
7024 this.getPreviewView().open(this.model.get('module_id'), this.model.get('module_type'), $button, previewData);
7025 },
7026 getPreviewView: function getPreviewView() {
7027 if (!this.previewView) {
7028 var previewView = Hustle.get('Modals.Preview');
7029 this.previewView = new previewView();
7030 }
7031
7032 return this.previewView;
7033 },
7034 // ============================================================
7035 // Module's actions.
7036 handleSingleModuleAction: function handleSingleModuleAction(e) {
7037 Module.handleActions.initAction(e, 'listing', this);
7038 }
7039 };
7040 });
7041 Hustle.define('Settings.Palettes', function ($) {
7042 'use strict';
7043
7044 return Backbone.View.extend({
7045 el: '#palettes-box',
7046 events: {
7047 'click .hustle-create-palette': 'openCreatePaletteModal',
7048 'click .hustle-delete-button': 'openDeletePaletteModal',
7049 'click .hustle-button-delete': 'delettePalette'
7050 },
7051 initialize: function initialize() {
7052 var PaletteModal = Hustle.get('Settings.Palettes_Modal');
7053 this.paletteModal = new PaletteModal();
7054 },
7055 openCreatePaletteModal: function openCreatePaletteModal(e) {
7056 this.paletteModal.open(e);
7057 },
7058 openDeletePaletteModal: function openDeletePaletteModal(e) {
7059 e.preventDefault();
7060 var $this = $(e.currentTarget),
7061 data = {
7062 id: $this.data('id'),
7063 title: $this.data('title'),
7064 description: $this.data('description'),
7065 action: 'delete',
7066 nonce: $this.data('nonce'),
7067 actionClass: 'hustle-button-delete'
7068 };
7069 Module.deleteModal.open(data, $this[0]); // This element is outside the view and only added after opening the modal.
7070
7071 $('.hustle-button-delete').on('click', $.proxy(this.delettePalette, this));
7072 },
7073
7074 /**
7075 * Handle the color palettes 'delete' action.
7076 *
7077 * @since 4.0.3
7078 * @param {Object} e
7079 */
7080 delettePalette: function delettePalette(e) {
7081 e.preventDefault();
7082 var $this = $(e.currentTarget),
7083 relatedFormId = $this.data('form-id'),
7084 actionData = $this.data(),
7085 $form = $('#' + relatedFormId),
7086 data = new FormData($form[0]); // TODO: remove when "hustle_action" field name is changed to "hustleAction"
7087
7088 $.each(actionData, function (name, value) {
7089 return data.append(name, value);
7090 });
7091 data.append('_ajax_nonce', optinVars.settings_palettes_action_nonce);
7092 data.append('action', 'hustle_handle_palette_actions');
7093 $.ajax({
7094 url: ajaxurl,
7095 type: 'POST',
7096 data: data,
7097 contentType: false,
7098 processData: false
7099 }).done(function (res) {
7100 if (res.data.url) {
7101 location.replace(res.data.url);
7102 } else if (res.data.notification) {
7103 Module.Notification.open(res.data.notification.status, res.data.notification.message, res.data.notification.delay);
7104 } // Don't remove the 'loading' icon when redirecting/reloading.
7105
7106
7107 if (!res.data.url) {
7108 $('.sui-button-onload').removeClass('sui-button-onload');
7109 }
7110 }).fail(function () {
7111 Module.Notification.open('error', optinVars.messages.commons.generic_ajax_error);
7112 $('.sui-button-onload').removeClass('sui-button-onload');
7113 });
7114 }
7115 });
7116 });
7117 Hustle.define('Settings.Data_Settings', function ($) {
7118 'use strict';
7119
7120 return Backbone.View.extend({
7121 el: '#data-box',
7122 events: {
7123 'click #hustle-dialog-open--reset-data-settings': 'dataDialog'
7124 },
7125 // ============================================================
7126 // DIALOG: Reset Settings
7127 // Open dialog
7128 dataDialog: function dataDialog(e) {
7129 e.preventDefault();
7130 var $button = this.$(e.target),
7131 $dialog = $('#hustle-dialog--reset-data-settings');
7132 SUI.openModal('hustle-dialog--reset-data-settings', $button[0], $dialog.find('.sui-box-header .sui-button-icon')[0], true);
7133 $('#hustle-reset-settings').off('click').on('click', $.proxy(this.settingsReset));
7134 },
7135 // Confirm and close
7136 settingsReset: function settingsReset(e) {
7137 var $this = $(e.currentTarget),
7138 $dialog = $this.closest('.sui-modal'),
7139 $buttons = $dialog.find('button, .sui-button');
7140 $buttons.prop('disabled', true);
7141 $this.addClass('sui-button-onload');
7142 $.ajax({
7143 url: ajaxurl,
7144 type: 'POST',
7145 data: {
7146 action: 'hustle_reset_settings',
7147 _ajax_nonce: $this.data('nonce') // eslint-disable-line camelcase
7148
7149 },
7150 success: function success() {
7151 $('#' + $this.data('notice')).show();
7152 SUI.closeModal();
7153 $this.removeClass('sui-button-onload');
7154 $buttons.prop('disabled', false);
7155 Module.Notification.open('success', optinVars.messages.settings_was_reset);
7156 window.setTimeout(function () {
7157 return location.reload(true);
7158 }, 2000);
7159 },
7160 error: function error() {
7161 SUI.closeModal();
7162 $this.removeClass('sui-button-onload');
7163 $buttons.prop('disabled', false);
7164 Module.Notification.open('error', optinVars.messages.something_went_wrong);
7165 }
7166 });
7167 }
7168 });
7169 });
7170 Hustle.define('Settings.Palettes_Modal', function ($) {
7171 'use strict';
7172
7173 return Backbone.View.extend({
7174 el: '#hustle-dialog--edit-palette',
7175 events: {
7176 'click .hustle-button-action': 'handleAction',
7177 'click .hustle-modal-close': 'closeCreatePaletteModal',
7178 'change #hustle-palette-module-type': 'updateModulesOptions'
7179 },
7180 initialize: function initialize() {},
7181 open: function open(e) {
7182 var slug = $(e.currentTarget).data('slug');
7183
7184 if ('undefined' !== typeof slug) {
7185 // When editing a palette.
7186 this.handleAction(e);
7187 } else {
7188 // When creating a new palette.
7189 // Update the modules' options when opening.
7190 this.$('#hustle-palette-module-type').trigger('change');
7191 SUI.openModal('hustle-dialog--edit-palette', e.currentTarget, 'hustle-palette-name', false);
7192 }
7193 },
7194
7195 /**
7196 * Handle the color palettes 'save' action.
7197 *
7198 * @since 4.0.3
7199 * @param {Object} e
7200 */
7201 handleAction: function handleAction(e) {
7202 e.preventDefault();
7203 var self = this,
7204 $this = $(e.currentTarget),
7205 relatedFormId = $this.data('form-id');
7206 $this.addClass('sui-button-onload');
7207 Module.Utils.accessibleHide(this.$('.sui-error-message'));
7208 var data = new FormData(),
7209 errors = false; // Grab the form's data if the action has a related form.
7210
7211 if ('undefined' !== typeof relatedFormId) {
7212 var $form = $('#' + relatedFormId);
7213
7214 if ($form.length) {
7215 data = new FormData($form[0]);
7216 $form.find('.hustle-required-field').each(function (i, el) {
7217 var $field = $(el);
7218
7219 if (!$field.val().trim().length) {
7220 var errorMessage = $field.data('error-message'),
7221 $errorMessage = $field.siblings('.sui-error-message');
7222 $errorMessage.html(errorMessage);
7223 Module.Utils.accessibleShow($errorMessage);
7224 errors = true;
7225 }
7226 });
7227 }
7228 } // Don't do the request if there are missing required fields.
7229
7230
7231 if (errors) {
7232 $('.sui-button-onload').removeClass('sui-button-onload');
7233 return;
7234 }
7235
7236 var actionData = $this.data();
7237 $.each(actionData, function (name, value) {
7238 return data.append(name, value);
7239 });
7240 data.append('_ajax_nonce', optinVars.settings_palettes_action_nonce);
7241 data.append('action', 'hustle_handle_palette_actions');
7242 $.ajax({
7243 url: ajaxurl,
7244 type: 'POST',
7245 data: data,
7246 contentType: false,
7247 processData: false
7248 }).done(function (res) {
7249 // If there's a defined callback, call it.
7250 if (res.data.callback && 'function' === typeof self[res.data.callback]) {
7251 // This calls the "action{ hustle action }" functions from this view.
7252 // For example: actionToggleStatus();
7253 self[res.data.callback](res.data, res.success, e);
7254 } else if (res.data.url) {
7255 location.replace(res.data.url);
7256 } else if (res.data.notification) {
7257 Module.Notification.open(res.data.notification.status, res.data.notification.message, res.data.notification.delay);
7258 } // Don't remove the 'loading' icon when redirecting/reloading.
7259
7260
7261 if (!res.data.url) {
7262 $('.sui-button-onload').removeClass('sui-button-onload');
7263 }
7264 }).fail(function () {
7265 $('.sui-button-onload').removeClass('sui-button-onload');
7266 });
7267 },
7268 actionOpenEditPalette: function actionOpenEditPalette(data, success, e) {
7269 this.actionGoToSecondStep(data);
7270 SUI.openModal('hustle-dialog--edit-palette', e.currentTarget, 'hustle-palette-name', false);
7271
7272 if (data.palette_data.name) {
7273 $('#hustle-dialog--edit-palette').find('#hustle-palette-name').val(data.palette_data.name);
7274 }
7275 },
7276 actionGoToSecondStep: function actionGoToSecondStep(data) {
7277 var stepOne = this.$('#hustle-edit-palette-first-step'),
7278 stepTwo = this.$('#hustle-edit-palette-second-step'),
7279 btnAction = this.$('.hustle-button-action'),
7280 paletteData = data.palette_data,
7281 template = Optin.template('hustle-dialog--edit-palette-tpl'); // Hide first step
7282
7283 Module.Utils.accessibleHide(stepOne, true); // Print and show second step
7284
7285 stepTwo.html(template(paletteData));
7286 this.initiateSecondStepElements();
7287 Module.Utils.accessibleShow(stepTwo, true);
7288 stepTwo.focus(); // Set new step
7289
7290 btnAction.data('step', 3);
7291 btnAction.addClass('sui-button-blue');
7292 Module.Utils.accessibleHide(btnAction.find('#hustle-step-button-text'));
7293 Module.Utils.accessibleShow(btnAction.find('#hustle-finish-button-text'));
7294 },
7295 initiateSecondStepElements: function initiateSecondStepElements() {
7296 // Accordions.
7297 this.$('.sui-accordion').each(function () {
7298 SUI.suiAccordion(this);
7299 }); // Init tabs
7300
7301 SUI.suiTabs();
7302 SUI.tabs(); // Color pickers.
7303
7304 this.createPickers();
7305 },
7306 closeCreatePaletteModal: function closeCreatePaletteModal() {
7307 var self = this,
7308 stepOne = this.$('#hustle-edit-palette-first-step'),
7309 stepTwo = this.$('#hustle-edit-palette-second-step'),
7310 btnAction = this.$('.hustle-button-action');
7311 setTimeout(function () {
7312 // Hide error messages
7313 Module.Utils.accessibleHide(self.$('.sui-error-message')); // Hide second step
7314
7315 Module.Utils.accessibleHide(stepTwo, true);
7316 stepTwo.html(''); // Show first step
7317
7318 Module.Utils.accessibleShow(stepOne, true); // Reset action button
7319
7320 btnAction.removeClass('sui-button-blue');
7321 btnAction.data('step', 2);
7322 Module.Utils.accessibleShow(btnAction.find('#hustle-step-button-text'));
7323 Module.Utils.accessibleHide(btnAction.find('#hustle-finish-button-text'));
7324 }, 500);
7325 },
7326 // ============================================================
7327 // Color Pickers
7328 // TODO: Copied from wizards. Re-use instead of copy-pasting
7329 createPickers: function createPickers() {
7330 var $suiPickerInputs = this.$('.sui-colorpicker-input');
7331 $suiPickerInputs.wpColorPicker({
7332 change: function change(event, ui) {
7333 var $this = $(this); // Prevent the model from being marked as changed on load.
7334
7335 if ($this.val() !== ui.color.toCSS()) {
7336 $this.val(ui.color.toCSS()).trigger('change');
7337 }
7338 },
7339 palettes: ['#333333', '#FFFFFF', '#17A8E3', '#E1F6FF', '#666666', '#AAAAAA', '#E6E6E6']
7340 });
7341
7342 if ($suiPickerInputs.hasClass('wp-color-picker')) {
7343 $suiPickerInputs.each(function () {
7344 var $suiPickerInput = $(this),
7345 $suiPicker = $suiPickerInput.closest('.sui-colorpicker-wrap'),
7346 $suiPickerColor = $suiPicker.find('.sui-colorpicker-value span[role=button]'),
7347 $suiPickerValue = $suiPicker.find('.sui-colorpicker-value'),
7348 $suiPickerClear = $suiPickerValue.find('button'),
7349 $shownInput = $suiPickerValue.find('.hustle-colorpicker-input');
7350 var $wpPicker = $suiPickerInput.closest('.wp-picker-container'),
7351 $wpPickerButton = $wpPicker.find('.wp-color-result'),
7352 $wpPickerAlpha = $wpPickerButton.find('.color-alpha'),
7353 $wpPickerClear = $wpPicker.find('.wp-picker-clear');
7354 var $suiPickerType = 'hex'; // Check if alpha exists
7355
7356 if (true === $suiPickerInput.data('alpha')) {
7357 $suiPickerType = 'rgba'; // Listen to color change
7358
7359 $suiPickerInput.on('change', function () {
7360 // Change color preview
7361 $suiPickerColor.find('span').css({
7362 'background-color': $wpPickerAlpha.css('background')
7363 }); // We trigger this 'change' manually when the shown input changes.
7364 // Don't update its value again if this is the case.
7365
7366 if ('undefined' === typeof data) {
7367 // Change color value
7368 $shownInput.val($suiPickerInput.val());
7369 }
7370 });
7371 } else {
7372 // Listen to color change
7373 $suiPickerInput.on('change', function () {
7374 // Change color preview
7375 $suiPickerColor.find('span').css({
7376 'background-color': $wpPickerButton.css('background-color')
7377 }); // We trigger this 'change' manually when the shown input changes.
7378 // Don't update its value again if this is the case.
7379
7380 if ('undefined' === typeof data) {
7381 // Change color value
7382 $shownInput.val($suiPickerInput.val());
7383 }
7384 });
7385 } // Allow updating the colors without having to open the colorpicker.
7386
7387
7388 $shownInput.on('change', function () {
7389 // Change color value
7390 $suiPickerInput.val($shownInput.val());
7391 $suiPickerInput.trigger('change', [{
7392 triggeredByUs: true
7393 }]);
7394 }); // Add picker type class
7395
7396 $suiPicker.find('.sui-colorpicker').addClass('sui-colorpicker-' + $suiPickerType); // Open iris picker
7397
7398 $suiPicker.find('.sui-button, span[role=button]').on('click', function (e) {
7399 $wpPickerButton.click();
7400 e.preventDefault();
7401 e.stopPropagation();
7402 }); // Clear color value
7403
7404 $suiPickerClear.on('click', function (e) {
7405 var inputName = $suiPickerInput.data('attribute'),
7406 selectedStyle = $('#hustle-palette-module-fallback').val(),
7407 resetValue = optinVars.palettes[selectedStyle][inputName];
7408 $wpPickerClear.click();
7409 $suiPickerValue.find('input').val(resetValue);
7410 $suiPickerInput.val(resetValue).trigger('change');
7411 $suiPickerColor.find('span').css({
7412 'background-color': resetValue
7413 });
7414 e.preventDefault();
7415 e.stopPropagation();
7416 });
7417 });
7418 }
7419 },
7420 updateModulesOptions: function updateModulesOptions(e) {
7421 var $this = $(e.currentTarget),
7422 self = this,
7423 moduleType = $this.val(),
7424 $modulesOptionsSelect = this.$('#hustle-palette-module-name');
7425 var html = '';
7426 $.each(optinVars.current[moduleType], function (id, name) {
7427 html += "<option value=\"".concat(id, "\">").concat(name, "</option>");
7428 });
7429 $modulesOptionsSelect.html(html);
7430 this.$('.sui-select:not(.hustle-select-ajax)').SUIselect2({
7431 dropdownCssClass: 'sui-select-dropdown',
7432 dropdownParent: self.$('.sui-box')
7433 });
7434 }
7435 });
7436 });
7437 Hustle.define('Settings.Permissions_View', function ($) {
7438 'use strict';
7439
7440 return Backbone.View.extend({
7441 el: '#permissions-box',
7442 initialize: function initialize() {
7443 $(function () {
7444 //Delete the remove ability for Administrator option in select2
7445 function blockingAdminRemove() {
7446 $('.select2-selection__rendered li:first-child .select2-selection__choice__remove').off('click').text('').on('click', function (e) {
7447 e.stopImmediatePropagation();
7448 e.preventDefault();
7449 });
7450 }
7451
7452 $('select').on('change.select2', function () {
7453 blockingAdminRemove();
7454 });
7455 blockingAdminRemove();
7456 });
7457 }
7458 });
7459 });
7460 Hustle.define('Settings.Privacy_Settings', function ($) {
7461 'use strict';
7462
7463 return Backbone.View.extend({
7464 el: '#privacy-box',
7465 events: {
7466 'click #hustle-dialog-open--delete-ips': 'openDeleteIpsDialog'
7467 },
7468 initialize: function initialize() {
7469 $('#hustle-delete-ips-submit').on('click', this.handleIpDeletion);
7470 },
7471 // ============================================================
7472 // DIALOG: Delete All IPs
7473 // Open dialog
7474 openDeleteIpsDialog: function openDeleteIpsDialog(e) {
7475 SUI.openModal('hustle-dialog--delete-ips', $(e.currentTarget)[0], this.$('#hustle-dialog--delete-ips .sui-box-header .sui-button-icon')[0], true);
7476 e.preventDefault();
7477 },
7478 handleIpDeletion: function handleIpDeletion(e) {
7479 e.preventDefault();
7480 var $this = $(e.currentTarget),
7481 $form = $('#' + $this.data('formId')),
7482 data = new FormData($form[0]);
7483 data.append('action', 'hustle_remove_ips');
7484 data.append('_ajax_nonce', $this.data('nonce'));
7485 $this.addClass('sui-button-onload');
7486 $.ajax({
7487 url: ajaxurl,
7488 type: 'POST',
7489 data: data,
7490 contentType: false,
7491 processData: false,
7492 success: function success(res) {
7493 Module.Notification.open('success', res.data.message);
7494 SUI.closeModal();
7495 $('.sui-button-onload').removeClass('sui-button-onload');
7496 },
7497 error: function error() {
7498 SUI.closeModal();
7499 $('.sui-button-onload').removeClass('sui-button-onload');
7500 Module.Notification.open('error', optinVars.messages.something_went_wrong);
7501 }
7502 });
7503 }
7504 });
7505 });
7506 Hustle.define('Settings.reCaptcha_Settings', function ($) {
7507 'use strict';
7508
7509 return Backbone.View.extend({
7510 el: '#recaptcha-box',
7511 data: {},
7512 initialize: function initialize() {
7513 this.maybeRenderRecaptchas();
7514 },
7515 maybeRenderRecaptchas: function maybeRenderRecaptchas() {
7516 var _this = this;
7517
7518 var self = this,
7519 versions = ['v2_checkbox', 'v2_invisible', 'v3_recaptcha'];
7520 var scriptAdded = false;
7521
7522 var _loop = function _loop() {
7523 var version = _versions[_i];
7524
7525 var $previewContainer = _this.$("#hustle-modal-recaptcha-".concat(version, "-0")),
7526 sitekey = _this.$("input[name=\"".concat(version, "_site_key\"]")).val().trim(),
7527 secretkey = _this.$("input[name=\"".concat(version, "_secret_key\"]")).val().trim();
7528
7529 if (sitekey && secretkey) {
7530 $previewContainer.data('sitekey', sitekey);
7531
7532 if (!scriptAdded) {
7533 $.ajax({
7534 url: ajaxurl,
7535 type: 'POST',
7536 data: {
7537 action: 'hustle_load_recaptcha_preview'
7538 }
7539 }).done(function (result) {
7540 if (result.success) {
7541 scriptAdded = true;
7542 self.$('#hustle-recaptcha-script-container').html(result.data);
7543 setTimeout(function () {
7544 return HUI.maybeRenderRecaptcha($previewContainer.closest('.sui-form-field'));
7545 }, 1000);
7546 }
7547 });
7548 } else {
7549 HUI.maybeRenderRecaptcha($previewContainer.closest('.sui-form-field'));
7550 }
7551
7552 _this.$(".hustle-recaptcha-".concat(version, "-preview-notice")).hide();
7553
7554 $previewContainer.show();
7555 } else {
7556 _this.$(".hustle-recaptcha-".concat(version, "-preview-notice")).show();
7557
7558 $previewContainer.hide();
7559 }
7560 };
7561
7562 for (var _i = 0, _versions = versions; _i < _versions.length; _i++) {
7563 _loop();
7564 }
7565 }
7566 });
7567 });
7568 Hustle.define('Settings.Top_Metrics_View', function () {
7569 'use strict';
7570
7571 return Backbone.View.extend({
7572 el: '#top-metrics-box',
7573 events: {
7574 'click .sui-checkbox input': 'maybeDisableInputs'
7575 },
7576 initialize: function initialize() {
7577 this.maybeDisableInputs();
7578 },
7579 maybeDisableInputs: function maybeDisableInputs() {
7580 var $allchecked = this.$el.find('input:checked'),
7581 $unchecked = this.$el.find('input:not(:checked)'),
7582 $button = this.$el.find('button[type="submit"]'),
7583 $buttonTip = $button.parent(),
7584 $design = $unchecked.next('span');
7585
7586 if (3 <= $allchecked.length) {
7587 $unchecked.prop('disabled', true);
7588 $design.addClass('sui-tooltip');
7589 $design.css('opacity', '1');
7590 $button.prop('disabled', false);
7591 $buttonTip.removeClass('sui-tooltip');
7592 } else {
7593 $button.prop('disabled', true);
7594 $unchecked.prop('disabled', false);
7595 $design.removeClass('sui-tooltip');
7596 $design.css('opacity', '');
7597 $buttonTip.addClass('sui-tooltip');
7598 }
7599 }
7600 });
7601 });
7602 Hustle.define('Integration_Modal_Handler', function ($) {
7603 'use strict';
7604
7605 return Backbone.View.extend({
7606 events: {
7607 'click .hustle-provider-connect': 'connectAddOn',
7608 'click .hustle-provider-disconnect': 'disconnectAddOn',
7609 'click .hustle-provider-next': 'submitNextStep',
7610 'click .hustle-provider-back': 'goPrevStep',
7611 'click .hustle-refresh-email-lists': 'refreshLists',
7612 'click .hustle-provider-form-disconnect': 'disconnectAddOnForm',
7613 'click .hustle-provider-clear-radio-options': 'clearRadioOptions',
7614 'change select#group': 'showInterests'
7615 },
7616 initialize: function initialize(options) {
7617 var _this = this;
7618
7619 this.slug = options.slug;
7620 this.nonce = options.nonce;
7621 this.action = options.action; // eslint-disable-next-line camelcase
7622
7623 this.moduleId = options.moduleId; // eslint-disable-next-line camelcase
7624
7625 this.multi_id = options.multiId;
7626 this.globalMultiId = options.globalMultiId;
7627 this.step = 0; // eslint-disable-next-line camelcase
7628
7629 this.next_step = false; // eslint-disable-next-line camelcase
7630
7631 this.prev_step = false; // Attach close actions to SUI's modal close event.
7632
7633 this.$el.off('close').on('close', function () {
7634 return _this.modalClosed();
7635 });
7636 return this.render();
7637 },
7638 render: function render() {
7639 var data = {};
7640 data.action = this.action; // eslint-disable-next-line camelcase
7641
7642 data._ajax_nonce = this.nonce;
7643 data.data = {};
7644 data.data.slug = this.slug;
7645 data.data.step = this.step; // eslint-disable-next-line camelcase
7646
7647 data.data.current_step = this.step;
7648
7649 if (this.moduleId) {
7650 // eslint-disable-next-line camelcase
7651 data.data.module_id = this.moduleId;
7652 }
7653
7654 if (this.multi_id) {
7655 // eslint-disable-next-line camelcase
7656 data.data.multi_id = this.multi_id;
7657 }
7658
7659 if (this.globalMultiId) {
7660 // eslint-disable-next-line camelcase
7661 data.data.global_multi_id = this.globalMultiId;
7662 }
7663
7664 this.request(data, false, true);
7665 },
7666 applyLoader: function applyLoader($element) {
7667 $element.find('.sui-button:not(.disable-loader)').addClass('sui-button-onload');
7668 },
7669 resetLoader: function resetLoader($element) {
7670 $element.find('.sui-button').removeClass('sui-button-onload');
7671 },
7672 request: function request(data, close, loader) {
7673 var self = this;
7674
7675 if (loader) {
7676 this.$el.find('.sui-box-body').addClass('sui-block-content-center').html( // TODO: translate "loading content".
7677 '<p class="sui-loading-dialog" aria-label="Loading content"><i class="sui-icon-loader sui-loading" aria-hidden="true"></i></p>');
7678 this.$el.find('.sui-box-footer').html('');
7679 this.$el.find('.integration-header').html('');
7680 }
7681
7682 this.applyLoader(this.$el);
7683 this.ajax = $.post({
7684 url: ajaxurl,
7685 type: 'post',
7686 data: data
7687 }).done(function (result) {
7688 if (result && result.success) {
7689 // Shorten result data
7690 var resultData = result.data.data; // Handle close modal
7691
7692 if (close || !_.isUndefined(resultData.is_close) && resultData.is_close) {
7693 SUI.closeModal();
7694 } else {
7695 // Render popup body
7696 self.renderBody(result); // Render popup footer
7697
7698 self.renderFooter(result);
7699 self.onRender(resultData);
7700 self.resetLoader(self.$el);
7701 } // Handle notifications
7702
7703
7704 if (!_.isUndefined(resultData.notification) && !_.isUndefined(resultData.notification.type) && !_.isUndefined(resultData.notification.text)) {
7705 var custom = Module.Notification;
7706 custom.open(resultData.notification.type, resultData.notification.text);
7707 } // Handle the modal's size.
7708
7709
7710 if (!_.isUndefined(resultData.size)) {
7711 var $modal = self.$el.closest('.sui-modal'); // Remove all sizes
7712
7713 if (resultData.size === 'normal') {
7714 $modal.removeClass('sui-modal-sm sui-modal-lg');
7715 }
7716
7717 if (resultData.size === 'small') {
7718 $modal.addClass('sui-modal-sm');
7719 $modal.removeClass('sui-modal-lg');
7720 }
7721
7722 if (resultData.size === 'large') {
7723 $modal.addClass('sui-modal-lg');
7724 $modal.removeClass('sui-modal-sm');
7725 }
7726 } // Show Mailchimp interests is Group is already choosen
7727
7728
7729 if ('mailchimp' === self.slug) {
7730 var group = self.$el.find('#group');
7731
7732 if (group.length) {
7733 group.trigger('change');
7734 }
7735 } // There's a response, but not a successful one.
7736
7737 } else {
7738 // Render popup body
7739 self.renderBody(result); // Render popup footer
7740
7741 self.renderFooter(result);
7742 }
7743 }); // Remove the preloader
7744
7745 this.ajax.always(function (result) {
7746 // Add closing event.
7747 self.$el.find('.hustle-modal-close').off('click').on('click', function () {
7748 SUI.closeModal();
7749 });
7750 self.$el.find('.sui-box-body').removeClass('sui-block-content-center');
7751 self.$el.find('.sui-loading-dialog').remove();
7752
7753 if (!result || !result.success || !result.data) {
7754 self.showGenericErrorMessage();
7755 }
7756 });
7757 },
7758 renderBody: function renderBody(result) {
7759 if (!result.data || !result.data.data) {
7760 return;
7761 }
7762
7763 this.$el.find('.sui-box-body').html(result.data.data.html); // append header to integration-header
7764
7765 var integrationHeader = this.$el.find('.sui-box-body .integration-header').remove();
7766
7767 if (0 < integrationHeader.length) {
7768 this.$el.find('.integration-header').html(integrationHeader.html());
7769 } // Hide empty content
7770
7771
7772 if (!this.$el.find('.sui-box-body').html().trim().length) {
7773 this.$el.find('.sui-box-body').addClass('sui-hidden');
7774 this.$el.find('.sui-box-footer').css('padding-top', '');
7775 } else {
7776 var children = this.$el.find('.sui-box-body').children();
7777 var hideBody = true;
7778 $.each(children, function (i, child) {
7779 if (!$(child).is(':hidden')) {
7780 hideBody = false;
7781 }
7782 }); // Hide the content only when all children are hidden.
7783
7784 if (hideBody) {
7785 this.$el.find('.sui-box-body').addClass('sui-hidden');
7786 this.$el.find('.sui-box-footer').css('padding-top', '');
7787 } else {
7788 // FIX: Prevent extra spacing.
7789 // eslint-disable-next-line no-lonely-if
7790 if (this.$el.find('.sui-box-body .sui-notice').next().is('input[type="hidden"]')) {
7791 this.$el.find('.sui-box-body .sui-notice').css({
7792 'margin-bottom': '0'
7793 });
7794 }
7795 }
7796 }
7797 },
7798 renderFooter: function renderFooter(result) {
7799 if (!result.data || !result.data.data) {
7800 return;
7801 }
7802
7803 var self = this,
7804 buttons = result.data.data.buttons,
7805 body = self.$el.find('.sui-box-body'),
7806 footer = self.$el.find('.sui-box-footer'); // Clear the body's spacing classes.
7807
7808 body.removeClass('sui-spacing-bottom--0').removeClass('sui-spacing-bottom--30'); // Clear footer from previous buttons
7809
7810 footer.removeClass('sui-hidden').removeClass('sui-hidden-important').removeClass('sui-content-center').removeClass('sui-content-right').removeClass('sui-content-separated').html(''); // Append buttons
7811
7812 _.each(buttons, function (button) {
7813 footer.append(button.markup);
7814 });
7815
7816 if (0 === footer.find('.sui-button').length) {
7817 footer.addClass('sui-hidden-important');
7818 body.addClass('sui-spacing-bottom--30');
7819 } else {
7820 // The footer is shown. It has the required spacing.
7821 body.addClass('sui-spacing-bottom--0'); // FIX: Align buttons to center.
7822
7823 if (footer.find('.sui-button').hasClass('sui-button-center')) {
7824 footer.addClass('sui-content-center'); // FIX: Align buttons to right.
7825 } else if (footer.find('.sui-button').hasClass('sui-button-right')) {
7826 if (!footer.find('.sui-button').hasClass('sui-button-left')) {
7827 footer.addClass('sui-content-right');
7828 } // FIX: Align buttons separated.
7829
7830 } else {
7831 footer.addClass('sui-content-separated');
7832 }
7833 }
7834 },
7835 onRender: function onRender(result) {
7836 var self = this;
7837 this.delegateEvents(); // Update current step
7838
7839 if (!_.isUndefined(result.opt_in_provider_current_step)) {
7840 this.step = +result.opt_in_provider_current_step;
7841 } // Update has next step
7842
7843
7844 if (!_.isUndefined(result.opt_in_provider_has_next_step)) {
7845 // eslint-disable-next-line camelcase
7846 this.next_step = result.opt_in_provider_has_next_step;
7847 } // Update has prev step
7848
7849
7850 if (!_.isUndefined(result.opt_in_provider_has_prev_step)) {
7851 // eslint-disable-next-line camelcase
7852 this.prev_step = result.opt_in_provider_has_prev_step;
7853 }
7854
7855 self.$el.find('.sui-select').SUIselect2({
7856 dropdownCssClass: 'sui-select-dropdown',
7857 dropdownParent: $('#hustle-integration-dialog .sui-box')
7858 });
7859 },
7860 refreshLists: function refreshLists(e) {
7861 var _this2 = this;
7862
7863 e.preventDefault();
7864 e.stopPropagation();
7865 var $this = $(e.currentTarget),
7866 $oldSelect = $('#list_id').length ? $('#list_id') : $('#form_id'),
7867 id = this.moduleId,
7868 slug = this.slug,
7869 type = $('#form_id').length ? 'forms' : 'lists',
7870 nonce = this.nonce;
7871 $this.addClass('sui-button-onload');
7872 $.ajax({
7873 url: ajaxurl,
7874 type: 'POST',
7875 data: {
7876 action: 'hustle_refresh_email_lists',
7877 id: id,
7878 slug: slug,
7879 type: type,
7880 _ajax_nonce: nonce // eslint-disable-line camelcase
7881
7882 }
7883 }).done(function (result) {
7884 if (result.success) {
7885 if ('undefined' !== typeof result.data.select) {
7886 var $refreshContainer = $oldSelect.closest('.hui-select-refresh');
7887 $oldSelect.SUIselect2('destroy');
7888 $oldSelect.remove();
7889 var $newSelect = $(result.data.select);
7890 $refreshContainer.prepend($newSelect);
7891 $newSelect.SUIselect2({
7892 dropdownParent: _this2.$('.sui-box'),
7893 dropdownCssClass: 'sui-select-dropdown'
7894 });
7895 }
7896 }
7897 }).fail(function () {// TODO: handle errors
7898 }).always(function () {
7899 $this.removeClass('sui-button-onload');
7900 });
7901 },
7902 submitNextStep: function submitNextStep() {
7903 var data = {},
7904 form = this.$el.find('form'),
7905 params = {
7906 slug: this.slug,
7907 step: this.getStep(),
7908 // eslint-disable-next-line camelcase
7909 current_step: this.step
7910 };
7911 var formData = form.serialize();
7912
7913 if (this.moduleId) {
7914 // eslint-disable-next-line camelcase
7915 params.module_id = this.moduleId;
7916 }
7917
7918 formData = formData + '&' + $.param(params);
7919 data.action = this.action; // eslint-disable-next-line camelcase
7920
7921 data._ajax_nonce = this.nonce;
7922 data.data = formData;
7923 this.request(data, false, false);
7924 },
7925 goPrevStep: function goPrevStep() {
7926 var data = {},
7927 params = {
7928 slug: this.slug,
7929 step: this.getPrevStep(),
7930 current_step: this.step
7931 };
7932
7933 if (this.moduleId) {
7934 // eslint-disable-next-line camelcase
7935 params.module_id = this.moduleId;
7936 }
7937
7938 if (this.multi_id) {
7939 // eslint-disable-next-line camelcase
7940 params.multi_id = this.multi_id;
7941 }
7942
7943 data.action = this.action; // eslint-disable-next-line camelcase
7944
7945 data._ajax_nonce = this.nonce;
7946 data.data = params;
7947 this.request(data, false, false);
7948 },
7949 getStep: function getStep() {
7950 if (this.next_step) {
7951 return this.step + 1;
7952 }
7953
7954 return this.step;
7955 },
7956 getPrevStep: function getPrevStep() {
7957 if (this.prev_step) {
7958 return this.step - 1;
7959 }
7960
7961 return this.step;
7962 },
7963 connectAddOn: function connectAddOn() {
7964 var data = {},
7965 form = this.$el.find('form'),
7966 params = {
7967 slug: this.slug,
7968 step: this.getStep(),
7969 // eslint-disable-next-line camelcase
7970 current_step: this.step
7971 };
7972 var formData = form.serialize();
7973
7974 if (this.moduleId) {
7975 // eslint-disable-next-line camelcase
7976 params.module_id = this.moduleId;
7977 }
7978
7979 if (this.multi_id) {
7980 // eslint-disable-next-line camelcase
7981 params.multi_id = this.multi_id;
7982 }
7983
7984 formData = formData + '&' + $.param(params);
7985 data.action = this.action; // eslint-disable-next-line camelcase
7986
7987 data._ajax_nonce = this.nonce;
7988 data.data = formData;
7989 this.request(data, false, false);
7990 },
7991 disconnectAddOn: function disconnectAddOn() {
7992 var self = this,
7993 img = this.$el.find('.sui-box-logo img').attr('src'),
7994 title = this.$el.find('#dialogTitle2').html(),
7995 data = {},
7996 isActiveData = {};
7997 var modules = {};
7998 data.action = 'hustle_provider_deactivate'; // eslint-disable-next-line camelcase
7999
8000 data._ajax_nonce = this.nonce;
8001 data.data = {};
8002 data.data.slug = this.slug;
8003 data.data.img = img;
8004 data.data.title = title;
8005
8006 if (this.globalMultiId) {
8007 // eslint-disable-next-line camelcase
8008 data.data.global_multi_id = this.globalMultiId;
8009 }
8010
8011 isActiveData.action = 'hustle_provider_is_on_module'; // eslint-disable-next-line camelcase
8012
8013 isActiveData._ajax_nonce = this.nonce;
8014 isActiveData.data = {};
8015 isActiveData.data.slug = this.slug;
8016 isActiveData.data.globalMultiId = this.globalMultiId;
8017 this.$el.find('.sui-button:not(.disable-loader)').addClass('sui-button-onload');
8018 $.ajax({
8019 url: ajaxurl,
8020 type: 'POST',
8021 data: isActiveData,
8022 success: function success(resp) {
8023 if (true === resp.success) {
8024 modules = resp;
8025 }
8026 },
8027 complete: function complete() {
8028 if (true === modules.success) {
8029 Module.integrationsActiveRemove.open(modules.data, data, self);
8030 } else {
8031 self.request(data, true, false);
8032 }
8033 }
8034 });
8035 },
8036 disconnectAddOnForm: function disconnectAddOnForm() {
8037 var self = this;
8038 var data = {};
8039 var active = $('#hustle-integrations-active-count').val(),
8040 activeIntegration = $('#hustle-integrations-active-integrations').val();
8041 data.action = 'hustle_provider_form_deactivate'; // eslint-disable-next-line camelcase
8042
8043 data._ajax_nonce = this.nonce;
8044 data.data = {};
8045 data.data.slug = this.slug; // eslint-disable-next-line camelcase
8046
8047 data.data.module_id = this.moduleId;
8048
8049 if (this.multi_id) {
8050 // eslint-disable-next-line camelcase
8051 data.data.multi_id = this.multi_id;
8052 }
8053
8054 if ('1' === active && activeIntegration === this.slug && 'local_list' !== this.slug) {
8055 Module.integrationsAllRemove.open(data, self);
8056 } else if ('1' === active && 'local_list' === this.slug) {
8057 Module.Notification.open('error', optinVars.messages.integraiton_required);
8058 SUI.closeModal();
8059 } else {
8060 this.request(data, true, false);
8061 }
8062 },
8063 modalClosed: function modalClosed() {
8064 // Kill AJAX hearbeat
8065 this.ajax.abort(); // Refrest add-on list
8066
8067 Hustle.Events.trigger('hustle:providers:reload');
8068 },
8069 clearRadioOptions: function clearRadioOptions() {
8070 this.$('input[type=radio]', this.$el).prop('checked', false);
8071 },
8072 //show interests for mailchimp
8073 showInterests: function showInterests(e) {
8074 var self = this,
8075 $this = $(e.currentTarget),
8076 nonce = $this.data('nonce'),
8077 group = $this.val(),
8078 data = {},
8079 form = self.$el.find('form'),
8080 params = {
8081 slug: self.slug,
8082 group: group,
8083 module_id: self.moduleId
8084 };
8085 var formData = form.serialize();
8086 formData = formData + '&' + $.param(params);
8087 data.action = 'hustle_mailchimp_get_group_interests'; // eslint-disable-next-line camelcase
8088
8089 data._ajax_nonce = nonce;
8090 data.data = formData;
8091 self.applyLoader(self.$el);
8092 $.ajax({
8093 url: ajaxurl,
8094 type: 'POST',
8095 data: data
8096 }).done(function (result) {
8097 if (result.success) {
8098 form.find('.sui-form-field').slice(1).remove();
8099 form.find('.sui-form-field:first-child').after(result.data);
8100 }
8101 }).fail(function () {// TODO: handle errors
8102 }).always(function () {
8103 self.resetLoader(self.$el);
8104 });
8105 },
8106 showGenericErrorMessage: function showGenericErrorMessage() {
8107 this.$el.find('.sui-box-body').html('<div id="hustle-request-error-reload-notice" role="alert" aria-live="assertive" class="sui-notice"></div>');
8108 this.$el.find('.sui-box-footer').html('');
8109 SUI.openNotice('hustle-request-error-reload-notice', '<p>' + optinVars.messages.request_error_reload_notice + '</p>', {
8110 type: "error",
8111 autoclose: {
8112 show: false
8113 }
8114 });
8115 }
8116 });
8117 });
8118 var Module = window.Module || {};
8119 Hustle.define('Model', function ($) {
8120 'use strict';
8121
8122 return Backbone.Model.extend({
8123 initialize: function initialize() {
8124 this.on('change', this.userHasChange, this);
8125 Backbone.Model.prototype.initialize.apply(this, arguments);
8126 $(window).on('beforeunload', this.changesNotSaved);
8127 },
8128 userHasChange: function userHasChange() {
8129 Module.hasChanges = true; // Add the "unsaved" status tag to the module screen.
8130
8131 Hustle.Events.trigger('modules.view.switch_status', 'unsaved');
8132 },
8133 changesNotSaved: function changesNotSaved() {
8134 if (Module.hasChanges) {
8135 return 'You have unsaved changes'; // This message is ignored anyway.
8136 }
8137 }
8138 });
8139 });
8140 Hustle.define('Models.M', function () {
8141 'use strict';
8142
8143 return Hustle.get('Model').extend({
8144 toJSON: function toJSON() {
8145 var json = _.clone(this.attributes);
8146
8147 var attr;
8148
8149 for (attr in json) {
8150 if (json[attr] instanceof Backbone.Model || json[attr] instanceof Backbone.Collection) {
8151 json[attr] = json[attr].toJSON();
8152 }
8153 }
8154
8155 return json;
8156 },
8157 set: function set(key, val, options) {
8158 var parent, child, parentModel;
8159
8160 if ('string' === typeof key && -1 !== key.indexOf('.')) {
8161 parent = key.split('.')[0];
8162 child = key.split('.')[1];
8163 parentModel = this.get(parent);
8164
8165 if (parentModel && parentModel instanceof Backbone.Model) {
8166 parentModel.set(child, val, options);
8167 this.trigger('change:' + key, key, val, options);
8168 this.trigger('change:' + parent, key, val, options);
8169 }
8170 } else {
8171 Backbone.Model.prototype.set.call(this, key, val, options);
8172 }
8173 },
8174 get: function get(key) {
8175 var parent, child;
8176
8177 if ('string' === typeof key && -1 !== key.indexOf('.')) {
8178 parent = key.split('.')[0];
8179 child = key.split('.')[1];
8180 return this.get(parent).get(child);
8181 }
8182
8183 return Backbone.Model.prototype.get.call(this, key);
8184 }
8185 });
8186 });
8187 Hustle.define('Models.Trigger', function () {
8188 'use strict';
8189
8190 return Hustle.get('Model').extend({
8191 defaults: {
8192 trigger: ['time'],
8193 // time | scroll | click | exit_intent | adblock
8194 on_time_delay: '0',
8195 on_time_unit: 'seconds',
8196 on_scroll: 'scrolled',
8197 // scrolled | selector
8198 on_scroll_page_percent: '20',
8199 on_scroll_css_selector: '',
8200 enable_on_click_element: '1',
8201 on_click_element: '',
8202 enable_on_click_shortcode: '1',
8203 on_exit_intent_per_session: '1',
8204 on_exit_intent_delayed_time: '5',
8205 on_exit_intent_delayed_unit: 'seconds',
8206 on_adblock_delay: '0',
8207 on_adblock_delay_unit: 'seconds'
8208 }
8209 });
8210 });
8211 Module.Model = Hustle.get('Models.M').extend({
8212 defaults: {
8213 module_name: '',
8214 moduleType: 'popup',
8215 active: '0'
8216 }
8217 });
8218 (function ($) {
8219 'use strict';
8220 /**
8221 * READ BEFORE ADDING A NEW OBJECT HERE.
8222 * This file should only contain *views* that are used in *more than one page*.
8223 * The idea is to have reusable views in a single place.
8224 * If the functionality you're about to introduce is used in a single page,
8225 * there's probably a specific view for that page, and its functionalities
8226 * should belong in there.
8227 *
8228 * For example:
8229 * Module's preview modal => It's used in Dashboard, Listings, and Wizards. All good.
8230 * Module's tracking charts => It's only used in Listings. Not good.
8231 * There's a view for listings. It should be handled in the listing's view.
8232 */
8233
8234 var Module = window.Module || {};
8235 /**
8236 * Render a notification at the top of the page.
8237 * Used in the global settings page when saving, for example.
8238 *
8239 * @since 4.0
8240 */
8241
8242 Module.Notification = {
8243 id: null,
8244 $floatingContainer: null,
8245 open: function open(type, message) {
8246 var closeTime = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4000;
8247 var id = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'hustle-only-active-notification';
8248 var isFloating = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
8249 this.id = id;
8250 var options = {
8251 type: type || '',
8252 icon: 'info',
8253 // We're only using info icons atm.
8254 dismiss: {},
8255 autoclose: {}
8256 };
8257
8258 if (isFloating) {
8259 this.$floatingContainer = $('#hustle-floating-notifications-wrapper');
8260 this.createFloatingNoticeWrapper(); // Make sure closeTime is an integer, unless it's false.
8261
8262 if (false !== closeTime) {
8263 var parsedCloseTime = parseInt(closeTime); // Use the 4secs default otherwise.
8264
8265 if (isNaN(parsedCloseTime)) {
8266 closeTime = 4000;
8267 }
8268 }
8269
8270 options.dismiss = {
8271 show: false === closeTime,
8272 label: optinVars.messages.commons.dismiss
8273 };
8274 options.autoclose = {
8275 show: false !== closeTime,
8276 time: closeTime
8277 };
8278 } else {
8279 // We don't have auto-close nor dismissable inline notices.
8280 options.autoclose.show = false;
8281 options.dismiss.show = false;
8282 }
8283
8284 message = "<p>".concat(message, "</p>");
8285 SUI.openNotice(id, message, options);
8286 },
8287 createFloatingNoticeWrapper: function createFloatingNoticeWrapper() {
8288 var id = this.id; // Create the notice wrapper if it doesn't exist already.
8289
8290 if (!$('#' + id).length) {
8291 this.$floatingContainer.append("<div\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tid=\"".concat(id, "\"\n\t\t\t\t\tclass=\"sui-notice\"\n\t\t\t\t\taria-live=\"assertive\"\n\t\t\t\t></div>"));
8292 }
8293 }
8294 };
8295 /**
8296 * Render the modal used for editing the itnegrations' settings.
8297 *
8298 * @since 4.0
8299 */
8300
8301 Module.integrationsModal = {
8302 $popup: {},
8303 open: function open(e) {
8304 var self = this;
8305 var $target = $(e.target);
8306
8307 if (!$target.hasClass('connect-integration')) {
8308 $target = $target.closest('.connect-integration');
8309 } // Remove the templated modal before adding a new one.
8310
8311
8312 if ($('#hustle-integration-dialog').closest('.sui-modal').length) {
8313 $('#hustle-integration-dialog').closest('.sui-modal').remove();
8314 }
8315
8316 var nonce = $target.data('nonce'),
8317 slug = $target.data('slug'),
8318 title = $target.data('title'),
8319 image = $target.data('image'),
8320 action = $target.data('action'),
8321 moduleId = $target.data('module_id'),
8322 multiId = $target.data('multi_id'),
8323 globalMultiId = $target.data('global_multi_id'),
8324 tpl = Optin.template('hustle-integration-dialog-tpl');
8325 $('.sui-wrap-hustle').append(tpl({
8326 image: image,
8327 title: title
8328 }));
8329 this.$popup = $('#hustle-integration-dialog');
8330 var settingsView = Hustle.get('Integration_Modal_Handler');
8331 this.view = new settingsView({
8332 slug: slug,
8333 nonce: nonce,
8334 action: action,
8335 moduleId: moduleId,
8336 multiId: multiId,
8337 globalMultiId: globalMultiId,
8338 el: this.$popup
8339 });
8340 this.$popup.on('close', function () {
8341 return self.close();
8342 });
8343 SUI.openModal('hustle-integration-dialog', $target[0], this.$popup.find('.sui-box .hustle-modal-close')[0], true); // Make sui-tabs changeable
8344
8345 this.$popup.on('click', '.sui-tab-item', function (ev) {
8346 var $this = $(ev.currentTarget),
8347 $items = $this.closest('.sui-side-tabs').find('.sui-tab-item');
8348 $items.removeClass('active');
8349 $this.addClass('active');
8350 });
8351 },
8352 close: function close() {
8353 if (this.view) {
8354 this.$popup.closest('.sui-modal').remove();
8355 this.view.remove();
8356 }
8357 }
8358 };
8359 /**
8360 * Handle the modal for when removing the only integration in a module.
8361 * In Wizard pages.
8362 *
8363 * @since 4.0.1
8364 */
8365
8366 Module.integrationsAllRemove = {
8367 referrer: {},
8368
8369 /**
8370 * @since 4.0.2
8371 * @param {Object} data
8372 * @param {Object} referrer
8373 */
8374 open: function open(data, referrer) {
8375 var self = this;
8376 this.referrer = referrer;
8377 var dialogId = $('#hustle-dialog--final-delete');
8378
8379 var insertLocal = function insertLocal(insertData) {
8380 self.insertLocalList(insertData);
8381 return false;
8382 };
8383
8384 var deleteInt = function deleteInt(deleteData, deleteReferrer) {
8385 self.deleteIntegration(deleteData, deleteReferrer);
8386 return false;
8387 };
8388
8389 var $closeButton = dialogId.find('.sui-box-header .sui-button-icon'); // Add closing event
8390
8391 $closeButton.on('click', self.close);
8392 dialogId.find('#hustle-delete-final-button-cancel').on('click', self.close);
8393 $('#hustle-delete-final-button').off('click').on('click', function () {
8394 $('#hustle-delete-final-button').addClass('sui-button-onload');
8395 deleteInt(data, referrer);
8396 insertLocal(data);
8397 self.close();
8398 });
8399 $('#hustle-delete-final-button').prop('disabled', false);
8400 var $providerConfigButton = $('#hustle-connected-providers-section button[data-slug="' + data.data.slug + '"]');
8401 SUI.replaceModal('hustle-dialog--final-delete', $providerConfigButton[0], $closeButton[0], true);
8402 },
8403 close: function close() {
8404 $('#hustle-delete-final-button').removeClass('sui-button-onload');
8405 $('#hustle-delete-final-button').prop('disabled', false);
8406 },
8407 confirmDelete: function confirmDelete(data, referrer) {
8408 this.deleteIntegration(data, referrer);
8409 this.insertLocal(data);
8410 this.close();
8411 },
8412 deleteIntegration: function deleteIntegration(data, referrer) {
8413 referrer.request(data, true, false);
8414 },
8415 insertLocalList: function insertLocalList(data) {
8416 var ajaxData = {
8417 id: data.data.module_id,
8418 _ajax_nonce: data._ajax_nonce,
8419 action: 'hustle_provider_insert_local_list'
8420 };
8421 $.ajax({
8422 url: ajaxurl,
8423 type: 'POST',
8424 data: ajaxData,
8425 success: function success(resp) {
8426 if (resp.success) {
8427 Hustle.Events.trigger('hustle:providers:reload');
8428 } else {
8429 Module.Notification.open('error', optinVars.messages.something_went_wrong);
8430 }
8431 },
8432 error: function error() {
8433 Module.Notification.open('error', optinVars.messages.something_went_wrong);
8434 }
8435 });
8436 }
8437 };
8438 /**
8439 * Render the modal used when disconnecting a global integration that's in use in a module.
8440 * Used in the global Integrations page.
8441 *
8442 * @since 4.0.1
8443 */
8444
8445 Module.integrationsActiveRemove = {
8446 $popup: {},
8447 _deferred: {},
8448
8449 /**
8450 * @since 4.0.2
8451 * @param {Object} data
8452 * @param {Object} disconnect
8453 * @param {Object} referrer
8454 */
8455 open: function open(data, disconnect, referrer) {
8456 var self = this,
8457 dialogId = $('#hustle-dialog--remove-active');
8458
8459 var goBack = function goBack() {
8460 self.back(referrer);
8461 return false;
8462 };
8463
8464 var removeIntegration = function removeIntegration(integrationData, integrationReferrer, modules) {
8465 self.removeIntegration(integrationData, integrationReferrer, modules);
8466 };
8467
8468 var tpl = Optin.template('hustle-modules-active-integration-tpl'),
8469 tplImg = Optin.template('hustle-modules-active-integration-img-tpl'),
8470 tplHead = Optin.template('hustle-modules-active-integration-header-tpl'),
8471 tplDesc = Optin.template('hustle-modules-active-integration-desc-tpl'); //remove previous html
8472
8473 $('#hustle-dialog--remove-active tbody').html('');
8474 $('#hustle-dialog--remove-active .sui-box-logo').html('');
8475 $('#hustle-dialog--remove-active #hustle-dialog--remove-active-title').html('');
8476 $('#hustle-dialog--remove-active #hustle-dialog--remove-active-description').html('');
8477 $('#hustle-dialog--remove-active .sui-box-logo').append(tplImg({
8478 image: disconnect.data.img,
8479 title: disconnect.data.slug
8480 }));
8481 $('#hustle-dialog--remove-active #hustle-dialog--remove-active-title').append(tplHead({
8482 title: disconnect.data.title.replace(/Connect|Configure/gi, ' ')
8483 }));
8484 $('#hustle-dialog--remove-active #hustle-dialog--remove-active-description').append(tplDesc({
8485 title: disconnect.data.title.replace(/Connect|Configure/gi, ' ')
8486 }));
8487 $.each(data, function (id, meta) {
8488 $('#hustle-dialog--remove-active tbody').append(tpl({
8489 name: meta.name,
8490 type: meta.type,
8491 editUrl: meta.edit_url
8492 }));
8493 });
8494 dialogId.find('#hustle-remove-active-integration-back').off('click').on('click', function () {
8495 goBack();
8496 });
8497 $('#hustle-remove-active-button').off('click').on('click', function () {
8498 $(this).addClass('sui-button-onload');
8499 removeIntegration(disconnect, referrer, data);
8500 }); // Set the element to focus on once the modal is closed.
8501
8502 var $configButton = '';
8503
8504 if (referrer.globalMultiId) {
8505 $configButton = $('button[data-global_multi_id="' + referrer.globalMultiId + '"]');
8506 } else {
8507 $configButton = $('button[data-slug="' + disconnect.data.slug + '"]');
8508 }
8509
8510 SUI.replaceModal('hustle-dialog--remove-active', $configButton[0], dialogId.find('.hustle-modal-close')[0], true);
8511 },
8512 close: function close() {
8513 SUI.closeModal();
8514 },
8515 back: function back(referrer) {
8516 var self = this;
8517 self.close(); //integrations that doesn't support global multi id.
8518
8519 if ('hubspot' === referrer.slug || 'constantcontact' === referrer.slug || 'zapier' === referrer.slug) {
8520 $('button[data-slug="' + referrer.slug + '"]').trigger('click');
8521 } else {
8522 $('button[data-global_multi_id="' + referrer.globalMultiId + '"]').trigger('click');
8523 }
8524 },
8525 removeIntegration: function removeIntegration(data, referrer, modules) {
8526 var self = this;
8527 $.each(modules, function (id, meta) {
8528 if (data.data.slug === meta.active.active_integrations) {
8529 self.insertLocalList(data, id);
8530 }
8531 });
8532 referrer.request(data, true, false);
8533 $('#hustle-remove-active-button').removeClass('sui-button-onload');
8534 },
8535 insertLocalList: function insertLocalList(data, id) {
8536 var ajaxData = {
8537 id: id,
8538 _ajax_nonce: data._ajax_nonce,
8539 action: 'hustle_provider_insert_local_list'
8540 };
8541 $.ajax({
8542 url: ajaxurl,
8543 type: 'POST',
8544 data: ajaxData,
8545 success: function success(resp) {
8546 if (false === resp.success) {
8547 Module.Notification.open('error', optinVars.messages.something_went_wrong);
8548 }
8549 },
8550 error: function error() {
8551 Module.Notification.open('error', optinVars.messages.something_went_wrong);
8552 }
8553 });
8554 }
8555 };
8556 /**
8557 * The provider migration model
8558 *
8559 * @since 4.0.3
8560 */
8561
8562 Module.ProviderMigration = {
8563 $popup: {},
8564
8565 /**
8566 * @since 4.0.3
8567 * @param {string} slug Provider slug.
8568 * @param {string} id Provider global multi ID.
8569 */
8570 open: function open(slug) {
8571 var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
8572
8573 var dialogId = $('#hustle-dialog-migrate--' + slug),
8574 self = this,
8575 reauthMultiID = function reauthMultiID() {
8576 var form = dialogId.find('form'),
8577 data = {},
8578 params = {
8579 slug: slug,
8580 // eslint-disable-next-line camelcase
8581 global_multi_id: id
8582 };
8583 var formData = form.serialize();
8584 $('#integration-migrate').addClass('sui-button-onload'); // eslint-disable-next-line camelcase
8585
8586 data._ajax_nonce = $('#integration-migrate').data('nonce');
8587 data.action = 'hustle_provider_migrate_aweber';
8588 formData = formData + '&' + $.param(params);
8589 data.data = formData;
8590 self.reauth(dialogId, data, id, slug);
8591 };
8592
8593 if (id) {
8594 $('#integration-migrate').attr('data-id', id);
8595 }
8596
8597 setTimeout(function () {
8598 return SUI.openModal('hustle-dialog-migrate--' + slug, $('.sui-header-title')[0], $('#hustle-dialog-migrate--' + slug + ' .sui-box-header .sui-button-icon')[0], true);
8599 }, 300);
8600 dialogId.find('#integration-migrate').on('click', reauthMultiID);
8601 },
8602 reauth: function reauth(dialogId, data, id, slug) {
8603 var notice = $('.hustle_migration_notice__' + slug + '[data-id="' + id + '"]');
8604 this.ajax = $.post({
8605 url: ajaxurl,
8606 type: 'post',
8607 data: data
8608 }).done(function (result) {
8609 if (result && result.success) {
8610 SUI.closeModal();
8611 notice.hide();
8612 Module.Notification.open('success', optinVars.messages.aweber_migration_success, false);
8613 } else {
8614 $(dialogId).find('#integration-migrate').removeClass('sui-button-onload');
8615 $(dialogId).find('.sui-error-message').removeClass('sui-hidden');
8616 $(dialogId).find('.sui-form-field').addClass('sui-form-field-error');
8617 }
8618 });
8619 }
8620 };
8621 /**
8622 * The "are you sure?" modal from when deleting modules or entries.
8623 *
8624 * @since 4.0
8625 */
8626
8627 Module.deleteModal = {
8628 /**
8629 * @since 4.0
8630 * @param {Object} data - Must contain 'title', 'description', 'nonce', 'action', and 'id' that's being deleted.
8631 * @param {Object} focusOnClose - Document node to focus on after the modal is closed.
8632 */
8633 open: function open(data, focusOnClose) {
8634 var dialogId = 'hustle-dialog--delete',
8635 template = Optin.template('hustle-dialog--delete-tpl'),
8636 content = template(data); // Add the templated content to the modal.
8637
8638 $('#' + dialogId + ' #hustle-delete-dialog-content').html(content); // Add the title to the modal.
8639
8640 $('#' + dialogId + ' #hustle-dialog--delete-title').html(data.title); // Attach the closing event.
8641
8642 $('#' + dialogId + ' .hustle-cancel-button').on('click', function () {
8643 return SUI.closeModal(dialogId);
8644 });
8645 $('#' + dialogId + ' .hustle-delete-confirm').on('click', function (e) {
8646 var $button = $(e.currentTarget);
8647 $button.addClass('sui-button-onload');
8648 });
8649 SUI.openModal(dialogId, focusOnClose, $('#' + dialogId + '.hustle-modal-close')[0], true);
8650 }
8651 };
8652 /**
8653 * Key var to listen user changes before triggering
8654 * navigate away message.
8655 **/
8656
8657 Module.hasChanges = false;
8658 })(jQuery);
8659 (function ($) {
8660 'use strict';
8661
8662 var Module = window.Module || {};
8663 Module.Utils = {
8664 /*
8665 * Return URL param value
8666 */
8667 getUrlParam: function getUrlParam(param) {
8668 var defaultReturn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
8669 var urlParams = optinVars.urlParams;
8670
8671 if ('undefined' !== typeof urlParams[param]) {
8672 return urlParams[param];
8673 }
8674
8675 return defaultReturn;
8676 },
8677 accessibleHide: function accessibleHide($elements) {
8678 var isFocusable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
8679 var extraToUpdate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8680 $elements.hide();
8681 $elements.attr('aria-hidden', true);
8682 $elements.prop('hidden', true);
8683
8684 if (isFocusable) {
8685 $elements.prop('tabindex', '-1');
8686 }
8687
8688 if (extraToUpdate) {
8689 if ('undefined' !== typeof extraToUpdate.name) {
8690 if ('undefined' !== typeof extraToUpdate.value) {
8691 $elements.attr(extraToUpdate.name, extraToUpdate.value);
8692 } else {
8693 $elements.removeAttr(extraToUpdate.name);
8694 }
8695 }
8696 }
8697 },
8698 accessibleShow: function accessibleShow($elements) {
8699 var isFocusable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
8700 var extraToUpdate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
8701 $elements.show();
8702 $elements.prop('aria-hidden', false);
8703 $elements.removeClass('sui-hidden');
8704 $elements.prop('hidden', false);
8705
8706 if (isFocusable) {
8707 $elements.attr('tabindex', '0');
8708 }
8709
8710 if (extraToUpdate) {
8711 if ('undefined' !== typeof extraToUpdate.name) {
8712 if ('undefined' !== typeof extraToUpdate.value) {
8713 $elements.attr(extraToUpdate.name, extraToUpdate.value);
8714 } else {
8715 $elements.removeAttr(extraToUpdate.name);
8716 }
8717 }
8718 }
8719 },
8720 showHideDependencyOnSelectChange: function showHideDependencyOnSelectChange($parent) {
8721 var $selectsWithContainers = $parent ? $parent.find('select.hustle-select-with-container') : $('select.hustle-select-with-container');
8722 $selectsWithContainers.each(function () {
8723 var $this = $(this),
8724 $depContainer = $("[data-field-content=\"".concat(this.name, "\"]")),
8725 valuesOn = $this.data('content-on') ? $this.data('content-on').split(',') : false,
8726 valuesOff = $this.data('content-off') ? $this.data('content-off').split(',') : false;
8727
8728 var doToggle = function doToggle() {
8729 var currentVal = $this.val(),
8730 doShow = valuesOn ? valuesOn.includes(currentVal) : !valuesOff.includes(currentVal);
8731
8732 if (doShow) {
8733 Module.Utils.accessibleShow($depContainer);
8734 } else {
8735 Module.Utils.accessibleHide($depContainer);
8736 }
8737 }; // Do it on load.
8738
8739
8740 doToggle(); // And do it on change.
8741
8742 $this.on('change', function () {
8743 return doToggle();
8744 });
8745 });
8746 },
8747 serializeObject: function serializeObject($form) {
8748 var object = {},
8749 array = $form.serializeArray();
8750 $.each(array, function () {
8751 if (undefined !== object[this.name]) {
8752 if (!object[this.name].push) {
8753 object[this.name] = [object[this.name]];
8754 }
8755
8756 object[this.name].push(this.value || '');
8757 } else {
8758 object[this.name] = this.value || '';
8759 }
8760 });
8761 $form.find('input:disabled[name]').each(function () {
8762 object[this.name] = this.value || '';
8763 });
8764 $form.find('select:disabled[name]').each(function () {
8765 object[this.name] = this.value || '';
8766 });
8767 $form.find('input[type="checkbox"]:not(:checked)').each(function () {
8768 if (undefined === object[this.name]) {
8769 object[this.name] = '0';
8770 } else if ('0' === object[this.name] && !$form.find("input[name=\"".concat(this.name, "\"]:checked")).length) {
8771 object[this.name] = [];
8772 } else if (!Array.isArray(object[this.name])) {
8773 object[this.name] = [object[this.name]];
8774 }
8775 });
8776 return object;
8777 }
8778 };
8779 /**
8780 * One callback to rule them all.
8781 * Receives the events from single module actions.
8782 * Call another callback or does an action (eg. a redirect) according to the ajax request response.
8783 * Used in module listing pages and dashboard.
8784 *
8785 * @since 4.0.3
8786 */
8787
8788 Module.handleActions = {
8789 context: '',
8790 $target: null,
8791
8792 /**
8793 * @param {Object} data AJAX request response.
8794 */
8795 responseData: {},
8796
8797 /**
8798 * Function to initiate the action.
8799 *
8800 * @since 4.0.3
8801 * @param {Object} e
8802 * @param {string} context Where it's called from. dashboard|listing
8803 */
8804 initAction: function initAction(e, context) {
8805 var _this = this;
8806
8807 e.preventDefault();
8808 this.context = context;
8809 this.$target = $(e.currentTarget);
8810 var self = this,
8811 relatedFormId = this.$target.data('form-id'),
8812 actionData = this.$target.data();
8813 var data = new FormData(); // Grab the form's data if the action has a related form.
8814
8815 if ('undefined' !== typeof relatedFormId) {
8816 var $form = $('#' + relatedFormId);
8817
8818 if ($form.length) {
8819 data = new FormData($form[0]);
8820 }
8821 }
8822
8823 $.each(actionData, function (name, value) {
8824 return data.append(name, value);
8825 });
8826 data.append('context', this.context);
8827 data.append('_ajax_nonce', optinVars.single_module_action_nonce);
8828 data.append('action', 'hustle_module_handle_single_action');
8829 $.ajax({
8830 url: ajaxurl,
8831 type: 'POST',
8832 data: data,
8833 contentType: false,
8834 processData: false
8835 }).done(function (res) {
8836 // If there's a defined callback, call it.
8837 if (res.data.callback && 'function' === typeof self[res.data.callback]) {
8838 _this.responseData = res.data; // This calls the "action{ hustle action }" function from this view.
8839 // For example: actionToggleStatus();
8840
8841 self[res.data.callback]();
8842 } else if (res.data.url) {
8843 window.location.assign(res.data.url);
8844 } else if (res.data.notification) {
8845 Module.Notification.open(res.data.notification.status, res.data.notification.message, res.data.notification.delay);
8846 } else if (!res.success) {
8847 Module.Notification.open('error', optinVars.messages.something_went_wrong, false);
8848 } // Don't remove the 'loading' icon when redirecting/reloading.
8849
8850
8851 if (!res.data.url) {
8852 $('.sui-button-onload').removeClass('sui-button-onload');
8853 }
8854 }).fail(function () {
8855 $('.sui-button-onload').removeClass('sui-button-onload');
8856 });
8857 },
8858
8859 /**
8860 * initAction succcess callback for "toggle-tracking".
8861 * Only for Listing.
8862 *
8863 * @since 4.0.3
8864 */
8865 actionToggleTracking: function actionToggleTracking() {
8866 var $accordionContainer, isTrackingEnabled;
8867
8868 if (!this.responseData.is_embed_or_sshare) {
8869 isTrackingEnabled = 1 - (this.responseData.was_enabled ? 1 : 0);
8870 $accordionContainer = this.$target.parents('.sui-accordion-item');
8871 this.$target.data('enabled', isTrackingEnabled);
8872 this.$target.find('.hustle-toggle-tracking-button-description').toggleClass('sui-hidden');
8873 } else {
8874 var $button = $('.hustle-manage-tracking-button[data-module-id="' + this.$target.data('module-id') + '"]');
8875 $button.data('tracking-types', this.responseData.enabled_types);
8876 isTrackingEnabled = 0 !== this.responseData.enabled_types.length;
8877 $accordionContainer = $button.parents('.sui-accordion-item');
8878 SUI.closeModal();
8879 } // Update the tracking chart if it was being displayed.
8880
8881
8882 if ($accordionContainer.hasClass('sui-accordion-item--open')) {
8883 $accordionContainer.find('.sui-accordion-open-indicator').trigger('click').trigger('click');
8884 } // Display the 'Analytics Disable' tag if the module is active and tracking is disabled.
8885
8886
8887 var displayTag = this.responseData.is_active && !isTrackingEnabled;
8888 this.toggleTrackingDisabledTag(displayTag, $accordionContainer);
8889 Module.Notification.open('success', this.responseData.message, 10000);
8890 },
8891 actionToggleStatus: function actionToggleStatus() {
8892 if ('listing' === this.context) {
8893 this.listingToggleStatus();
8894 } else {
8895 this.dashboardToggleStatus();
8896 }
8897 },
8898
8899 /**
8900 * initAction succcess callback for "toggle-status".
8901 *
8902 * @since 4.0.4
8903 */
8904 listingToggleStatus: function listingToggleStatus() {
8905 var $accordionContainer = this.$target.closest('.sui-accordion-item'),
8906 $accordionTag = $accordionContainer.find('.sui-accordion-item-title span.sui-tag');
8907 $accordionTag.toggleClass('sui-tag-blue');
8908 $accordionTag.find('.hustle-toggle-status-button-description').toggleClass('sui-hidden');
8909 this.$target.find('.hustle-toggle-status-button-description').toggleClass('sui-hidden'); // Update tracking data
8910
8911 if ($accordionContainer.hasClass('sui-accordion-item--open')) {
8912 $accordionContainer.find('.sui-accordion-open-indicator').trigger('click').trigger('click');
8913 } // Display the 'Analytics Disable' tag if the module is active and tracking is disabled.
8914
8915
8916 var displayTag = this.responseData.is_active && !this.responseData.is_tracking_enabled;
8917 this.toggleTrackingDisabledTag(displayTag, $accordionContainer);
8918 },
8919
8920 /**
8921 * initAction succcess callback for "toggle-status".
8922 *
8923 * @since 4.0.4
8924 */
8925 dashboardToggleStatus: function dashboardToggleStatus() {
8926 var isActive = this.responseData.is_active;
8927 this.$target.find('.hustle-toggle-status-button-description').toggleClass('sui-hidden');
8928 var tooltip = this.$target.parents('td.hui-status').find('span.sui-tooltip');
8929 tooltip.removeClass('sui-draft sui-published');
8930
8931 if (isActive) {
8932 tooltip.addClass('sui-published').attr('data-tooltip', optinVars.messages.commons.published);
8933 } else {
8934 tooltip.addClass('sui-draft').attr('data-tooltip', optinVars.messages.commons.draft);
8935 }
8936 },
8937 actionImportDisplayError: function actionImportDisplayError() {
8938 var message = this.responseData.message,
8939 $dialog = this.$target.closest('.sui-modal'),
8940 dialogId = $dialog.find('.sui-modal-content').attr('id'),
8941 noticeId = dialogId + '-error-notice';
8942 Module.Notification.open('error', message, false, noticeId, false);
8943 $dialog.find('input[name="import_file"]').focus();
8944 },
8945 toggleTrackingDisabledTag: function toggleTrackingDisabledTag(isDisplay, $accordionContainer) {
8946 var $trackingDisabledTag = $accordionContainer.find('.hustle-analytics-disabled-tag');
8947
8948 if (isDisplay) {
8949 $trackingDisabledTag.removeClass('sui-hidden');
8950 } else {
8951 $trackingDisabledTag.addClass('sui-hidden');
8952 }
8953 }
8954 };
8955 var Optin = window.Optin || {};
8956
8957 Optin.globalMixin = function () {
8958 _.mixin({
8959 /**
8960 * Converts val to boolian
8961 *
8962 * @param {*} val Value to check.
8963 * @return {boolean} Passed value as boolean.
8964 */
8965 toBool: function toBool(val) {
8966 if (_.isBoolean(val)) {
8967 return val;
8968 }
8969
8970 if (_.isString(val) && -1 !== ['true', 'false', '1'].indexOf(val.toLowerCase())) {
8971 return 'true' === val.toLowerCase() || '1' === val.toLowerCase() ? true : false;
8972 }
8973
8974 if (_.isNumber(val)) {
8975 return !!val;
8976 }
8977
8978 if (_.isUndefined(val) || _.isNull(val) || _.isNaN(val)) {
8979 return false;
8980 }
8981
8982 return val;
8983 },
8984
8985 /**
8986 * Checks if val is truthy
8987 *
8988 * @param {*} val Value to check.
8989 * @return {boolean} Passed value as boolean.
8990 */
8991 isTrue: function isTrue(val) {
8992 if (_.isUndefined(val) || _.isNull(val) || _.isNaN(val)) {
8993 return false;
8994 }
8995
8996 if (_.isNumber(val)) {
8997 return 0 !== val;
8998 }
8999
9000 val = val.toString().toLowerCase();
9001 return -1 !== ['1', 'true', 'on'].indexOf(val);
9002 },
9003 isFalse: function isFalse(val) {
9004 return !_.isTrue(val);
9005 },
9006 controlBase: function controlBase(checked, current, attribute) {
9007 attribute = _.isUndefined(attribute) ? 'checked' : attribute;
9008 checked = _.toBool(checked);
9009 current = _.isBoolean(checked) ? _.isTrue(current) : current;
9010
9011 if (_.isEqual(checked, current)) {
9012 return attribute + '=' + attribute;
9013 }
9014
9015 return '';
9016 },
9017
9018 /**
9019 * Checks whether the input should be checked.
9020 *
9021 * @param {number|string} checked Currently checked value.
9022 * @param {number|string} current Current value in the iteration.
9023 * @return {string} Returns checked="checked" if checked variable is equal to current state.
9024 */
9025 checked: function checked(_checked, current) {
9026 return _.controlBase(_checked, current, 'checked');
9027 },
9028
9029 /**
9030 * Checks whether the option should be selected.
9031 *
9032 * @param {number|string} selected Currently checked value.
9033 * @param {number|string} current Current value in the iteration.
9034 * @return {string} Returns selected="selected" if checked variable is equal to current state.
9035 */
9036 selected: function selected(_selected, current) {
9037 return _.controlBase(_selected, current, 'selected');
9038 },
9039
9040 /**
9041 * Checks whether the thing should be disabled.
9042 *
9043 * @param {number|string} disabled Currently checked value.
9044 * @param {number|string} current Current value in the iteration.
9045 * @return {string} Returns disabled="disabled" if checked variable is equal to current state.
9046 */
9047 disabled: function disabled(_disabled, current) {
9048 return _.controlBase(_disabled, current, 'disabled');
9049 },
9050
9051 /**
9052 * Returns css class based on the passed condition.
9053 *
9054 * @param {*} conditon The condition to check.
9055 * @param {string} className Class to add if the condition is true.
9056 * @param {string} negatingClass Class to add if the condition is not true.
9057 * @return {string} Class to add.
9058 */
9059 class: function _class(conditon, className, negatingClass) {
9060 if (_.isTrue(conditon)) {
9061 return className;
9062 }
9063
9064 return 'undefined' !== typeof negatingClass ? negatingClass : '';
9065 }
9066 });
9067 };
9068
9069 Optin.globalMixin();
9070 /**
9071 * Recursive toJSON
9072 *
9073 * @return {*} JSON.
9074 */
9075
9076 Backbone.Model.prototype.toJSON = function () {
9077 var json = _.clone(this.attributes);
9078
9079 var attr;
9080
9081 for (attr in json) {
9082 if (json[attr] instanceof Backbone.Model || Backbone.Collection && json[attr] instanceof Backbone.Collection) {
9083 json[attr] = json[attr].toJSON();
9084 }
9085 }
9086
9087 return json;
9088 };
9089 })(jQuery);
9090 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9091
9092 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
9093
9094 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
9095
9096 Hustle.define('SShare.Content_View', function ($) {
9097 'use strict';
9098
9099 return Hustle.View.extend(_.extend({}, Hustle.get('Mixins.Module_Content'), {
9100 el: '#hustle-wizard-content',
9101 activePlatforms: [],
9102 events: {
9103 'change select.hustle-select-field-variables': 'addPlaceholderToField',
9104 'click ul.wpmudev-tabs-menu li label': 'toggleCheckbox',
9105 // Open Add Platforms popup
9106 'click .hustle-choose-platforms': 'openPlatformsModal'
9107 },
9108 render: function render() {
9109 var me = this,
9110 data = this.model.toJSON();
9111
9112 if ('undefined' !== typeof data.social_icons && data.social_icons) {
9113 for (var platform in data.social_icons) {
9114 me.addPlatformToPanel(platform, data.social_icons[platform]);
9115 }
9116 } // Initiate the sortable functionality to sort form platforms' order.
9117
9118
9119 var sortableContainer = this.$('#hustle-social-services').sortable({
9120 axis: 'y',
9121 containment: '.sui-box-builder'
9122 });
9123 sortableContainer.on('sortupdate', $.proxy(me.platformsOrderChanged, me, sortableContainer)); //add all platforms to Add Platforms popup
9124
9125 for (var _platform in optinVars.social_platforms) {
9126 me.addPlatformToDialog(_platform);
9127 }
9128
9129 this.bindRemoveService();
9130
9131 if ('true' === Module.Utils.getUrlParam('new')) {
9132 Module.Notification.open('success', optinVars.messages.module_created, 10000);
9133 }
9134 },
9135 bindRemoveService: function bindRemoveService() {
9136 // Delete Social Service
9137 $('#hustle-wizard-content .hustle-remove-social-service').off('click').on('click', $.proxy(this.removeService, this));
9138 },
9139 openPlatformsModal: function openPlatformsModal() {
9140 var self = this,
9141 savedPlatforms = this.model.get('social_icons'),
9142 platforms = 'undefined' !== typeof savedPlatforms ? Object.keys(savedPlatforms) : [],
9143 PlatformsModalView = Hustle.get('Modals.Services_Platforms'),
9144 platformsModal = new PlatformsModalView(platforms);
9145 platformsModal.on('platforms:added', $.proxy(self.addNewPlatforms, self)); // Show dialog
9146
9147 SUI.openModal('hustle-dialog--add-platforms', this.$('.hustle-choose-platforms')[0], this.$('#hustle-dialog--add-platforms .hustle-modal-close')[0], true);
9148 },
9149 addNewPlatforms: function addNewPlatforms(platforms) {
9150 var _this = this;
9151
9152 if (!this.model.get('social_icons')) {
9153 this.model.set('social_icons', {});
9154 }
9155
9156 var self = this,
9157 savedPlatforms = _.extend({}, this.model.get('social_icons'));
9158
9159 $.each(platforms, function (i, platform) {
9160 if (savedPlatforms && platform in savedPlatforms) {
9161 //If this platform is already set, abort. Prevent duplicated platforms.
9162 return true;
9163 }
9164
9165 self.addPlatformToPanel(platform, {});
9166
9167 var data = _this.getPlatformDefaults(platform);
9168
9169 savedPlatforms[platform] = data;
9170 });
9171 this.bindRemoveService();
9172 this.model.set('social_icons', savedPlatforms);
9173 Hustle.Events.trigger('view.rendered', this);
9174 },
9175 addPlatformToPanel: function addPlatformToPanel(platform, data) {
9176 var template = Optin.template('hustle-platform-row-tpl'),
9177 $platformsContainer = this.$('#hustle-social-services');
9178 data = _.extend({}, this.getPlatformViewDefaults(platform), data);
9179 this.activePlatforms.push(platform);
9180 $platformsContainer.append(template(data));
9181 },
9182 addPlatformToDialog: function addPlatformToDialog(platform) {
9183 var template = Optin.template('hustle-add-platform-li-tpl'),
9184 $container = $('#hustle_add_platforms_container'),
9185 data = this.getPlatformViewDefaults(platform);
9186 $container.append(template(data));
9187 },
9188 getPlatformDefaults: function getPlatformDefaults(platform) {
9189 var label = platform in optinVars.social_platforms ? optinVars.social_platforms[platform] : platform,
9190 defaults = {
9191 platform: platform,
9192 label: label,
9193 type: 'click',
9194 counter: '0',
9195 link: ''
9196 };
9197
9198 if ('email' === platform) {
9199 defaults.title = '{post_title}';
9200 defaults.message = optinVars.social_platforms_data.email_message_default;
9201 }
9202
9203 return defaults;
9204 },
9205 getPlatformViewDefaults: function getPlatformViewDefaults(platform) {
9206 var data = this.model.toJSON(),
9207 counterEnabled = 'undefined' === typeof data.counter_enabled ? 'true' : data.counter_enabled,
9208 changedStyles = {
9209 fivehundredpx: '500px'
9210 },
9211 hasEndpoint = -1 !== optinVars.social_platforms_with_endpoints.indexOf(platform),
9212 hasCounter = -1 !== optinVars.social_platforms_with_api.indexOf(platform);
9213
9214 var platformStyle = platform in changedStyles ? changedStyles[platform] : platform,
9215 viewDefaults = _.extend({}, this.getPlatformDefaults(platform), {
9216 platform_style: platformStyle,
9217 counter_enabled: counterEnabled,
9218 hasEndpoint: hasEndpoint,
9219 hasCounter: hasCounter
9220 });
9221
9222 return viewDefaults;
9223 },
9224
9225 /**
9226 * Assign the new platfom order to the model. Triggered when the platforms are sorted.
9227 *
9228 * @since 4.0.0
9229 * @param {Object} sortable
9230 */
9231 platformsOrderChanged: function platformsOrderChanged(sortable) {
9232 var platforms = this.model.get('social_icons'),
9233 newOrder = sortable.sortable('toArray', {
9234 attribute: 'data-platform'
9235 }),
9236 orderedPlatforms = {};
9237
9238 var _iterator = _createForOfIteratorHelper(newOrder),
9239 _step;
9240
9241 try {
9242 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9243 var id = _step.value;
9244 orderedPlatforms[id] = platforms[id];
9245 }
9246 } catch (err) {
9247 _iterator.e(err);
9248 } finally {
9249 _iterator.f();
9250 }
9251
9252 this.model.set('social_icons', orderedPlatforms);
9253 this.model.trigger('change', this.model);
9254 },
9255 removeService: function removeService(e) {
9256 var $this = $(e.currentTarget),
9257 platform = $this.data('platform'),
9258 socialIcons = this.model.get('social_icons'),
9259 $platformContainer = this.$('#hustle-platform-' + platform); // Remove the platform container from the page.
9260
9261 $platformContainer.remove();
9262 this.activePlatforms = _.without(this.activePlatforms, platform);
9263 delete socialIcons[platform];
9264 this.model.trigger('change', this.model);
9265 e.stopPropagation();
9266 },
9267 modelUpdated: function modelUpdated(e) {
9268 var changed = e.changed,
9269 key = 'undefined' !== typeof Object.keys(changed)[0] ? Object.keys(changed)[0] : '';
9270 var socialIcons; // for service_type
9271
9272 if ('service_type' in changed) {
9273 this.serviceTypeUpdated(changed.service_type);
9274 } // for click_counter
9275
9276
9277 if ('click_counter' in changed) {
9278 this.clickCounterUpdated(changed.click_counter);
9279 } else if (-1 !== key.indexOf('_counter')) {
9280 var platform = key.slice(0, -8);
9281 socialIcons = this.model.get('social_icons');
9282
9283 if (platform in socialIcons) {
9284 socialIcons[platform].counter = parseInt(changed[key]);
9285 }
9286
9287 this.model.unset(key, {
9288 silent: true
9289 });
9290 }
9291
9292 if (-1 !== key.indexOf('_link')) {
9293 var _platform2 = key.slice(0, -5);
9294
9295 socialIcons = this.model.get('social_icons');
9296
9297 if (_platform2 in socialIcons) {
9298 socialIcons[_platform2].link = changed[key];
9299 }
9300
9301 this.model.unset(key, {
9302 silent: true
9303 });
9304 }
9305
9306 if (-1 !== key.indexOf('_type')) {
9307 var _platform3 = key.slice(0, -5);
9308
9309 socialIcons = this.model.get('social_icons');
9310
9311 if (_platform3 in socialIcons) {
9312 socialIcons[_platform3].type = 'native' === changed[key] ? 'native' : 'click';
9313 }
9314
9315 this.model.unset(key, {
9316 silent: true
9317 });
9318 }
9319
9320 if ('email_title' in changed) {
9321 var _platform4 = 'email';
9322 socialIcons = this.model.get('social_icons');
9323
9324 if (_platform4 in socialIcons) {
9325 socialIcons[_platform4].title = changed[key];
9326 }
9327
9328 this.model.unset(key, {
9329 silent: true
9330 });
9331 }
9332
9333 if ('email_message' in changed) {
9334 var _platform5 = 'email';
9335 socialIcons = this.model.get('social_icons');
9336
9337 if (_platform5 in socialIcons) {
9338 socialIcons[_platform5].message = changed[key];
9339 }
9340
9341 this.model.unset(key, {
9342 silent: true
9343 });
9344 }
9345 },
9346 serviceTypeUpdated: function serviceTypeUpdated(val) {
9347 var $counterOptions = this.$('#wpmudev-sshare-counter-options'),
9348 $nativeOptions = $('.wph-wizard-services-icons-native'),
9349 $customOptions = $('.wph-wizard-services-icons-custom');
9350
9351 if ('native' === val) {
9352 $counterOptions.removeClass('wpmudev-hidden');
9353 $customOptions.addClass('wpmudev-hidden');
9354 $nativeOptions.removeClass('wpmudev-hidden');
9355 } else {
9356 $counterOptions.addClass('wpmudev-hidden');
9357 $nativeOptions.addClass('wpmudev-hidden');
9358 $customOptions.removeClass('wpmudev-hidden');
9359 }
9360 },
9361 clickCounterUpdated: function clickCounterUpdated(val) {
9362 var $counterNotice = $('#wpmudev-sshare-counter-options .hustle-twitter-notice');
9363
9364 if ('native' === val) {
9365 $counterNotice.removeClass('wpmudev-hidden');
9366 } else if (!$counterNotice.hasClass('wpmudev-hidden')) {
9367 $counterNotice.addClass('wpmudev-hidden');
9368 }
9369
9370 $('#wph-wizard-services-icons-native .wpmudev-social-item').each(function () {
9371 var $checkbox = $(this).find('.toggle-checkbox'),
9372 isChecked = $checkbox.is(':checked'),
9373 $inputCounter = $(this).find('input.wpmudev-input_number');
9374
9375 if ('none' !== val && isChecked) {
9376 $inputCounter.removeClass('wpmudev-hidden');
9377 } else if (!$inputCounter.hasClass('wpmudev-hidden')) {
9378 $inputCounter.addClass('wpmudev-hidden');
9379 }
9380 });
9381 $('#wph-wizard-services-icons-native #wpmudev-counter-title>strong').removeClass('wpmudev-hidden');
9382
9383 if ('none' === val) {
9384 $('#wph-wizard-services-icons-native #wpmudev-counter-title>strong:first-child').addClass('wpmudev-hidden');
9385 } else {
9386 $('#wph-wizard-services-icons-native #wpmudev-counter-title>strong:nth-child(2)').addClass('wpmudev-hidden');
9387 }
9388 },
9389 toggleCheckbox: function toggleCheckbox(e) {
9390 e.preventDefault();
9391 e.stopPropagation();
9392 var $this = this.$(e.target),
9393 $li = $this.closest('li');
9394
9395 if ($li.hasClass('current')) {
9396 return;
9397 }
9398
9399 $li.addClass('current');
9400 $li.siblings().removeClass('current');
9401 var $input = $li.find('input'),
9402 prop = $input.data('attribute');
9403 this.model.set(prop, $input.val());
9404 },
9405 setSocialIcons: function setSocialIcons() {
9406 var services = this.model.toJSON();
9407 services = this.getSocialIconsData(services);
9408 this.model.set('social_icons', services.social_icons, {
9409 silent: true
9410 });
9411 },
9412 getSocialIconsData: function getSocialIconsData(services) {
9413 var $socialContainers = $('#wph-wizard-services-icons-' + services.service_type + ' .wpmudev-social-item'),
9414 socialIcons = {};
9415 $socialContainers.each(function () {
9416 var $sc = $(this),
9417 $toggleInput = $sc.find('input.toggle-checkbox'),
9418 icon = $toggleInput.data('id'),
9419 $counter = $sc.find('input.wpmudev-input_number'),
9420 $link = $sc.find('input.wpmudev-input_text'); // check if counter have negative values
9421
9422 if ($counter.length) {
9423 var counterVal = parseInt($counter.val());
9424
9425 if (0 > counterVal) {
9426 $counter.val(0);
9427 }
9428 }
9429
9430 if ($toggleInput.is(':checked')) {
9431 socialIcons[icon] = {
9432 enabled: true,
9433 counter: $counter.length ? $counter.val() : '0',
9434 link: $link.length ? $link.val() : ''
9435 };
9436 }
9437 });
9438
9439 if ($socialContainers.length) {
9440 services.social_icons = socialIcons;
9441 }
9442
9443 return services;
9444 },
9445 addPlaceholderToField: function addPlaceholderToField(e) {
9446 var $select = $(e.currentTarget),
9447 selectedPlaceholder = $select.val(),
9448 targetInputName = $select.data('field'),
9449 $input = $("[name=\"".concat(targetInputName, "\"]")),
9450 val = $input.val() + selectedPlaceholder;
9451 $input.val(val).trigger('change');
9452 }
9453 }));
9454 });
9455 Hustle.define('SShare.Design_View', function ($) {
9456 'use strict';
9457
9458 return Hustle.View.extend(_.extend({}, Hustle.get('Mixins.Model_Updater'), Hustle.get('Mixins.Module_Design'), {
9459 beforeRender: function beforeRender() {
9460 this.listenTo(this.model, 'change', this.modelUpdated); // Update the Appearance tab view when the display types are changed in the Display tab.
9461
9462 Hustle.Events.off('modules.view.displayTypeUpdated').on('modules.view.displayTypeUpdated', $.proxy(this.viewChangedDisplayTab, this));
9463 },
9464 render: function render() {
9465 var _this = this;
9466
9467 this.createPickers($('.sui-colorpicker-input')); // Trigger preview when this tab is shown.
9468
9469 $('a[data-tab="appearance"]').on('click', $.proxy(this.updatePreview, this));
9470 $('.sui-box[data-tab="display"] .sui-button[data-direction="next"').on('click', $.proxy(this.updatePreview, this));
9471 $('.sui-box[data-tab="visibility"] .sui-button[data-direction="prev"').on('click', $.proxy(this.updatePreview, this));
9472 this.updateSocialIconsPickers();
9473 setTimeout(function () {
9474 return _this.updatePreview();
9475 }, 100);
9476 },
9477 updatePreview: function updatePreview() {
9478 $('#hui-preview-social-shares-floating').trigger('hustle_update_prewiev');
9479 },
9480 // Adjust the view when model is updated
9481 modelUpdated: function modelUpdated(model) {
9482 var changedKey = Object.keys(model.changed)[0],
9483 actionToDo = this.getActionOnContentModelUpdated(changedKey);
9484
9485 if ('undefined' !== typeof actionToDo) {
9486 actionToDo();
9487 }
9488
9489 this.updatePreview();
9490 },
9491 getActionOnContentModelUpdated: function getActionOnContentModelUpdated(changedKey) {
9492 var _this2 = this;
9493
9494 var functions = {
9495 icon_style: function icon_style() {
9496 return _this2.updateSocialIconsPickers();
9497 }
9498 };
9499 return functions[changedKey];
9500 },
9501 updateSocialIconsPickers: function updateSocialIconsPickers() {
9502 var iconStyle = this.model.get('icon_style');
9503
9504 if ('flat' === iconStyle) {
9505 $('#hustle-floating-icons-custom-background').addClass('sui-hidden');
9506 $('#hustle-widget-icons-custom-background').addClass('sui-hidden');
9507 } else {
9508 $('#hustle-floating-icons-custom-background').removeClass('sui-hidden');
9509 $('#hustle-widget-icons-custom-background').removeClass('sui-hidden');
9510 }
9511
9512 if ('outline' === iconStyle) {
9513 // Replace "icon background" text with "icon border"
9514 this.$('.hustle-icon-bg-color-label').addClass('sui-hidden');
9515 this.$('.hustle-icon-border-color-label').removeClass('sui-hidden'); // Hide counter border color
9516
9517 $('#hustle-floating-counter-border').addClass('sui-hidden');
9518 $('#hustle-widget-counter-border').addClass('sui-hidden');
9519 } else {
9520 // Replace "icon border" text with "icon background"
9521 this.$('.hustle-icon-bg-color-label').removeClass('sui-hidden');
9522 this.$('.hustle-icon-border-color-label').addClass('sui-hidden'); // Show counter border color
9523
9524 $('#hustle-floating-counter-border').removeClass('sui-hidden');
9525 $('#hustle-widget-counter-border').removeClass('sui-hidden');
9526 }
9527 },
9528 viewChangedDisplayTab: function viewChangedDisplayTab(model) {
9529 var inline = model.get('inline_enabled'),
9530 widget = model.get('widget_enabled'),
9531 shortcode = model.get('shortcode_enabled'),
9532 floatDesktop = model.get('float_desktop_enabled'),
9533 floatMobile = model.get('float_mobile_enabled'),
9534 isWidgetEnabled = _.intersection([1, '1', 'true'], [inline, widget, shortcode]).length,
9535 isFloatingEnabled = _.intersection([1, '1', 'true'], [floatMobile, floatDesktop]).length; // TODO: we should be using this.$( '...' ) here instead.
9536
9537
9538 if (isFloatingEnabled) {
9539 $('#hustle-appearance-floating-icons-row').show();
9540 $('#hustle-appearance-floating-icons-placeholder').hide();
9541 } else {
9542 $('#hustle-appearance-floating-icons-row').hide();
9543 $('#hustle-appearance-floating-icons-placeholder').show();
9544 }
9545
9546 if (isWidgetEnabled) {
9547 $('#hustle-appearance-widget-icons-row').show();
9548 $('#hustle-appearance-widget-icons-placeholder').hide();
9549 } else {
9550 $('#hustle-appearance-widget-icons-row').hide();
9551 $('#hustle-appearance-widget-icons-placeholder').show();
9552 }
9553
9554 if (!isWidgetEnabled && !isFloatingEnabled) {
9555 $('#hustle-appearance-icons-style').hide();
9556 $('#hustle-appearance-empty-message').show();
9557 $('#hustle-appearance-floating-icons-placeholder').hide();
9558 $('#hustle-appearance-widget-icons-placeholder').hide();
9559 } else {
9560 $('#hustle-appearance-icons-style').show();
9561 $('#hustle-appearance-empty-message').hide();
9562 }
9563 },
9564 colorPickerCleared: function colorPickerCleared(e, parentSuiPickerInput) {
9565 var inputName = parentSuiPickerInput.data('attribute'),
9566 resetValue = optinVars.palettes.sshare_defaults[inputName],
9567 $suiPicker = parentSuiPickerInput.closest('.sui-colorpicker-wrap'),
9568 $suiPickerValue = $suiPicker.find('.sui-colorpicker-value'),
9569 $suiPickerColor = $suiPicker.find('.sui-colorpicker-value span[role=button]'),
9570 $wpPicker = parentSuiPickerInput.closest('.wp-picker-container'),
9571 $wpPickerClear = $wpPicker.find('.wp-picker-clear');
9572 $wpPickerClear.click();
9573 $suiPickerValue.find('input').val(resetValue);
9574 parentSuiPickerInput.val(resetValue).trigger('change');
9575 $suiPickerColor.find('span').css({
9576 'background-color': resetValue
9577 });
9578 e.preventDefault();
9579 e.stopPropagation();
9580 },
9581 updatePickers: function updatePickers() {
9582 var self = this;
9583
9584 if ('undefined' !== typeof optinVars.palettes.sshare_defaults) {
9585 var colors = optinVars.palettes.sshare_defaults; // update color palettes
9586
9587 _.each(colors, function (color, key) {
9588 self.$('input[data-attribute="' + key + '"]').val(color).trigger('change');
9589 });
9590 }
9591 }
9592 }));
9593 });
9594 Hustle.define('SShare.Display_View', function () {
9595 'use strict';
9596
9597 return Hustle.View.extend(_.extend({}, Hustle.get('Mixins.Module_Display'), {
9598 viewChanged: function viewChanged(changed) {
9599 if (_.intersection(['float_desktop_enabled', 'float_mobile_enabled', 'inline_enabled', 'widget_enabled', 'shortcode_enabled'], Object.keys(changed)).length) {
9600 // Show/hide some settings in the Appearance tab.
9601 Hustle.Events.trigger('modules.view.displayTypeUpdated', this.model);
9602 } else if ('float_desktop_position' in changed) {
9603 if ('right' === changed.float_desktop_position) {
9604 this.$('#hustle-float_desktop-left-offset-label').addClass('sui-hidden');
9605 this.$('#hustle-float_desktop-right-offset-label').removeClass('sui-hidden');
9606 this.$('#hustle-float_desktop-offset-x-wrapper').removeClass('sui-hidden');
9607 } else if ('left' === changed.float_desktop_position) {
9608 this.$('#hustle-float_desktop-left-offset-label').removeClass('sui-hidden');
9609 this.$('#hustle-float_desktop-right-offset-label').addClass('sui-hidden');
9610 this.$('#hustle-float_desktop-offset-x-wrapper').removeClass('sui-hidden');
9611 } else {
9612 this.$('#hustle-float_desktop-offset-x-wrapper').addClass('sui-hidden');
9613 }
9614 } else if ('float_desktop_position_y' in changed) {
9615 if ('bottom' === changed.float_desktop_position_y) {
9616 this.$('#hustle-float_desktop-top-offset-label').addClass('sui-hidden');
9617 this.$('#hustle-float_desktop-bottom-offset-label').removeClass('sui-hidden');
9618 } else {
9619 this.$('#hustle-float_desktop-top-offset-label').removeClass('sui-hidden');
9620 this.$('#hustle-float_desktop-bottom-offset-label').addClass('sui-hidden');
9621 }
9622 } else if ('float_mobile_position' in changed) {
9623 if ('right' === changed.float_mobile_position) {
9624 this.$('#hustle-float_mobile-left-offset-label').addClass('sui-hidden');
9625 this.$('#hustle-float_mobile-right-offset-label').removeClass('sui-hidden');
9626 this.$('#hustle-float_mobile-offset-x-wrapper').removeClass('sui-hidden');
9627 } else if ('left' === changed.float_mobile_position) {
9628 this.$('#hustle-float_mobile-left-offset-label').removeClass('sui-hidden');
9629 this.$('#hustle-float_mobile-right-offset-label').addClass('sui-hidden');
9630 this.$('#hustle-float_mobile-offset-x-wrapper').removeClass('sui-hidden');
9631 } else {
9632 this.$('#hustle-float_mobile-offset-x-wrapper').addClass('sui-hidden');
9633 }
9634 } else if ('float_mobile_position_y' in changed) {
9635 if ('bottom' === changed.float_mobile_position_y) {
9636 this.$('#hustle-float_mobile-top-offset-label').addClass('sui-hidden');
9637 this.$('#hustle-float_mobile-bottom-offset-label').removeClass('sui-hidden');
9638 } else {
9639 this.$('#hustle-float_mobile-top-offset-label').removeClass('sui-hidden');
9640 this.$('#hustle-float_mobile-bottom-offset-label').addClass('sui-hidden');
9641 }
9642 }
9643 }
9644 }));
9645 });
9646 function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9647
9648 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
9649
9650 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
9651
9652 Hustle.define('Modals.Services_Platforms', function () {
9653 'use strict';
9654
9655 return Backbone.View.extend({
9656 el: '#hustle-dialog--add-platforms',
9657 selectedPlatforms: [],
9658 events: {
9659 'click .sui-box-selector input': 'selectPlatforms',
9660 //Add platforms
9661 'click #hustle-add-platforms': 'addPlatforms'
9662 },
9663 initialize: function initialize(platforms) {
9664 this.selectedPlatforms = platforms;
9665 this.$('.hustle-add-platforms-option').prop('checked', false).prop('disabled', false);
9666
9667 var _iterator = _createForOfIteratorHelper(this.selectedPlatforms),
9668 _step;
9669
9670 try {
9671 for (_iterator.s(); !(_step = _iterator.n()).done;) {
9672 var platform = _step.value;
9673 this.$('#hustle-social--' + platform).prop('checked', true).prop('disabled', true);
9674 }
9675 } catch (err) {
9676 _iterator.e(err);
9677 } finally {
9678 _iterator.f();
9679 }
9680 },
9681 selectPlatforms: function selectPlatforms(e) {
9682 var $input = this.$(e.target),
9683 $selectorLabel = this.$el.find('label[for="' + $input.attr('id') + '"]'),
9684 value = $input.val();
9685 $selectorLabel.toggleClass('selected');
9686
9687 if ($input.prop('checked')) {
9688 this.selectedPlatforms.push(value);
9689 } else {
9690 this.selectedPlatforms = _.without(this.selectedPlatforms, value);
9691 }
9692 },
9693 checkPlatforms: function checkPlatforms() {
9694 var _iterator2 = _createForOfIteratorHelper(this.selectedPlatforms),
9695 _step2;
9696
9697 try {
9698 for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
9699 var platform = _step2.value;
9700
9701 if (!this.$('#hustle-social--' + platform).prop('checked')) {
9702 this.selectedPlatforms = _.without(this.selectedPlatforms, platform);
9703 }
9704 }
9705 } catch (err) {
9706 _iterator2.e(err);
9707 } finally {
9708 _iterator2.f();
9709 }
9710 },
9711 addPlatforms: function addPlatforms(e) {
9712 var $button = this.$(e.target);
9713 $button.addClass('sui-button-onload');
9714 this.checkPlatforms();
9715 this.trigger('platforms:added', this.selectedPlatforms);
9716 setTimeout(function () {
9717 // Hide dialog
9718 SUI.closeModal();
9719 $button.removeClass('sui-button-onload');
9720 }, 500);
9721 }
9722 });
9723 });
9724 Hustle.define('SShare.View', function ($) {
9725 'use strict';
9726
9727 return Hustle.View.extend(_.extend({}, Hustle.get('Mixins.Wizard_View'), {
9728 _events: {
9729 'hustle_update_prewiev #hui-preview-social-shares-floating': 'updatePreview'
9730 },
9731 updatePreview: function updatePreview() {
9732 var previewData = _.extend({}, this.model.toJSON(), this.getDataToSave());
9733
9734 $.ajax({
9735 type: 'POST',
9736 url: ajaxurl,
9737 dataType: 'json',
9738 data: {
9739 action: 'hustle_preview_module',
9740 id: this.model.get('module_id'),
9741 previewData: previewData
9742 },
9743 success: function success(res) {
9744 if (res.success) {
9745 var $floatingContainer = $('#hui-preview-social-shares-floating'),
9746 $widgetContainer = $('#hui-preview-social-shares-widget');
9747 $floatingContainer.html(res.data.floatingHtml);
9748 $widgetContainer.html(res.data.widgetHtml);
9749
9750 if (res.data.style) {
9751 $floatingContainer.append(res.data.style);
9752 }
9753
9754 $('.hustle-share-icon').on('click', function (ev) {
9755 return ev.preventDefault();
9756 });
9757 }
9758 }
9759 });
9760 },
9761
9762 /**
9763 * Overriding.
9764 *
9765 * @param {Object} opts
9766 */
9767 setTabsViews: function setTabsViews(opts) {
9768 this.contentView = opts.contentView;
9769 this.displayView = opts.displayView;
9770 this.designView = opts.designView;
9771 this.visibilityView = opts.visibilityView;
9772 this.moduleType = this.model.get('module_type');
9773 },
9774
9775 /**
9776 * Overriding.
9777 */
9778 renderTabs: function renderTabs() {
9779 // Services
9780 this.contentView.delegateEvents(); // Appearance view
9781
9782 this.designView.delegateEvents(); // Display Options View
9783
9784 this.displayView.delegateEvents(); // Visibility view.
9785
9786 this.visibilityView.delegateEvents();
9787 this.visibilityView.afterRender();
9788 },
9789
9790 /**
9791 * Overriding.
9792 */
9793 sanitizeData: function sanitizeData() {},
9794
9795 /**
9796 * Overriding.
9797 */
9798 getDataToSave: function getDataToSave() {
9799 return {
9800 content: this.contentView.model.toJSON(),
9801 display: this.displayView.model.toJSON(),
9802 design: this.designView.model.toJSON(),
9803 visibility: this.visibilityView.model.toJSON()
9804 };
9805 }
9806 }));
9807 });
9808 (function () {
9809 'use strict';
9810 /**
9811 * Listing Page
9812 */
9813
9814 (function () {
9815 var page = '_page_hustle_popup_listing';
9816
9817 if (page !== pagenow.substr(pagenow.length - page.length)) {
9818 return;
9819 }
9820
9821 new Optin.listingBase({
9822 moduleType: optinVars.current.module_type
9823 });
9824 })();
9825 /**
9826 * Edit or New page
9827 */
9828
9829
9830 (function () {
9831 var page = '_page_hustle_popup';
9832
9833 if (page !== pagenow.substr(pagenow.length - page.length)) {
9834 return;
9835 }
9836
9837 var View = Hustle.View.extend(Hustle.get('Mixins.Wizard_View')),
9838 ViewContent = Hustle.View.extend(Hustle.get('Mixins.Module_Content')),
9839 ViewEmails = Hustle.View.extend(Hustle.get('Mixins.Module_Emails')),
9840 ViewDesign = Hustle.View.extend(Hustle.get('Mixins.Module_Design')),
9841 ViewVisibility = Hustle.View.extend(Hustle.get('Mixins.Module_Visibility')),
9842 ViewSettings = Hustle.View.extend(Hustle.get('Mixins.Module_Settings')),
9843 ViewIntegrations = Hustle.get('Module.IntegrationsView'),
9844 ModelView = Module.Model,
9845 BaseModel = Hustle.get('Models.M');
9846 return new View({
9847 model: new ModelView(optinVars.current.data || {}),
9848 contentView: new ViewContent({
9849 BaseModel: BaseModel
9850 }),
9851 emailsView: new ViewEmails({
9852 BaseModel: BaseModel
9853 }),
9854 designView: new ViewDesign({
9855 BaseModel: BaseModel
9856 }),
9857 integrationsView: new ViewIntegrations({
9858 BaseModel: BaseModel
9859 }),
9860 visibilityView: new ViewVisibility({
9861 BaseModel: BaseModel
9862 }),
9863 settingsView: new ViewSettings({
9864 BaseModel: BaseModel
9865 })
9866 });
9867 })();
9868 })();
9869 (function () {
9870 'use strict';
9871 /**
9872 * Listing Page
9873 */
9874
9875 (function () {
9876 var page = '_page_hustle_slidein_listing';
9877
9878 if (page !== pagenow.substr(pagenow.length - page.length)) {
9879 return;
9880 }
9881
9882 new Optin.listingBase({
9883 moduleType: optinVars.current.module_type
9884 });
9885 })();
9886 /**
9887 * Edit or New page
9888 */
9889
9890
9891 (function () {
9892 var page = '_page_hustle_slidein';
9893
9894 if (page !== pagenow.substr(pagenow.length - page.length)) {
9895 return;
9896 }
9897
9898 var View = Hustle.View.extend(Hustle.get('Mixins.Wizard_View')),
9899 ViewContent = Hustle.View.extend(Hustle.get('Mixins.Module_Content')),
9900 ViewEmails = Hustle.View.extend(Hustle.get('Mixins.Module_Emails')),
9901 ViewDesign = Hustle.View.extend(Hustle.get('Mixins.Module_Design')),
9902 ViewVisibility = Hustle.View.extend(Hustle.get('Mixins.Module_Visibility')),
9903 ViewSettings = Hustle.View.extend(Hustle.get('Mixins.Module_Settings')),
9904 ViewIntegrations = Hustle.get('Module.IntegrationsView'),
9905 ModelView = Module.Model,
9906 BaseModel = Hustle.get('Models.M');
9907 return new View({
9908 model: new ModelView(optinVars.current.data || {}),
9909 contentView: new ViewContent({
9910 BaseModel: BaseModel
9911 }),
9912 emailsView: new ViewEmails({
9913 BaseModel: BaseModel
9914 }),
9915 designView: new ViewDesign({
9916 BaseModel: BaseModel
9917 }),
9918 integrationsView: new ViewIntegrations({
9919 BaseModel: BaseModel
9920 }),
9921 visibilityView: new ViewVisibility({
9922 BaseModel: BaseModel
9923 }),
9924 settingsView: new ViewSettings({
9925 BaseModel: BaseModel
9926 })
9927 });
9928 })();
9929 })();
9930 (function () {
9931 'use strict'; // Listings Page
9932
9933 (function () {
9934 var page = '_page_hustle_embedded_listing';
9935
9936 if (page !== pagenow.substr(pagenow.length - page.length)) {
9937 return;
9938 }
9939
9940 new Optin.listingBase({
9941 moduleType: optinVars.current.module_type
9942 });
9943 })(); // Wizard Page
9944
9945
9946 (function () {
9947 var page = '_page_hustle_embedded';
9948
9949 if (page !== pagenow.substr(pagenow.length - page.length)) {
9950 return;
9951 }
9952
9953 var view = Hustle.View.extend(Hustle.get('Mixins.Wizard_View')),
9954 ViewContent = Hustle.View.extend(Hustle.get('Mixins.Module_Content')),
9955 ViewEmails = Hustle.View.extend(Hustle.get('Mixins.Module_Emails')),
9956 ViewDesign = Hustle.View.extend(Hustle.get('Mixins.Module_Design')),
9957 ViewDisplay = Hustle.View.extend(Hustle.get('Mixins.Module_Display')),
9958 ViewVisibility = Hustle.View.extend(Hustle.get('Mixins.Module_Visibility')),
9959 ViewSettings = Hustle.View.extend(Hustle.get('Mixins.Module_Settings')),
9960 ViewIntegrations = Hustle.get('Module.IntegrationsView'),
9961 viewModel = Module.Model,
9962 BaseModel = Hustle.get('Models.M');
9963 return new view({
9964 model: new viewModel(optinVars.current.data || {}),
9965 contentView: new ViewContent({
9966 BaseModel: BaseModel
9967 }),
9968 emailsView: new ViewEmails({
9969 BaseModel: BaseModel
9970 }),
9971 designView: new ViewDesign({
9972 BaseModel: BaseModel
9973 }),
9974 integrationsView: new ViewIntegrations({
9975 BaseModel: BaseModel
9976 }),
9977 displayView: new ViewDisplay({
9978 BaseModel: BaseModel
9979 }),
9980 visibilityView: new ViewVisibility({
9981 BaseModel: BaseModel
9982 }),
9983 settingsView: new ViewSettings({
9984 BaseModel: BaseModel
9985 })
9986 });
9987 })();
9988 })();
9989 (function () {
9990 'use strict';
9991 /**
9992 * Listing Page.
9993 */
9994
9995 (function () {
9996 var page = '_page_hustle_sshare_listing';
9997
9998 if (page !== pagenow.substr(pagenow.length - page.length)) {
9999 return;
10000 }
10001
10002 new Optin.listingBase({
10003 moduleType: optinVars.current.module_type
10004 });
10005 })();
10006 /**
10007 * Wizard page.
10008 */
10009
10010
10011 (function () {
10012 var page = '_page_hustle_sshare';
10013
10014 if (page !== pagenow.substr(pagenow.length - page.length)) {
10015 return;
10016 }
10017
10018 var view = Hustle.get('SShare.View'),
10019 ViewContent = Hustle.get('SShare.Content_View'),
10020 ViewDisplay = Hustle.get('SShare.Display_View'),
10021 ViewDesign = Hustle.get('SShare.Design_View'),
10022 ViewVisibility = Hustle.View.extend(Hustle.get('Mixins.Module_Visibility')),
10023 viewModel = Module.Model,
10024 BaseModel = Hustle.get('Models.M');
10025 return new view({
10026 model: new viewModel(optinVars.current.data || {}),
10027 contentView: new ViewContent({
10028 BaseModel: BaseModel
10029 }),
10030 displayView: new ViewDisplay({
10031 BaseModel: BaseModel
10032 }),
10033 designView: new ViewDesign({
10034 BaseModel: BaseModel
10035 }),
10036 visibilityView: new ViewVisibility({
10037 BaseModel: BaseModel
10038 })
10039 });
10040 })();
10041 })();
10042 Hustle.define('Dashboard.View', function ($) {
10043 'use strict';
10044
10045 if ('toplevel_page_hustle' !== pagenow) {
10046 // eslint-disable-line camelcase
10047 return;
10048 }
10049
10050 var dashboardView = Backbone.View.extend({
10051 el: '.sui-wrap-hustle',
10052 events: {
10053 'click .hustle-preview-module-button': 'previewModule',
10054 'click .hustle-delete-module-button': 'openDeleteModal',
10055 'click .hustle-free-version-create': 'showUpgradeModal',
10056 'click .sui-dropdown .hustle-onload-icon-action': 'addLoadingIconToActionsButton',
10057 // Modules' actions.
10058 'click .hustle-single-module-button-action': 'handleSingleModuleAction'
10059 },
10060 initialize: function initialize() {
10061 if ($('#hustle-dialog--welcome').length) {
10062 this.openWelcomeDialog();
10063 } else if ($('#hustle-dialog--migrate').length) {
10064 this.openMigrateDialog();
10065 } else if ($('#hustle-dialog--release-highlight').length) {
10066 this.openReleaseHighlightDialog();
10067 }
10068
10069 this.doActionsBasedOnUrl();
10070 },
10071 doActionsBasedOnUrl: function doActionsBasedOnUrl() {
10072 // Display notice based on URL parameters.
10073 if (Module.Utils.getUrlParam('show-notice')) {
10074 var status = 'success' === Module.Utils.getUrlParam('show-notice') ? 'success' : 'error',
10075 notice = Module.Utils.getUrlParam('notice'),
10076 message = notice && 'undefined' !== optinVars.messages[notice] ? optinVars.messages[notice] : Module.Utils.getUrlParam('notice-message');
10077
10078 if ('undefined' !== typeof message && message.length) {
10079 Module.Notification.open(status, message);
10080 }
10081 }
10082 },
10083 previewModule: function previewModule(e) {
10084 e.preventDefault();
10085 var $button = $(e.currentTarget);
10086 this.getPreviewView().open($button.data('id'), $button.data('type'), $button, {
10087 module_name: $button.data('name')
10088 });
10089 },
10090 getPreviewView: function getPreviewView() {
10091 if (!this.previewView) {
10092 var previewView = Hustle.get('Modals.Preview');
10093 this.previewView = new previewView();
10094 }
10095
10096 return this.previewView;
10097 },
10098 showUpgradeModal: function showUpgradeModal(e) {
10099 if ('undefined' !== typeof e) {
10100 e.preventDefault();
10101 }
10102
10103 var $upgradeModal = $('#wph-upgrade-modal');
10104 $upgradeModal.addClass('wpmudev-modal-active');
10105 },
10106
10107 /**
10108 * @since 4.0.0
10109 * @param {Object} e
10110 */
10111 openDeleteModal: function openDeleteModal(e) {
10112 e.preventDefault();
10113 var $this = $(e.currentTarget),
10114 data = {
10115 id: $this.data('id'),
10116 nonce: $this.data('nonce'),
10117 action: 'delete',
10118 title: $this.data('title'),
10119 description: $this.data('description')
10120 };
10121 Module.deleteModal.open(data, $this[0]);
10122 },
10123 addLoadingIconToActionsButton: function addLoadingIconToActionsButton(e) {
10124 var $actionButton = $(e.currentTarget),
10125 $mainButton = $actionButton.closest('.sui-dropdown').find('.sui-dropdown-anchor');
10126 $mainButton.addClass('sui-button-onload');
10127 },
10128 openWelcomeDialog: function openWelcomeDialog() {
10129 Hustle.get('Modals.Welcome');
10130 },
10131 openMigrateDialog: function openMigrateDialog() {
10132 Hustle.get('Modals.Migration');
10133 },
10134 openReleaseHighlightDialog: function openReleaseHighlightDialog() {
10135 Hustle.get('Modals.ReleaseHighlight');
10136 },
10137 handleSingleModuleAction: function handleSingleModuleAction(e) {
10138 Module.handleActions.initAction(e, 'dashboard', this);
10139 }
10140 });
10141 new dashboardView();
10142 });
10143 Hustle.define('Integrations.View', function ($) {
10144 'use strict';
10145
10146 var page = '_page_hustle_integrations';
10147
10148 if (page !== pagenow.substr(pagenow.length - page.length)) {
10149 return;
10150 }
10151
10152 var integrationsView = Backbone.View.extend({
10153 el: '.sui-wrap-hustle',
10154 events: {
10155 'click .connect-integration': 'connectIntegration',
10156 'keypress .connect-integration': 'preventEnterKeyFromDoingThings'
10157 },
10158 initialize: function initialize() {
10159 this.stopListening(Hustle.Events, 'hustle:providers:reload', this.renderProvidersTables);
10160 this.listenTo(Hustle.Events, 'hustle:providers:reload', this.renderProvidersTables);
10161 this.render();
10162 },
10163 render: function render() {
10164 var $notConnectedWrapper = this.$el.find('#hustle-not-connected-providers-section'),
10165 $connectedWrapper = this.$el.find('#hustle-connected-providers-section');
10166
10167 if (0 < $notConnectedWrapper.length && 0 < $connectedWrapper.length) {
10168 this.renderProvidersTables();
10169 }
10170
10171 if (optinVars.integration_redirect) {
10172 this.handleIntegrationRedirect();
10173 }
10174 },
10175 renderProvidersTables: function renderProvidersTables() {
10176 var self = this,
10177 data = {};
10178 this.$el.find('.hustle-integrations-display').html("<div class=\"sui-notice hustle-integration-loading-notice\">\n\t\t\t\t\t\t<div class=\"sui-notice-content\">\n\t\t\t\t\t\t\t<div class=\"sui-notice-message\">\n\n\t\t\t\t\t\t\t\t<span class=\"sui-notice-icon sui-icon-loader sui-loading sui-md\" aria-hidden=\"true\"></span>\n\t\t\t\t\t\t\t\t<p>".concat(optinVars.fetching_list, "</p>\n\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>"));
10179 data.action = 'hustle_provider_get_providers';
10180 data._ajax_nonce = optinVars.providers_action_nonce; // eslint-disable-line camelcase
10181
10182 data.data = {};
10183 var ajax = $.post({
10184 url: ajaxurl,
10185 type: 'post',
10186 data: data
10187 }).done(function (result) {
10188 if (result && result.success) {
10189 self.$el.find('#hustle-not-connected-providers-section').html(result.data.not_connected);
10190 self.$el.find('#hustle-connected-providers-section').html(result.data.connected);
10191 }
10192 }); // Remove the preloader.
10193
10194 ajax.always(function () {
10195 self.$el.find('.hustle-integration-loading-notice').remove();
10196 });
10197 },
10198 // Prevent the enter key from opening integrations modals and breaking the page.
10199 preventEnterKeyFromDoingThings: function preventEnterKeyFromDoingThings(e) {
10200 if (13 === e.which) {
10201 // the enter key code
10202 e.preventDefault();
10203 }
10204 },
10205 connectIntegration: function connectIntegration(e) {
10206 Module.integrationsModal.open(e);
10207 },
10208 handleIntegrationRedirect: function handleIntegrationRedirect() {
10209 var data = optinVars.integration_redirect;
10210 var migrate = optinVars.integrations_migrate;
10211 window.history.pushState({}, document.title, optinVars.integrations_url);
10212
10213 if ('notification' === data.action) {
10214 var status = 'success' === data.status ? 'success' : 'error',
10215 delay = data.delay ? data.delay : 10000;
10216 Module.Notification.open(status, data.message, delay);
10217 } // We're not doing CTCT yet.
10218 //if ( migrate.hasOwnProperty( 'provider_modal' ) && 'constantcontact' === migrate.provider_modal ) {
10219 // Module.ProviderMigration.open( migrate.provider_modal );
10220 //}
10221
10222
10223 if (migrate.hasOwnProperty('provider_modal') && 'aweber' === migrate.provider_modal) {
10224 Module.ProviderMigration.open(migrate.provider_modal, migrate.integration_id);
10225 }
10226
10227 if (migrate.hasOwnProperty('migration_notificaiton')) {
10228 var _status = 'success' === migrate.migration_notificaiton.status ? 'success' : 'error',
10229 _delay = migrate.migration_notificaiton.delay ? migrate.migration_notificaiton.delay : 10000;
10230
10231 Module.Notification.open(_status, migrate.migration_notificaiton.message, _delay);
10232 }
10233 }
10234 });
10235 new integrationsView();
10236 });
10237 /* global moment */
10238 Hustle.define('Entries.View', function ($) {
10239 'use strict';
10240
10241 var page = '_page_hustle_entries';
10242
10243 if (page !== pagenow.substr(pagenow.length - page.length)) {
10244 return;
10245 }
10246
10247 var entriesView = Backbone.View.extend({
10248 el: '.sui-wrap-hustle',
10249 events: {
10250 'click .sui-pagination-wrap .hustle-open-inline-filter': 'openFilterInline',
10251 'click .sui-pagination-wrap .hustle-open-dialog-filter': 'openFilterModal',
10252 'click .hustle-delete-entry-button': 'openDeleteModal',
10253 'click .sui-active-filter-remove': 'removeFilter',
10254 'change input[name=search_email]': 'toggleClearButton',
10255 'change input[name=date_range]': 'toggleClearButton',
10256 'apply.daterangepicker input[name=date_range]': 'toggleClearButton',
10257 'click .hustle-entries-clear-filter': 'clearFilter'
10258 },
10259 initialize: function initialize() {
10260 this.initializeDaterangepicker();
10261 var entriesAlert = $('.hui-entries-alert');
10262
10263 if (entriesAlert.length) {
10264 // Assign correct colspan.
10265 entriesAlert.attr('colspan', entriesAlert.closest('.sui-table').find('> thead tr th').length); // Show message.
10266
10267 entriesAlert.find('i').hide();
10268 entriesAlert.find('span').removeClass('sui-screen-reader-text');
10269 }
10270
10271 $('input[name=search_email]').trigger('change');
10272 },
10273 initializeDaterangepicker: function initializeDaterangepicker() {
10274 var $desktopInputs = this.$('.hui-box-actions input.hustle-entries-filter-date'),
10275 $mobileInput = this.$('#hustle-dialog--filter-entries input.hustle-entries-filter-date'),
10276 onApplyCallback = function onApplyCallback(ev, picker) {
10277 $(this).val(picker.startDate.format('MM/DD/YYYY') + ' - ' + picker.endDate.format('MM/DD/YYYY'));
10278 }; // Initialize for desktop fields.
10279
10280
10281 var options = {
10282 autoUpdateInput: false,
10283 autoApply: true,
10284 alwaysShowCalendars: true,
10285 locale: optinVars.daterangepicker,
10286 ranges: this.getDaterangepickerRanges()
10287 };
10288 $desktopInputs.daterangepicker(options);
10289 $desktopInputs.on('apply.daterangepicker', onApplyCallback); // Initialize for mobile field.
10290
10291 var mobileOptions = Object.assign({
10292 parentEl: '#hustle-dialog--filter-entries .sui-box-body'
10293 }, options);
10294 $mobileInput.daterangepicker(mobileOptions);
10295 $mobileInput.on('apply.daterangepicker', onApplyCallback);
10296 },
10297 getDaterangepickerRanges: function getDaterangepickerRanges() {
10298 var rangesWithLabels = {};
10299 var labels = optinVars.daterangepicker.ranges,
10300 momentRanges = {
10301 today: [moment(), moment()],
10302 yesterday: [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
10303 last_seven_days: [moment().subtract(6, 'days'), moment()],
10304 last_thirty_days: [moment().subtract(29, 'days'), moment()],
10305 this_month: [moment().startOf('month'), moment().endOf('month')],
10306 last_month: [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
10307 };
10308
10309 for (var key in momentRanges) {
10310 var label = labels[key],
10311 range = momentRanges[key];
10312 rangesWithLabels[label] = range;
10313 }
10314
10315 return rangesWithLabels;
10316 },
10317 openFilterInline: function openFilterInline(e) {
10318 var $this = this.$(e.target),
10319 $wrapper = $this.closest('.sui-pagination-wrap'),
10320 $button = $wrapper.find('.sui-button-icon'),
10321 $filters = $this.closest('.hui-actions-bar').next('.sui-pagination-filter');
10322 $button.toggleClass('sui-active');
10323 $filters.toggleClass('sui-open');
10324 e.preventDefault();
10325 e.stopPropagation();
10326 },
10327 openFilterModal: function openFilterModal(e) {
10328 e.preventDefault();
10329 SUI.openModal('hustle-dialog--filter-entries', $(e.currentTarget)[0], this.$('#hustle-dialog--filter-entries .hustle-modal-close')[0], true);
10330 },
10331 removeFilter: function removeFilter(e) {
10332 var $this = this.$(e.target),
10333 possibleFilters = ['order_by', 'search_email', 'date_range'],
10334 currentFilter = $this.data('filter'),
10335 re = new RegExp('&' + currentFilter + '=[^&]*', 'i');
10336
10337 if (-1 !== possibleFilters.indexOf(currentFilter)) {
10338 location.href = location.href.replace(re, '');
10339 }
10340 },
10341 openDeleteModal: function openDeleteModal(e) {
10342 e.preventDefault();
10343 var $this = $(e.target),
10344 data = {
10345 id: $this.data('id'),
10346 nonce: $this.data('nonce'),
10347 action: 'delete',
10348 title: $this.data('title'),
10349 description: $this.data('description'),
10350 actionClass: ''
10351 };
10352 Module.deleteModal.open(data, $this[0]);
10353 },
10354 toggleClearButton: function toggleClearButton(e) {
10355 var $form = $(e.target).closest('form'),
10356 $clearFilter = $form.find('.hustle-entries-clear-filter');
10357
10358 if ($form.find('input[name=search_email]').val() || $form.find('input[name=date_range]').val()) {
10359 $clearFilter.prop('disabled', false);
10360 } else {
10361 $clearFilter.prop('disabled', true);
10362 }
10363 },
10364 clearFilter: function clearFilter(e) {
10365 e.preventDefault();
10366 this.$('input[name=search_email]').val('');
10367 this.$('input[name=date_range]').val('');
10368 this.toggleClearButton(e);
10369 }
10370 });
10371 new entriesView();
10372 });
10373 Hustle.define('ProviderNotice.View', function ($) {
10374 'use strict';
10375
10376 var providerNotice = Backbone.View.extend({
10377 el: '.hustle-provider-notice',
10378 cookieKey: '',
10379 events: {
10380 'click .dismiss-provider-migration-notice': 'HideProviderNotice'
10381 },
10382 initialize: function initialize() {
10383 this.cookieKey = 'provider_migration_notice_';
10384
10385 if ($('.hustle-provider-notice').length) {
10386 this.showProviderNotice();
10387 }
10388 },
10389 HideProviderNotice: function HideProviderNotice(e) {
10390 Optin.cookie.set(this.cookieKey + $(e.currentTarget).data('name'), 1, 7);
10391 location.reload();
10392 },
10393 showProviderNotice: function showProviderNotice() {
10394 var provider = $('.hustle-provider-notice').data('name'),
10395 notice = Optin.cookie.get(this.cookieKey + provider);
10396
10397 if (1 !== notice) {
10398 $('.hustle_migration_notice__' + provider).show();
10399 }
10400 }
10401 });
10402 new providerNotice();
10403 });
10404 /* global tinyMCE */
10405 Hustle.define('Settings.View', function ($, doc, win) {
10406 'use strict';
10407
10408 var page = '_page_hustle_settings';
10409
10410 if (page !== pagenow.substr(pagenow.length - page.length)) {
10411 return;
10412 }
10413
10414 var viewSettings = Backbone.View.extend({
10415 el: '.sui-wrap-hustle',
10416 events: {
10417 'click .sui-sidenav .sui-vertical-tab a': 'sidenav',
10418 'change select.sui-mobile-nav': 'sidenavMobile',
10419 'click .sui-pagination-wrap > button': 'pagination',
10420 'click .hustle-load-on-click': 'addLoadingState',
10421 // Save settings.
10422 'click .hustle-settings-save': 'handleSave'
10423 },
10424 initialize: function initialize() {
10425 var me = this,
10426 recaptchaView = Hustle.get('Settings.reCaptcha_Settings'),
10427 topMetricsView = Hustle.get('Settings.Top_Metrics_View'),
10428 privacySettings = Hustle.get('Settings.Privacy_Settings'),
10429 permissionsView = Hustle.get('Settings.Permissions_View'),
10430 dataSettings = Hustle.get('Settings.Data_Settings'),
10431 palettesView = Hustle.get('Settings.Palettes');
10432 this.recaptchaView = new recaptchaView();
10433 new topMetricsView();
10434 new privacySettings();
10435 new permissionsView();
10436 new dataSettings();
10437 new palettesView();
10438 $(win).off('popstate', $.proxy(me.tabUpdate, me));
10439 $(win).on('popstate', $.proxy(me.tabUpdate, me));
10440 Hustle.Events.trigger('view.rendered', this);
10441 this.doActionsBasedOnUrl();
10442 },
10443 doActionsBasedOnUrl: function doActionsBasedOnUrl() {
10444 // Do stuff based on URL parameters.
10445 if (Module.Utils.getUrlParam('show-notice')) {
10446 // Display notices.
10447 var status = 'success' === Module.Utils.getUrlParam('show-notice') ? 'success' : 'error',
10448 notice = Module.Utils.getUrlParam('notice'),
10449 message = notice && 'undefined' !== optinVars.messages[notice] ? optinVars.messages[notice] : Module.Utils.getUrlParam('notice-message');
10450
10451 if ('undefined' !== typeof message && message.length) {
10452 Module.Notification.open(status, message);
10453 }
10454 } else if (Module.Utils.getUrlParam('404-downgrade-modal')) {
10455 // Display the downgrade to 4.0.4 modal.
10456 if (this.$('#hustle-dialog--404-downgrade').length) {
10457 SUI.openModal('hustle-dialog--404-downgrade', 'hustle-popup-number');
10458 }
10459 }
10460 },
10461 sidenav: function sidenav(e) {
10462 var tabName = $(e.target).data('tab');
10463
10464 if (tabName) {
10465 this.tabJump(tabName, true);
10466 }
10467
10468 e.preventDefault();
10469 },
10470 sidenavMobile: function sidenavMobile(e) {
10471 var tabName = $(e.currentTarget).val();
10472
10473 if (tabName) {
10474 this.tabJump(tabName, true);
10475 }
10476 },
10477 tabUpdate: function tabUpdate(e) {
10478 var state = e.originalEvent.state;
10479
10480 if (state) {
10481 this.tabJump(state.tabSelected);
10482 }
10483 },
10484 tabJump: function tabJump(tabName, updateHistory) {
10485 var $tab = this.$el.find('a[data-tab="' + tabName + '"]'),
10486 $sidenav = $tab.closest('.sui-vertical-tabs'),
10487 $tabs = $sidenav.find('.sui-vertical-tab'),
10488 $content = this.$el.find('.sui-box[data-tab]'),
10489 $current = this.$el.find('.sui-box[data-tab="' + tabName + '"]');
10490
10491 if (updateHistory) {
10492 history.pushState({
10493 tabSelected: tabName
10494 }, 'Hustle Settings', 'admin.php?page=hustle_settings&section=' + tabName);
10495 }
10496
10497 $tabs.removeClass('current');
10498 $content.hide();
10499 $tab.parent().addClass('current');
10500 $current.show();
10501 },
10502 pagination: function pagination(e) {
10503 var $this = this.$(e.target),
10504 $wrapper = $this.closest('.sui-pagination-wrap'),
10505 $button = $wrapper.find('.sui-button-icon'),
10506 $filters = $wrapper.next('.sui-pagination-filter');
10507 $button.toggleClass('sui-active');
10508 $filters.toggleClass('sui-open');
10509 e.preventDefault();
10510 e.stopPropagation();
10511 },
10512 // ============================================================
10513 // Handle saving actions
10514 handleSave: function handleSave(e) {
10515 e.preventDefault();
10516 var self = this,
10517 $this = $(e.currentTarget),
10518 relatedFormId = $this.data('form-id'),
10519 actionData = $this.data();
10520 var data = new FormData();
10521 tinyMCE.triggerSave(); // Grab the form's data if the action has a related form.
10522
10523 if ('undefined' !== typeof relatedFormId) {
10524 var $form = $('#' + relatedFormId);
10525
10526 if ($form.length) {
10527 data = new FormData($form[0]); // Add unchecked checkboxes.
10528
10529 $.each($form.find('input[type=checkbox]'), function () {
10530 var $input = $(this);
10531
10532 if (!$input.is(':checked')) {
10533 data.append($input.attr('name'), '0');
10534 }
10535 });
10536 }
10537 }
10538
10539 $.each(actionData, function (name, value) {
10540 return data.append(name, value);
10541 });
10542 data.append('_ajax_nonce', optinVars.current.save_settings_nonce);
10543 data.append('action', 'hustle_save_settings'); // Handle the button behavior.
10544
10545 $this.addClass('sui-button-onload');
10546 $this.prop('disabled', true);
10547 $.ajax({
10548 url: ajaxurl,
10549 type: 'POST',
10550 data: data,
10551 contentType: false,
10552 processData: false
10553 }).done(function (res) {
10554 // If the response returned actionable data.
10555 if (res.data) {
10556 // If there's a defined callback, call it.
10557 if (res.data.callback && 'undefined' !== self[res.data.callback]) {
10558 // This calls the "action{ hustle action }" functions from this view.
10559 // For example: actionToggleStatus();
10560 self[res.data.callback]($this, res.data, res.success);
10561 }
10562
10563 if (res.data.url) {
10564 if (true === res.data.url) {
10565 location.reload();
10566 } else {
10567 location.replace(res.data.url);
10568 }
10569 } else if (res.data.notification) {
10570 Module.Notification.open(res.data.notification.status, res.data.notification.message, res.data.notification.delay);
10571 } // Don't remove the 'loading' icon when redirecting/reloading.
10572
10573
10574 if (!res.data.url) {
10575 $('.sui-button-onload').removeClass('sui-button-onload');
10576 $this.prop('disabled', false);
10577 }
10578 } else {
10579 // Use default actions otherwise.
10580 if (res.success) {
10581 Module.Notification.open('success', optinVars.messages.settings_saved);
10582 } else {
10583 Module.Notification.open('error', optinVars.messages.something_went_wrong_reload);
10584 }
10585
10586 $('.sui-button-onload').removeClass('sui-button-onload');
10587 $this.prop('disabled', false);
10588 }
10589 }).fail(function () {
10590 $('.sui-button-onload').removeClass('sui-button-onload');
10591 $this.prop('disabled', false);
10592 Module.Notification.open('error', optinVars.messages.something_went_wrong);
10593 });
10594 },
10595
10596 /**
10597 * Callback action for when saving reCaptchas.
10598 *
10599 * @since 4.1.0
10600 */
10601 actionSaveRecaptcha: function actionSaveRecaptcha() {
10602 this.recaptchaView.maybeRenderRecaptchas();
10603 },
10604 addLoadingState: function addLoadingState(e) {
10605 var $button = $(e.currentTarget);
10606 $button.addClass('sui-button-onload');
10607 }
10608 });
10609 new viewSettings();
10610 });