| 1 |
"use strict"; |
| 2 |
|
| 3 |
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } |
| 4 |
; |
| 5 |
(function ($, window, document, undefined) { |
| 6 |
'use strict'; |
| 7 |
|
| 8 |
var merchant = merchant || {}; |
| 9 |
var params = new URLSearchParams(window.location.search); |
| 10 |
var currentModule = params.get('module'); |
| 11 |
$(document).ready(function () { |
| 12 |
// AjaxSave |
| 13 |
var $ajaxForm = $('.merchant-module-page-ajax-form'); |
| 14 |
var $ajaxHeader = $('.merchant-module-page-ajax-header'); |
| 15 |
var $ajaxSaveBtn = $('.merchant-module-save-button'); |
| 16 |
$('.merchant-module-page-content').on('change keypress change.merchant', function (e) { |
| 17 |
if ($(e.target).hasClass('merchant-backup-file') || $(e.target).hasClass('merchant-search-field')) { |
| 18 |
return; |
| 19 |
} |
| 20 |
|
| 21 |
// The licence key and the module feedback textarea save through their |
| 22 |
// own handlers, so editing them leaves the save bar alone. |
| 23 |
if (!$(e.target).is('.merchant-module-question-answer-textarea, .merchant-license-code-input')) { |
| 24 |
if (!merchant.show_save) { |
| 25 |
$ajaxHeader.addClass('merchant-show'); |
| 26 |
$ajaxHeader.removeClass('merchant-saving'); |
| 27 |
merchant.show_save = true; |
| 28 |
} |
| 29 |
} |
| 30 |
GroubField.initFlag(); |
| 31 |
}); |
| 32 |
|
| 33 |
// Warn the user before leaving the page with unsaved changes. |
| 34 |
window.addEventListener('beforeunload', function (e) { |
| 35 |
if (merchant.show_save) { |
| 36 |
e.preventDefault(); |
| 37 |
} |
| 38 |
}); |
| 39 |
$ajaxForm.ajaxForm({ |
| 40 |
beforeSubmit: function beforeSubmit(arr) { |
| 41 |
$ajaxHeader.addClass('merchant-saving'); |
| 42 |
|
| 43 |
// Serialize merchant[] fields into a single JSON payload |
| 44 |
// to bypass PHP's max_input_vars limit (Issue #608). |
| 45 |
var merchantData = {}; |
| 46 |
var keepIndexes = []; |
| 47 |
arr.forEach(function (field, index) { |
| 48 |
if (field.name === 'merchant-search-field') { |
| 49 |
keepIndexes.push(index); |
| 50 |
return; |
| 51 |
} |
| 52 |
if (field.name && field.name.indexOf('merchant[') === 0) { |
| 53 |
var keys = field.name.match(/\[([^\]]*)\]/g); |
| 54 |
if (!keys) { |
| 55 |
return; |
| 56 |
} |
| 57 |
var obj = merchantData; |
| 58 |
for (var i = 0; i < keys.length - 1; i++) { |
| 59 |
var key = keys[i].slice(1, -1); |
| 60 |
if (obj[key] === undefined) { |
| 61 |
var nextKey = keys[i + 1] ? keys[i + 1].slice(1, -1) : ''; |
| 62 |
obj[key] = /^\d+$/.test(nextKey) ? [] : {}; |
| 63 |
} |
| 64 |
obj = obj[key]; |
| 65 |
} |
| 66 |
var lastKey = keys[keys.length - 1].slice(1, -1); |
| 67 |
if (lastKey === '') { |
| 68 |
// Array notation (e.g. name="...[show_pages][]") — push into parent array. |
| 69 |
var parentKey = keys[keys.length - 2].slice(1, -1); |
| 70 |
var parentObj = merchantData; |
| 71 |
for (var j = 0; j < keys.length - 2; j++) { |
| 72 |
parentObj = parentObj[keys[j].slice(1, -1)]; |
| 73 |
} |
| 74 |
if (!Array.isArray(parentObj[parentKey])) { |
| 75 |
parentObj[parentKey] = []; |
| 76 |
} |
| 77 |
parentObj[parentKey].push(field.value); |
| 78 |
} else { |
| 79 |
obj[lastKey] = field.value; |
| 80 |
} |
| 81 |
} else { |
| 82 |
keepIndexes.push(index); |
| 83 |
} |
| 84 |
}); |
| 85 |
|
| 86 |
// Ensure unchecked checkboxes inside hydrated FC rows are saved as 0. |
| 87 |
// serializeArray() omits unchecked checkboxes entirely, which would |
| 88 |
// silently drop the field from the JSON payload and lose the "off" state. |
| 89 |
$('.merchant-flexible-content .layout:not([data-deferred])').each(function () { |
| 90 |
$(this).find('.layout-body input[type="checkbox"]:not(:checked)').each(function () { |
| 91 |
var name = $(this).attr('name') || ''; |
| 92 |
if (name.indexOf('merchant[') !== 0) { |
| 93 |
return; |
| 94 |
} |
| 95 |
|
| 96 |
// Skip checkbox_multiple (name ends with []) — absence = empty array. |
| 97 |
if (/\[\]$/.test(name)) { |
| 98 |
return; |
| 99 |
} |
| 100 |
var keys = name.match(/\[([^\]]*)\]/g); |
| 101 |
if (!keys) { |
| 102 |
return; |
| 103 |
} |
| 104 |
var obj = merchantData; |
| 105 |
for (var i = 0; i < keys.length - 1; i++) { |
| 106 |
var key = keys[i].slice(1, -1); |
| 107 |
if (obj[key] === undefined) { |
| 108 |
obj[key] = {}; |
| 109 |
} |
| 110 |
obj = obj[key]; |
| 111 |
} |
| 112 |
var lastKey = keys[keys.length - 1].slice(1, -1); |
| 113 |
if (obj[lastKey] === undefined) { |
| 114 |
obj[lastKey] = 0; |
| 115 |
} |
| 116 |
}); |
| 117 |
}); |
| 118 |
|
| 119 |
// Merge deferred (non-hydrated) layout row data into merchantData. |
| 120 |
// These rows have no form inputs, only a data-fields-json attribute. |
| 121 |
$('.merchant-flexible-content .layout[data-deferred="1"]').each(function () { |
| 122 |
var jsonStr = $(this).attr('data-fields-json'); |
| 123 |
if (!jsonStr) { |
| 124 |
return; |
| 125 |
} |
| 126 |
var deferredData; |
| 127 |
try { |
| 128 |
deferredData = JSON.parse(jsonStr); |
| 129 |
} catch (e) { |
| 130 |
return; |
| 131 |
} |
| 132 |
var $control = $(this).closest('.merchant-flexible-content-control'); |
| 133 |
var fcFieldId = $control.attr('data-id'); |
| 134 |
var rowIndex = parseInt($(this).find('.layout-count').text(), 10) - 1; |
| 135 |
if (!merchantData[fcFieldId]) { |
| 136 |
merchantData[fcFieldId] = {}; |
| 137 |
} |
| 138 |
|
| 139 |
// Build the row data from deferred values + layout/flexible_id from hidden inputs. |
| 140 |
var rowData = $.extend({}, deferredData.values || {}); |
| 141 |
rowData.layout = $(this).attr('data-type') || ''; |
| 142 |
rowData.flexible_id = $(this).find('.flexible-id').val() || ''; |
| 143 |
merchantData[fcFieldId][rowIndex] = rowData; |
| 144 |
}); |
| 145 |
var kept = keepIndexes.map(function (i) { |
| 146 |
return arr[i]; |
| 147 |
}); |
| 148 |
kept.push({ |
| 149 |
name: 'merchant_json_payload', |
| 150 |
value: JSON.stringify(merchantData) |
| 151 |
}); |
| 152 |
arr.length = 0; |
| 153 |
kept.forEach(function (item) { |
| 154 |
arr.push(item); |
| 155 |
}); |
| 156 |
}, |
| 157 |
success: function success() { |
| 158 |
$ajaxHeader.removeClass('merchant-show'); |
| 159 |
merchant.show_save = false; |
| 160 |
|
| 161 |
// Module Alert after Ajax Save |
| 162 |
if (!$('.merchant-module-action').hasClass('merchant-enabled')) { |
| 163 |
var $moduleAlert = $('.merchant-module-alert'); |
| 164 |
$moduleAlert.addClass('merchant-show'); |
| 165 |
$(document).off('click.merchant-alert-close'); |
| 166 |
$(document).on('click.merchant-alert-close', function (e) { |
| 167 |
if (!$(e.target).closest('.merchant-module-alert-wrapper').length) { |
| 168 |
$moduleAlert.removeClass('merchant-show'); |
| 169 |
$(document).off('click.merchant-alert-close'); |
| 170 |
} |
| 171 |
}); |
| 172 |
} |
| 173 |
$(document).trigger('save.merchant', [currentModule]); |
| 174 |
} |
| 175 |
}); |
| 176 |
var $disableModuleSubmitBtn = $('.merchant-module-question-answer-button'); |
| 177 |
var $disableModuleTextField = $('.merchant-module-question-answer-textarea'); |
| 178 |
$disableModuleTextField.on('input', function () { |
| 179 |
$disableModuleSubmitBtn.prop('disabled', $(this).val().trim() === ''); |
| 180 |
}); |
| 181 |
$disableModuleSubmitBtn.on('click', function (e) { |
| 182 |
e.preventDefault(); |
| 183 |
var message = $disableModuleTextField.val(); |
| 184 |
if (!message.trim()) { |
| 185 |
alert('Please provide the required information.'); |
| 186 |
return; |
| 187 |
} |
| 188 |
var $button = $(this); |
| 189 |
$('.merchant-module-question-answer-dropdown').removeClass('merchant-show'); |
| 190 |
$('.merchant-module-question-thank-you-dropdown').addClass('merchant-show'); |
| 191 |
window.wp.ajax.post('merchant_module_feedback', { |
| 192 |
subject: $disableModuleTextField.attr('data-subject'), |
| 193 |
message: message, |
| 194 |
module: $button.closest('.merchant-module-action').find('.merchant-module-page-button-action-activate').data('module'), |
| 195 |
nonce: window.merchant.nonce |
| 196 |
}); |
| 197 |
}); |
| 198 |
$('.merchant-module-page-button-action-activate').on('click', function (e) { |
| 199 |
e.preventDefault(); |
| 200 |
if ($(this).hasClass('merchant-module-deactivated-by-bp')) { |
| 201 |
return false; |
| 202 |
} |
| 203 |
$('.merchant-module-question-list-dropdown').removeClass('merchant-show'); |
| 204 |
$('.merchant-module-question-answer-dropdown').removeClass('merchant-show'); |
| 205 |
$('.merchant-module-question-answer-form').removeClass('merchant-show'); |
| 206 |
$('.merchant-module-question-answer-title').removeClass('merchant-show'); |
| 207 |
$('.merchant-module-question-thank-you-dropdown').removeClass('merchant-show'); |
| 208 |
$('.merchant-module-question-answer-textarea').val(''); |
| 209 |
window.wp.ajax.post('merchant_module_activate', { |
| 210 |
module: $(this).data('module'), |
| 211 |
nonce: window.merchant.nonce |
| 212 |
}).done(function () { |
| 213 |
$('body').removeClass('merchant-module-disabled').addClass('merchant-module-enabled'); |
| 214 |
$('.merchant-module-action').addClass('merchant-enabled'); |
| 215 |
}); |
| 216 |
}); |
| 217 |
$('.merchant-module-page-button-action-deactivate').on('click', function (e) { |
| 218 |
e.preventDefault(); |
| 219 |
window.wp.ajax.post('merchant_module_deactivate', { |
| 220 |
module: $(this).data('module'), |
| 221 |
nonce: window.merchant.nonce |
| 222 |
}).done(function () { |
| 223 |
$('body').removeClass('merchant-module-enabled').addClass('merchant-module-disabled'); |
| 224 |
$('.merchant-module-action').removeClass('merchant-enabled'); |
| 225 |
$('.merchant-module-question-list-dropdown').addClass('merchant-show'); |
| 226 |
}); |
| 227 |
}); |
| 228 |
$('.merchant-module-question-list-dropdown li').on('click', function (e) { |
| 229 |
$disableModuleSubmitBtn.prop('disabled', $disableModuleTextField.val().trim() === ''); |
| 230 |
var $question = $(this); |
| 231 |
var target = $question.data('answer-target'); |
| 232 |
var $answer = $('[data-answer-title="' + target + '"]'); |
| 233 |
if ($answer.length) { |
| 234 |
$answer.addClass('merchant-show').siblings().removeClass('merchant-show'); |
| 235 |
$('.merchant-module-question-answer-dropdown').addClass('merchant-show'); |
| 236 |
$('.merchant-module-question-answer-textarea').attr('data-subject', $question.text().trim()); |
| 237 |
} else { |
| 238 |
$('.merchant-module-question-thank-you-dropdown').addClass('merchant-show'); |
| 239 |
$('.merchant-module-question-answer-dropdown').removeClass('merchant-show'); |
| 240 |
} |
| 241 |
$('.merchant-module-question-answer-textarea').val(''); |
| 242 |
$('.merchant-module-question-list-dropdown').removeClass('merchant-show'); |
| 243 |
}); |
| 244 |
$('.merchant-module-dropdown-close').on('click', function (e) { |
| 245 |
e.preventDefault(); |
| 246 |
$(this).closest('.merchant-module-dropdown').removeClass('merchant-show'); |
| 247 |
}); |
| 248 |
$('.merchant-module-page-button-deactivate').on('click', function (e) { |
| 249 |
e.preventDefault(); |
| 250 |
var $button = $(this); |
| 251 |
var $dropdown = $('.merchant-module-deactivate-dropdown'); |
| 252 |
$dropdown.toggleClass('merchant-show'); |
| 253 |
$(document).off('click.merchant-close'); |
| 254 |
$(document).on('click.merchant-close', function (e) { |
| 255 |
if (!$(e.target).closest('.merchant-module-deactivate').length) { |
| 256 |
$dropdown.removeClass('merchant-show'); |
| 257 |
$(document).off('click.merchant-close'); |
| 258 |
} |
| 259 |
}); |
| 260 |
}); |
| 261 |
|
| 262 |
// Create a function that initializes a single range or all ranges in a context for range field |
| 263 |
function initMerchantRange() { |
| 264 |
var rangeFields = $(document).find('.merchant-range'); |
| 265 |
if (rangeFields.length === 0) { |
| 266 |
return; |
| 267 |
} |
| 268 |
rangeFields.each(function () { |
| 269 |
var $range = $(this); |
| 270 |
var $rangeInput = $range.find('.merchant-range-input'); |
| 271 |
var $numberInput = $range.find('.merchant-range-number-input'); |
| 272 |
$rangeInput.on('change input merchant.range merchant-init.range', function (e) { |
| 273 |
var $range = $(this); |
| 274 |
var value = (e.type === 'merchant' ? $numberInput.val() : $range.val()) || 0; |
| 275 |
var min = $range.attr('min') || 0; |
| 276 |
var max = $range.attr('max') || 1; |
| 277 |
var percentage = (value - min) / (max - min) * 100; |
| 278 |
if ($('body').hasClass('rtl')) { |
| 279 |
$range.css({ |
| 280 |
'background': 'linear-gradient(to left, #3858E9 0%, #3858E9 ' + percentage + '%, #ddd ' + percentage + '%, #ddd 100%)' |
| 281 |
}); |
| 282 |
} else { |
| 283 |
$range.css({ |
| 284 |
'background': 'linear-gradient(to right, #3858E9 0%, #3858E9 ' + percentage + '%, #ddd ' + percentage + '%, #ddd 100%)' |
| 285 |
}); |
| 286 |
} |
| 287 |
$rangeInput.val(value); |
| 288 |
$numberInput.val(value); |
| 289 |
}).trigger('merchant-init.range'); |
| 290 |
$numberInput.on('change input blur', function () { |
| 291 |
if ($rangeInput.hasClass('merchant-range-input')) { |
| 292 |
$rangeInput.val($(this).val()).trigger('merchant.range'); |
| 293 |
} |
| 294 |
}); |
| 295 |
}); |
| 296 |
} |
| 297 |
|
| 298 |
// 1. Initialize on DOM ready (existing fields) |
| 299 |
initMerchantRange(); |
| 300 |
$(document).on('click', '.merchant-module-page-setting-field-hidden-desc-trigger', function () { |
| 301 |
var $trigger = $(this); |
| 302 |
$trigger.toggleClass('expanded'); |
| 303 |
var showText = $trigger.attr('data-show-text'); |
| 304 |
var hiddenText = $trigger.attr('data-hidden-text'); |
| 305 |
$(this).find('span:first').text($trigger.text() === showText ? hiddenText : showText); |
| 306 |
$(this).closest('.merchant-module-page-setting-field').find('.merchant-module-page-setting-field-hidden-desc').stop(true, true).slideToggle('fast'); |
| 307 |
}); |
| 308 |
var moduleBackup = { |
| 309 |
init: function init() { |
| 310 |
this.events(); |
| 311 |
}, |
| 312 |
events: function events() { |
| 313 |
var self = this; |
| 314 |
$(document).on('click', '#download-backup-button', this.download.bind(this)); |
| 315 |
|
| 316 |
// Browse button opens file picker. |
| 317 |
$(document).on('click', '.merchant-dropzone__browse', function (e) { |
| 318 |
e.preventDefault(); |
| 319 |
e.stopPropagation(); |
| 320 |
$(this).closest('.merchant-dropzone').find('.merchant-backup-file').trigger('click'); |
| 321 |
}); |
| 322 |
|
| 323 |
// Auto-restore when a file is selected (via browse or drop). |
| 324 |
$(document).on('change', '#merchant-backup-file', function () { |
| 325 |
var file = this.files[0]; |
| 326 |
if (file) { |
| 327 |
self.restoreFile(file); |
| 328 |
} |
| 329 |
}); |
| 330 |
|
| 331 |
// Drag-and-drop visual feedback. |
| 332 |
var $dropzone = $('#merchant-restore-dropzone'); |
| 333 |
$dropzone.on('dragover dragenter', function (e) { |
| 334 |
e.preventDefault(); |
| 335 |
e.stopPropagation(); |
| 336 |
$(this).addClass('drag-over'); |
| 337 |
}); |
| 338 |
$dropzone.on('dragleave drop', function (e) { |
| 339 |
e.preventDefault(); |
| 340 |
e.stopPropagation(); |
| 341 |
$(this).removeClass('drag-over'); |
| 342 |
}); |
| 343 |
$dropzone.on('drop', function (e) { |
| 344 |
var file = e.originalEvent.dataTransfer.files[0]; |
| 345 |
if (file) { |
| 346 |
self.restoreFile(file); |
| 347 |
} |
| 348 |
}); |
| 349 |
}, |
| 350 |
download: function download(e) { |
| 351 |
var self = this; |
| 352 |
e.preventDefault(); |
| 353 |
var container = $('.merchant-module-page-setting-fields .backup-section'); |
| 354 |
var $status = container.find('.merchant-backup-status'); |
| 355 |
$status.removeAttr('class').addClass('merchant-backup-status').text(''); |
| 356 |
var module_id = $(e.target).attr('data-module-id'); |
| 357 |
$.ajax({ |
| 358 |
url: merchant_admin_options.ajaxurl, |
| 359 |
type: 'GET', |
| 360 |
data: { |
| 361 |
action: 'merchant_get_module_settings', |
| 362 |
nonce: merchant_admin_options.ajaxnonce, |
| 363 |
module_id: module_id |
| 364 |
}, |
| 365 |
beforeSend: function beforeSend() { |
| 366 |
self.showLoadingIndicator(container); |
| 367 |
}, |
| 368 |
success: function success(response) { |
| 369 |
self.hideLoadingIndicator(container); |
| 370 |
if (response.success) { |
| 371 |
var moduleSettings = response.data; |
| 372 |
var fileName = 'merchant-' + module_id + '-backup-' + new Date().toISOString().slice(0, 10) + '-' + new Date().getHours() + '-' + new Date().getMinutes() + '-' + new Date().getSeconds() + '.json'; |
| 373 |
self.downloadJson(moduleSettings, fileName); |
| 374 |
$status.addClass('is-success').text(merchant_admin_options.backup_success || 'Downloaded!'); |
| 375 |
} else { |
| 376 |
$status.addClass('is-error').text(response.data.message); |
| 377 |
} |
| 378 |
setTimeout(function () { |
| 379 |
$status.removeAttr('class').addClass('merchant-backup-status').text(''); |
| 380 |
}, 3000); |
| 381 |
}, |
| 382 |
error: function error(xhr, status, _error) { |
| 383 |
self.hideLoadingIndicator(container); |
| 384 |
$status.addClass('is-error').text(_error); |
| 385 |
setTimeout(function () { |
| 386 |
$status.removeAttr('class').addClass('merchant-backup-status').text(''); |
| 387 |
}, 3000); |
| 388 |
} |
| 389 |
}); |
| 390 |
}, |
| 391 |
restoreFile: function restoreFile(file) { |
| 392 |
var self = this; |
| 393 |
var container = $('.merchant-module-page-setting-fields .restore-section'); |
| 394 |
var module_id = $('#merchant-restore-dropzone').data('module-id'); |
| 395 |
self.hideError(); |
| 396 |
if (!file || file.type !== 'application/json') { |
| 397 |
self.displayError(merchant_admin_options.invalid_file); |
| 398 |
return; |
| 399 |
} |
| 400 |
var reader = new FileReader(); |
| 401 |
reader.onload = function (e) { |
| 402 |
var moduleSettings = e.target.result; |
| 403 |
$.ajax({ |
| 404 |
url: merchant_admin_options.ajaxurl, |
| 405 |
type: 'POST', |
| 406 |
data: { |
| 407 |
action: 'merchant_restore_module_settings', |
| 408 |
nonce: merchant_admin_options.ajaxnonce, |
| 409 |
module_id: module_id, |
| 410 |
module_settings: moduleSettings |
| 411 |
}, |
| 412 |
beforeSend: function beforeSend() { |
| 413 |
$('#merchant-restore-dropzone').addClass('is-loading'); |
| 414 |
self.showLoadingIndicator(container); |
| 415 |
}, |
| 416 |
success: function success(response) { |
| 417 |
if (response.success) { |
| 418 |
merchant.show_save = false; |
| 419 |
var $dz = $('#merchant-restore-dropzone'); |
| 420 |
$dz.removeClass('is-loading').addClass('is-success'); |
| 421 |
self.hideLoadingIndicator(container); |
| 422 |
setTimeout(function () { |
| 423 |
window.location.href = response.data.redirect_url; |
| 424 |
}, 1500); |
| 425 |
} else { |
| 426 |
self.displayError(response.data.message); |
| 427 |
} |
| 428 |
}, |
| 429 |
error: function error(xhr, status, _error2) { |
| 430 |
self.displayError(_error2); |
| 431 |
} |
| 432 |
}); |
| 433 |
}; |
| 434 |
reader.readAsText(file); |
| 435 |
}, |
| 436 |
downloadJson: function downloadJson(data, filename) { |
| 437 |
if (_typeof(data) === 'object') { |
| 438 |
data = JSON.stringify(data); |
| 439 |
} |
| 440 |
var blob = new Blob([data], { |
| 441 |
type: "application/json" |
| 442 |
}); |
| 443 |
var url = URL.createObjectURL(blob); |
| 444 |
var a = document.createElement('a'); |
| 445 |
a.href = url; |
| 446 |
a.download = filename; |
| 447 |
a.click(); |
| 448 |
URL.revokeObjectURL(url); |
| 449 |
}, |
| 450 |
displayError: function displayError(errorMsg) { |
| 451 |
var $dz = $('#merchant-restore-dropzone'); |
| 452 |
$dz.find('.merchant-dropzone__error p').text(errorMsg); |
| 453 |
$dz.removeClass('is-loading').addClass('is-error'); |
| 454 |
this.hideLoadingIndicator($('.merchant-module-page-setting-fields .restore-section')); |
| 455 |
setTimeout(function () { |
| 456 |
$dz.removeClass('is-error'); |
| 457 |
// Reset file input so same file can be re-selected. |
| 458 |
$dz.find('.merchant-backup-file').val(''); |
| 459 |
}, 3000); |
| 460 |
}, |
| 461 |
hideError: function hideError() { |
| 462 |
$('#merchant-restore-dropzone').removeClass('is-error'); |
| 463 |
}, |
| 464 |
showLoadingIndicator: function showLoadingIndicator(container) { |
| 465 |
container.find('.merchant-loading-spinner').addClass('show'); |
| 466 |
}, |
| 467 |
hideLoadingIndicator: function hideLoadingIndicator(container) { |
| 468 |
container.find('.merchant-loading-spinner').removeClass('show'); |
| 469 |
} |
| 470 |
}; |
| 471 |
moduleBackup.init(); |
| 472 |
|
| 473 |
// Add support for toggle field inside flexible content. |
| 474 |
var flexibleToggleField = { |
| 475 |
init: function init(field) { |
| 476 |
this.events(); |
| 477 |
}, |
| 478 |
events: function events() { |
| 479 |
$(document).on('click', '.merchant-flexible-content .merchant-toggle-switch .toggle-switch-label span', function () { |
| 480 |
var checkBox = $(this).closest('.merchant-toggle-switch').find('.toggle-switch-checkbox'); |
| 481 |
checkBox.prop('checked', !checkBox.prop('checked')); |
| 482 |
}).trigger('merchant.change'); |
| 483 |
} |
| 484 |
}; |
| 485 |
var dateTimePickerField = { |
| 486 |
init: function init() { |
| 487 |
this.initiate_datepicker(); |
| 488 |
this.events(); |
| 489 |
}, |
| 490 |
initiate_datepicker: function initiate_datepicker() { |
| 491 |
var elements = $('.merchant-module-page-setting-field .merchant-datetime-field'); |
| 492 |
if (elements.length === 0) { |
| 493 |
return; |
| 494 |
} |
| 495 |
elements.each(function (index) { |
| 496 |
var input = $(this).find('input'), |
| 497 |
options = { |
| 498 |
locale: JSON.parse(merchant_datepicker_locale), |
| 499 |
selectedDates: input.val() ? [new Date(input.val())] : [], |
| 500 |
onSelect: function onSelect(_ref) { |
| 501 |
var date = _ref.date, |
| 502 |
formattedDate = _ref.formattedDate, |
| 503 |
datepicker = _ref.datepicker; |
| 504 |
if (typeof formattedDate === "undefined") { |
| 505 |
// allow removing date |
| 506 |
// input.val(''); |
| 507 |
datepicker.$el.value = ''; |
| 508 |
} |
| 509 |
input.trigger('change.merchant'); |
| 510 |
input.trigger('change.merchant-datepicker', [formattedDate, input, options, index]); |
| 511 |
} |
| 512 |
}, |
| 513 |
fieldOptions = $(this).data('options'); |
| 514 |
// add buttons to fieldOptions |
| 515 |
fieldOptions.buttons = ['clear']; |
| 516 |
if (fieldOptions) { |
| 517 |
if (fieldOptions.minDate !== undefined && fieldOptions.minDate === 'today') { |
| 518 |
fieldOptions.minDate = new Date(); |
| 519 |
if (fieldOptions.timeZone !== undefined && fieldOptions.timeZone !== '') { |
| 520 |
fieldOptions.minDate = new Date(fieldOptions.minDate.toLocaleString('en-US', { |
| 521 |
timeZone: fieldOptions.timeZone |
| 522 |
})); |
| 523 |
} |
| 524 |
} |
| 525 |
options = Object.assign(options, fieldOptions); |
| 526 |
} |
| 527 |
var datepickerObj = new AirDatepicker(input.getPath(), options); |
| 528 |
input.attr('readonly', true); |
| 529 |
$(document).trigger('initiated.merchant-datepicker', [datepickerObj, input, options, index]); |
| 530 |
}); |
| 531 |
}, |
| 532 |
events: function events() { |
| 533 |
var self = this; |
| 534 |
$(document).on('merchant-flexible-content-added', function () { |
| 535 |
self.initiate_datepicker(); |
| 536 |
}); |
| 537 |
} |
| 538 |
}; |
| 539 |
dateTimePickerField.init(); |
| 540 |
|
| 541 |
// Sortable. |
| 542 |
var SortableField = { |
| 543 |
init: function init(field) { |
| 544 |
this.events(); |
| 545 |
}, |
| 546 |
events: function events() { |
| 547 |
var self = this; |
| 548 |
$('.merchant-sortable').each(function () { |
| 549 |
var field = $(this), |
| 550 |
input = field.find('.merchant-sortable-input'); |
| 551 |
|
| 552 |
// Init sortable. |
| 553 |
$(field.find('ul.merchant-sortable-list').first()).sortable({ |
| 554 |
// Update value when we stop sorting. |
| 555 |
update: function update() { |
| 556 |
input.val(self.sortableGetNewVal(field)).trigger('change.merchant'); |
| 557 |
} |
| 558 |
}).disableSelection().find('li').each(function () { |
| 559 |
// Enable/disable options when we click on the eye of Thundera. |
| 560 |
$(this).find('i.visibility').click(function () { |
| 561 |
$(this).toggleClass('dashicons-visibility-faint').parents('li:eq(0)').toggleClass('invisible'); |
| 562 |
}); |
| 563 |
}).click(function () { |
| 564 |
// Update value when click in the eye. |
| 565 |
if ($(event.target).hasClass('dashicons-visibility')) { |
| 566 |
input.val(self.sortableGetNewVal(field)).trigger('change.merchant'); |
| 567 |
} |
| 568 |
}); |
| 569 |
}); |
| 570 |
}, |
| 571 |
sortableGetNewVal: function sortableGetNewVal(field) { |
| 572 |
var items = $(field.find('li')); |
| 573 |
var newVal = []; |
| 574 |
_.each(items, function (item) { |
| 575 |
if (!$(item).hasClass('invisible')) { |
| 576 |
newVal.push($(item).data('value')); |
| 577 |
} |
| 578 |
}); |
| 579 |
return JSON.stringify(newVal); |
| 580 |
} |
| 581 |
}; |
| 582 |
|
| 583 |
// Initialize Sortable. |
| 584 |
SortableField.init(); |
| 585 |
flexibleToggleField.init(); |
| 586 |
|
| 587 |
// When adding/duplicating new item, refresh sorting |
| 588 |
$(document).on('merchant-flexible-content-added', function (e, $layout) { |
| 589 |
var $sortableWrapper = $layout.find('.merchant-sortable-repeater-control'); |
| 590 |
var $sortableElement = $sortableWrapper.find('.merchant-sortable-repeater.sortable'); |
| 591 |
SortableRepeaterField.makeFieldsSortable($sortableElement); |
| 592 |
}); |
| 593 |
|
| 594 |
// Sortable Repeater. |
| 595 |
var SortableRepeaterField = { |
| 596 |
init: function init(field) { |
| 597 |
var self = this; |
| 598 |
|
| 599 |
// Update the values for all our input fields and initialise the sortable repeater. |
| 600 |
$('.merchant-sortable-repeater-control').each(function () { |
| 601 |
// If there is an existing customizer value, populate our rows |
| 602 |
var defaultValuesArray = JSON.parse($(this).find('.merchant-sortable-repeater-input').val()); |
| 603 |
var numRepeaterItems = defaultValuesArray.length; |
| 604 |
if (numRepeaterItems > 0) { |
| 605 |
// Add the first item to our existing input field |
| 606 |
$(this).find('.repeater-input').val(defaultValuesArray[0]); |
| 607 |
|
| 608 |
// Create a new row for each new value |
| 609 |
if (numRepeaterItems > 1) { |
| 610 |
// var i; |
| 611 |
for (var i = 1; i < numRepeaterItems; ++i) { |
| 612 |
self.appendRow($(this), defaultValuesArray[i]); |
| 613 |
} |
| 614 |
} |
| 615 |
} |
| 616 |
|
| 617 |
// Todo: remove |
| 618 |
// Make our Repeater fields sortable. Doesn't work with flexible content |
| 619 |
// if (!$(this).hasClass('disable-sorting')) { |
| 620 |
// $(this).find('.merchant-sortable-repeater.sortable').sortable({ |
| 621 |
// update: function (event, ui) { |
| 622 |
// self.getAllInputs($(this).parent()); |
| 623 |
// } |
| 624 |
// }); |
| 625 |
// } |
| 626 |
}); |
| 627 |
|
| 628 |
// Events. |
| 629 |
this.events(); |
| 630 |
}, |
| 631 |
events: function events() { |
| 632 |
var self = this; |
| 633 |
|
| 634 |
// Remove item starting from its parent element |
| 635 |
$(document).on('click', '.merchant-sortable-repeater.sortable .customize-control-sortable-repeater-delete', function (event) { |
| 636 |
event.preventDefault(); |
| 637 |
$(this).parent().slideUp('fast', function () { |
| 638 |
var parentContainer = $(this).parent().parent(); |
| 639 |
$(this).remove(); |
| 640 |
self.getAllInputs(parentContainer); |
| 641 |
}); |
| 642 |
$(document).trigger('merchant-sortable-repeater-item-deleted'); |
| 643 |
}); |
| 644 |
|
| 645 |
// Add new item |
| 646 |
$(document).on('click', '.customize-control-sortable-repeater-add', function (event) { |
| 647 |
event.preventDefault(); |
| 648 |
self.appendRow($(this).parent()); |
| 649 |
self.getAllInputs($(this).parent()); |
| 650 |
}); |
| 651 |
|
| 652 |
// Refresh our hidden field if any fields change |
| 653 |
$(document).on('change', '.merchant-sortable-repeater.sortable', function () { |
| 654 |
self.getAllInputs($(this).parent()); |
| 655 |
}); |
| 656 |
$(document).on('focusout', '.merchant-sortable-repeater.sortable .repeater-input', function () { |
| 657 |
self.getAllInputs($(this).parent()); |
| 658 |
}); |
| 659 |
}, |
| 660 |
/** |
| 661 |
* Append a new row to our list of elements. |
| 662 |
* |
| 663 |
*/ |
| 664 |
appendRow: function appendRow($element) { |
| 665 |
var defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; |
| 666 |
var newRow = '<div class="repeater" style="display:none"><input type="text" value="' + defaultValue + '" class="repeater-input" /><span class="dashicons dashicons-menu"></span><a class="customize-control-sortable-repeater-delete" href="#"><span class="dashicons dashicons-no-alt"></span></a></div>'; |
| 667 |
$element.find('.sortable').append(newRow); |
| 668 |
var $newItem = $element.find('.sortable').find('.repeater:last'); |
| 669 |
$newItem.slideDown('slow', function () { |
| 670 |
$(this).find('input').focus(); |
| 671 |
}); |
| 672 |
|
| 673 |
// Make Repeater fields sortable; Putting here works better with flexible content |
| 674 |
this.makeFieldsSortable($element.find('.sortable')); |
| 675 |
$(document).trigger('merchant-sortable-repeater-item-added', [$newItem, $element.find('.sortable')]); |
| 676 |
}, |
| 677 |
makeFieldsSortable: function makeFieldsSortable($sortableElement) { |
| 678 |
if (!$sortableElement.hasClass('disable-sorting')) { |
| 679 |
$sortableElement.sortable({ |
| 680 |
update: function update(event, ui) { |
| 681 |
SortableRepeaterField.getAllInputs($sortableElement.parent()); |
| 682 |
} |
| 683 |
}); |
| 684 |
} |
| 685 |
}, |
| 686 |
/** |
| 687 |
* Get the values from the repeater input fields and add to our hidden field. |
| 688 |
* |
| 689 |
*/ |
| 690 |
getAllInputs: function getAllInputs($element) { |
| 691 |
var inputValues = $element.find('.repeater-input').map(function () { |
| 692 |
return $(this).val(); |
| 693 |
}).toArray(); |
| 694 |
|
| 695 |
// Keep one empty item if all deleted. |
| 696 |
if (!inputValues.length) { |
| 697 |
inputValues.push(''); |
| 698 |
} |
| 699 |
|
| 700 |
// Add all the values from our repeater fields to the hidden field (which is the one that actually gets saved) |
| 701 |
$element.find('.merchant-sortable-repeater-input').val(JSON.stringify(inputValues)); |
| 702 |
// Important! Make sure to trigger change event so Customizer knows it has to save the field |
| 703 |
$element.find('.merchant-sortable-repeater-input').trigger('change'); |
| 704 |
$element.find('.merchant-sortable-repeater-input').trigger('sortable.repeater.change'); |
| 705 |
} |
| 706 |
}; |
| 707 |
|
| 708 |
// Initialize Sortable Repeater. |
| 709 |
SortableRepeaterField.init(); |
| 710 |
|
| 711 |
// Sortable Repeater Icons. |
| 712 |
var SortableRepeaterIconsField = { |
| 713 |
CONTROL: '.merchant-sortable-repeater-icons-control', |
| 714 |
init: function init() { |
| 715 |
var self = this; |
| 716 |
$(self.CONTROL).each(function () { |
| 717 |
self.populate($(this)); |
| 718 |
}); |
| 719 |
this.events(); |
| 720 |
}, |
| 721 |
/** |
| 722 |
* Populate rows from the hidden JSON input. |
| 723 |
*/ |
| 724 |
populate: function populate($control) { |
| 725 |
var self = this; |
| 726 |
var items = []; |
| 727 |
try { |
| 728 |
items = JSON.parse($control.find('.merchant-sortable-repeater-input').val()); |
| 729 |
} catch (e) { |
| 730 |
items = []; |
| 731 |
} |
| 732 |
if (!Array.isArray(items) || items.length === 0) { |
| 733 |
return; |
| 734 |
} |
| 735 |
var $firstRow = $control.find('.repeater').first(); |
| 736 |
|
| 737 |
// First item populates the existing row. |
| 738 |
var firstItem = self.normalizeItem(items[0]); |
| 739 |
$firstRow.find('.repeater-input').val(firstItem.text); |
| 740 |
self.setRowIcon($firstRow, firstItem.icon); |
| 741 |
|
| 742 |
// Remaining items get new rows (silent = true to avoid triggering save bar). |
| 743 |
for (var i = 1; i < items.length; i++) { |
| 744 |
var item = self.normalizeItem(items[i]); |
| 745 |
self.appendRow($control, item.text, item.icon, true); |
| 746 |
} |
| 747 |
}, |
| 748 |
events: function events() { |
| 749 |
var self = this; |
| 750 |
|
| 751 |
// Toggle icon picker dropdown. |
| 752 |
$(document).on('click', self.CONTROL + ' .merchant-icon-picker-toggle', function (e) { |
| 753 |
e.preventDefault(); |
| 754 |
e.stopPropagation(); |
| 755 |
var $dropdown = $(this).siblings('.merchant-icon-picker-dropdown'); |
| 756 |
|
| 757 |
// Close all other dropdowns first. |
| 758 |
$(self.CONTROL + ' .merchant-icon-picker-dropdown').not($dropdown).hide(); |
| 759 |
$dropdown.toggle(); |
| 760 |
}); |
| 761 |
|
| 762 |
// Select icon from dropdown. |
| 763 |
$(document).on('click', self.CONTROL + ' .merchant-icon-option', function (e) { |
| 764 |
e.preventDefault(); |
| 765 |
e.stopPropagation(); |
| 766 |
var $repeater = $(this).closest('.repeater'); |
| 767 |
var iconKey = $(this).data('icon') || ''; |
| 768 |
self.setRowIcon($repeater, iconKey); |
| 769 |
$(this).closest('.merchant-icon-picker-dropdown').hide(); |
| 770 |
self.syncHiddenInput($(this).closest(self.CONTROL)); |
| 771 |
}); |
| 772 |
|
| 773 |
// Close dropdown on outside click. |
| 774 |
$(document).on('click', function () { |
| 775 |
$(SortableRepeaterIconsField.CONTROL + ' .merchant-icon-picker-dropdown').hide(); |
| 776 |
}); |
| 777 |
|
| 778 |
// Prevent dropdown clicks from closing. |
| 779 |
$(document).on('click', self.CONTROL + ' .merchant-icon-picker-dropdown', function (e) { |
| 780 |
e.stopPropagation(); |
| 781 |
}); |
| 782 |
|
| 783 |
// Delete row. |
| 784 |
$(document).on('click', self.CONTROL + ' .merchant-sortable-repeater-icons.sortable .customize-control-sortable-repeater-delete', function (e) { |
| 785 |
e.preventDefault(); |
| 786 |
var $control = $(this).closest(self.CONTROL); |
| 787 |
$(this).parent().slideUp('fast', function () { |
| 788 |
$(this).remove(); |
| 789 |
self.syncHiddenInput($control); |
| 790 |
}); |
| 791 |
$(document).trigger('merchant-sortable-repeater-item-deleted'); |
| 792 |
}); |
| 793 |
|
| 794 |
// Add new row. |
| 795 |
$(document).on('click', self.CONTROL + ' .customize-control-sortable-repeater-icons-add', function (e) { |
| 796 |
e.preventDefault(); |
| 797 |
var $control = $(this).closest(self.CONTROL); |
| 798 |
self.appendRow($control, '', ''); |
| 799 |
self.syncHiddenInput($control); |
| 800 |
}); |
| 801 |
|
| 802 |
// Text change → sync. |
| 803 |
$(document).on('focusout', self.CONTROL + ' .merchant-sortable-repeater-icons.sortable .repeater-input', function () { |
| 804 |
self.syncHiddenInput($(this).closest(self.CONTROL)); |
| 805 |
}); |
| 806 |
|
| 807 |
// Sort change → sync. |
| 808 |
$(document).on('change', self.CONTROL + ' .merchant-sortable-repeater-icons.sortable', function () { |
| 809 |
self.syncHiddenInput($(this).closest(self.CONTROL)); |
| 810 |
}); |
| 811 |
|
| 812 |
// Re-init on flexible content add. |
| 813 |
$(document).on('merchant-flexible-content-added', function (e, $layout) { |
| 814 |
var $control = $layout.find(self.CONTROL); |
| 815 |
if ($control.length) { |
| 816 |
self.populate($control); |
| 817 |
self.makeFieldsSortable($control.find('.sortable')); |
| 818 |
} |
| 819 |
}); |
| 820 |
}, |
| 821 |
/** |
| 822 |
* Append a new repeater row. |
| 823 |
*/ |
| 824 |
appendRow: function appendRow($control, text, icon, silent) { |
| 825 |
var self = this; |
| 826 |
var iconLibrary = self.getIconLibrary($control); |
| 827 |
var dropdownHtml = '<div class="merchant-icon-picker-dropdown" style="display:none;">'; |
| 828 |
dropdownHtml += '<button type="button" class="merchant-icon-option" data-icon="" title="Use campaign icon"><span class="dashicons dashicons-no-alt"></span></button>'; |
| 829 |
Object.keys(iconLibrary).forEach(function (key) { |
| 830 |
dropdownHtml += '<button type="button" class="merchant-icon-option" data-icon="' + key + '" title="' + key + '">' + iconLibrary[key] + '</button>'; |
| 831 |
}); |
| 832 |
dropdownHtml += '</div>'; |
| 833 |
var escapedText = $('<div>').text(text).html(); |
| 834 |
var newRow = $('<div class="repeater" style="display:none">' + '<button type="button" class="merchant-icon-picker-toggle" data-icon="" title="Select icon"><span class="dashicons dashicons-plus-alt2"></span></button>' + dropdownHtml + '<input type="text" value="' + escapedText + '" class="repeater-input" />' + '<span class="dashicons dashicons-menu"></span>' + '<a class="customize-control-sortable-repeater-delete" href="#"><span class="dashicons dashicons-no-alt"></span></a>' + '</div>'); |
| 835 |
$control.find('.sortable').append(newRow); |
| 836 |
if (silent) { |
| 837 |
newRow.show(); |
| 838 |
} else { |
| 839 |
newRow.slideDown('slow', function () { |
| 840 |
$(this).find('input').focus(); |
| 841 |
}); |
| 842 |
} |
| 843 |
if (icon) { |
| 844 |
self.setRowIcon(newRow, icon); |
| 845 |
} |
| 846 |
self.makeFieldsSortable($control.find('.sortable')); |
| 847 |
$(document).trigger('merchant-sortable-repeater-item-added', [newRow, $control.find('.sortable')]); |
| 848 |
}, |
| 849 |
/** |
| 850 |
* Set the icon on a repeater row. |
| 851 |
*/ |
| 852 |
setRowIcon: function setRowIcon($row, iconKey) { |
| 853 |
var $toggle = $row.find('.merchant-icon-picker-toggle'); |
| 854 |
$toggle.attr('data-icon', iconKey); |
| 855 |
if (iconKey) { |
| 856 |
var $control = $row.closest(SortableRepeaterIconsField.CONTROL); |
| 857 |
var iconLibrary = this.getIconLibrary($control); |
| 858 |
var svg = iconLibrary[iconKey] || ''; |
| 859 |
if (svg) { |
| 860 |
$toggle.html(svg).addClass('has-icon'); |
| 861 |
} else { |
| 862 |
$toggle.html('<span class="dashicons dashicons-plus-alt2"></span>').removeClass('has-icon'); |
| 863 |
} |
| 864 |
} else { |
| 865 |
$toggle.html('<span class="dashicons dashicons-plus-alt2"></span>').removeClass('has-icon'); |
| 866 |
} |
| 867 |
|
| 868 |
// Mark selected option in dropdown. |
| 869 |
$row.find('.merchant-icon-option').removeClass('selected'); |
| 870 |
$row.find('.merchant-icon-option[data-icon="' + iconKey + '"]').addClass('selected'); |
| 871 |
}, |
| 872 |
/** |
| 873 |
* Sync all rows back to the hidden JSON input. |
| 874 |
*/ |
| 875 |
syncHiddenInput: function syncHiddenInput($control) { |
| 876 |
var items = []; |
| 877 |
$control.find('.merchant-sortable-repeater-icons .repeater').each(function () { |
| 878 |
var text = $(this).find('.repeater-input').val(); |
| 879 |
var icon = $(this).find('.merchant-icon-picker-toggle').attr('data-icon') || ''; |
| 880 |
items.push({ |
| 881 |
text: text, |
| 882 |
icon: icon |
| 883 |
}); |
| 884 |
}); |
| 885 |
if (!items.length) { |
| 886 |
items.push({ |
| 887 |
text: '', |
| 888 |
icon: '' |
| 889 |
}); |
| 890 |
} |
| 891 |
$control.find('.merchant-sortable-repeater-input').val(JSON.stringify(items)); |
| 892 |
$control.find('.merchant-sortable-repeater-input').trigger('change'); |
| 893 |
$control.find('.merchant-sortable-repeater-input').trigger('sortable.repeater.change'); |
| 894 |
}, |
| 895 |
makeFieldsSortable: function makeFieldsSortable($sortable) { |
| 896 |
var self = this; |
| 897 |
if (!$sortable.hasClass('disable-sorting')) { |
| 898 |
$sortable.sortable({ |
| 899 |
handle: '.dashicons-menu', |
| 900 |
update: function update() { |
| 901 |
self.syncHiddenInput($sortable.closest(self.CONTROL)); |
| 902 |
} |
| 903 |
}); |
| 904 |
} |
| 905 |
}, |
| 906 |
getIconLibrary: function getIconLibrary($control) { |
| 907 |
if (!$control.data('_iconLibrary')) { |
| 908 |
try { |
| 909 |
$control.data('_iconLibrary', JSON.parse($control.attr('data-icons')) || {}); |
| 910 |
} catch (e) { |
| 911 |
$control.data('_iconLibrary', {}); |
| 912 |
} |
| 913 |
} |
| 914 |
return $control.data('_iconLibrary'); |
| 915 |
}, |
| 916 |
normalizeItem: function normalizeItem(item) { |
| 917 |
if (typeof item === 'string') { |
| 918 |
return { |
| 919 |
text: item, |
| 920 |
icon: '' |
| 921 |
}; |
| 922 |
} |
| 923 |
return { |
| 924 |
text: item.text || '', |
| 925 |
icon: item.icon || '' |
| 926 |
}; |
| 927 |
} |
| 928 |
}; |
| 929 |
|
| 930 |
// Initialize Sortable Repeater Icons. |
| 931 |
SortableRepeaterIconsField.init(); |
| 932 |
var GroubField = { |
| 933 |
init: function init() { |
| 934 |
var self = this; |
| 935 |
self.initAccordion(); |
| 936 |
self.initFlag(); |
| 937 |
}, |
| 938 |
initAccordion: function initAccordion() { |
| 939 |
var self = this; |
| 940 |
$('.merchant-group-field.has-accordion').each(function () { |
| 941 |
var element = $(this); |
| 942 |
element.accordion({ |
| 943 |
collapsible: true, |
| 944 |
header: "> .title-area", |
| 945 |
heightStyle: "content", |
| 946 |
active: element.hasClass('open') ? 0 : false |
| 947 |
}); |
| 948 |
}); |
| 949 |
}, |
| 950 |
initFlag: function initFlag() { |
| 951 |
$('.merchant-group-field.has-flag').each(function () { |
| 952 |
var element = $(this); |
| 953 |
var field_id = element.data('id'); |
| 954 |
var status_field = element.find(".merchant-field-".concat(field_id, "_status select")); |
| 955 |
var selected_value = status_field.val(); |
| 956 |
var selected_label = status_field.find('option:selected').text(); |
| 957 |
var status_element = element.find('.field-status'); |
| 958 |
status_element.removeClass('hidden active inactive').text(selected_label).addClass(selected_value); |
| 959 |
}); |
| 960 |
} |
| 961 |
}; |
| 962 |
var ReviewsSelector = { |
| 963 |
accordion: null, |
| 964 |
activePopupContainer: null, |
| 965 |
// Initialize the Reviews Selector |
| 966 |
init: function init() { |
| 967 |
var self = this; |
| 968 |
self.initAccordion(); |
| 969 |
self.events(); |
| 970 |
// Skip the flexible content hidden elements that will be cloned when initialize the sortable reviews functionality |
| 971 |
self.makeSelectedReviewsSortable($('.merchant-reviews-selector').not('.layouts .merchant-reviews-selector')); |
| 972 |
}, |
| 973 |
// Set up event listeners |
| 974 |
events: function events() { |
| 975 |
var self = this; |
| 976 |
$(document).on('input', '.merchant-reviews-selector .products-search', self.ajaxSearch.bind(self)); |
| 977 |
$(document).on('change', '.merchant-reviews-selector .product-review input[type="checkbox"]', self.toggleReview.bind(self)); |
| 978 |
$(document).on('click', '.merchant-reviews-selector .review-photo img', self.requestReviewImages.bind(self)); |
| 979 |
$(document).on('click', '.review-photos-popup .overlay', self.dismissImagesGallery.bind(self)); |
| 980 |
$(document).on('click', '.merchant-reviews-selector .product-reviews-load-more button', self.loadMoreReviews.bind(self)); |
| 981 |
$(document).on('click', '.merchant-reviews-selector .popup-trigger', self.initPopUp.bind(self)); |
| 982 |
$(document).on('click', '.merchant-reviews-selector .popup-header .close, .merchant-reviews-selector > .overlay', self.dismissPopUp.bind(self)); |
| 983 |
$(document).on('click', '.merchant-reviews-selector .product-review-delete', self.deleteSelectedReview.bind(self)); |
| 984 |
// Listen to escape key |
| 985 |
$(document).on('keyup', function (e) { |
| 986 |
if (e.key === "Escape") { |
| 987 |
var isGallery = self.dismissImagesGallery(); |
| 988 |
if (isGallery) { |
| 989 |
return; |
| 990 |
} |
| 991 |
self.dismissPopUp(self); |
| 992 |
} |
| 993 |
}); |
| 994 |
$(document).on('merchant-flexible-content-added', function (e, layout) { |
| 995 |
self.makeSelectedReviewsSortable(); |
| 996 |
self.initAccordion(); |
| 997 |
}); |
| 998 |
}, |
| 999 |
// Make selected reviews sortable |
| 1000 |
makeSelectedReviewsSortable: function makeSelectedReviewsSortable() { |
| 1001 |
var container = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : $(document).find('.merchant-reviews-selector').not('.layouts .merchant-reviews-selector'); |
| 1002 |
var self = this; |
| 1003 |
var selectedReviews = container.find('.selected-reviews .product-reviews'); |
| 1004 |
selectedReviews.sortable({ |
| 1005 |
axis: 'y', |
| 1006 |
cursor: 'move', |
| 1007 |
helper: 'original', |
| 1008 |
handle: '.product-review-move', |
| 1009 |
cancel: '', |
| 1010 |
placeholder: 'placeholder', |
| 1011 |
// Add a class for styling |
| 1012 |
update: function update(event, ui) { |
| 1013 |
var container = ui.item.closest('.merchant-reviews-selector'); |
| 1014 |
self.saveModifications(container); |
| 1015 |
}, |
| 1016 |
start: function start(event, ui) { |
| 1017 |
ui.placeholder.height(ui.item.height()); // Match height |
| 1018 |
} |
| 1019 |
}); |
| 1020 |
// $('.product-reviews').sortable('refresh'); |
| 1021 |
// console.log('selectedReviews',selectedReviews.sortable('instance')) |
| 1022 |
}, |
| 1023 |
// Delete selected review |
| 1024 |
deleteSelectedReview: function deleteSelectedReview(e) { |
| 1025 |
var self = this; |
| 1026 |
var review = $(e.target).closest('.product-review'); |
| 1027 |
var container = review.closest('.merchant-reviews-selector'); |
| 1028 |
review.remove(); |
| 1029 |
self.saveModifications(container); |
| 1030 |
self.updateSelectedReviewsCheckboxState(container); |
| 1031 |
self.countSelectedReviewsInProduct(container); |
| 1032 |
}, |
| 1033 |
// Handle AJAX search for reviews |
| 1034 |
ajaxSearch: function ajaxSearch(e) { |
| 1035 |
var self = this; |
| 1036 |
var searchField = $(e.target); |
| 1037 |
var container = searchField.closest('.merchant-reviews-selector'); |
| 1038 |
var header = container.find('.popup-header'); |
| 1039 |
|
| 1040 |
// check value length > 3 |
| 1041 |
if (searchField.val().length === 0 || searchField.val().length >= 3) { |
| 1042 |
$.ajax({ |
| 1043 |
url: merchant_admin_options.ajaxurl, |
| 1044 |
type: 'POST', |
| 1045 |
data: { |
| 1046 |
action: 'merchant_search_reviews', |
| 1047 |
search: searchField.val(), |
| 1048 |
nonce: merchant_admin_options.ajaxnonce |
| 1049 |
}, |
| 1050 |
beforeSend: function beforeSend() { |
| 1051 |
header.addClass('loading'); |
| 1052 |
self.destroyAccordion(container); |
| 1053 |
}, |
| 1054 |
success: function success(response) { |
| 1055 |
if (response.success) { |
| 1056 |
header.removeClass('popup-error'); |
| 1057 |
container.find('.products-search-results').html(response.data); |
| 1058 |
self.initAccordion(container); |
| 1059 |
} else { |
| 1060 |
header.addClass('popup-error'); |
| 1061 |
} |
| 1062 |
}, |
| 1063 |
complete: function complete() { |
| 1064 |
header.removeClass('loading'); |
| 1065 |
}, |
| 1066 |
error: function error() { |
| 1067 |
header.addClass('popup-error'); |
| 1068 |
} |
| 1069 |
}); |
| 1070 |
} |
| 1071 |
}, |
| 1072 |
// Load more reviews |
| 1073 |
loadMoreReviews: function loadMoreReviews(e) { |
| 1074 |
var self = this; |
| 1075 |
var product = $(e.target).closest('.product-item'); |
| 1076 |
var offset = product.find('.product-review').length; |
| 1077 |
$.ajax({ |
| 1078 |
url: merchant_admin_options.ajaxurl, |
| 1079 |
type: 'POST', |
| 1080 |
data: { |
| 1081 |
action: 'merchant_load_more_reviews', |
| 1082 |
product_id: product.attr('data-id'), |
| 1083 |
offset: offset, |
| 1084 |
nonce: merchant_admin_options.ajaxnonce |
| 1085 |
}, |
| 1086 |
beforeSend: function beforeSend() { |
| 1087 |
product.addClass('loading'); |
| 1088 |
}, |
| 1089 |
success: function success(response) { |
| 1090 |
if (response.success) { |
| 1091 |
product.find('.product-reviews .reviews-wrapper').append(response.data.reviews); |
| 1092 |
if (response.data.load_more === '') { |
| 1093 |
self.hideLoadMoreButton(product); |
| 1094 |
} |
| 1095 |
self.updateSelectedReviewsCheckboxState(product.closest('.merchant-reviews-selector')); |
| 1096 |
self.countSelectedReviewsInProduct(product.closest('.merchant-reviews-selector')); |
| 1097 |
} |
| 1098 |
}, |
| 1099 |
complete: function complete() { |
| 1100 |
product.removeClass('loading'); |
| 1101 |
} |
| 1102 |
}); |
| 1103 |
}, |
| 1104 |
// Hide load more button |
| 1105 |
hideLoadMoreButton: function hideLoadMoreButton(product) { |
| 1106 |
product.find('.product-reviews-load-more').remove(); |
| 1107 |
}, |
| 1108 |
// Initialize the popup |
| 1109 |
initPopUp: function initPopUp(e) { |
| 1110 |
var self = this; |
| 1111 |
var container = $(e.target).closest('.merchant-reviews-selector'); |
| 1112 |
var popup = container.find('.selector-popup'); |
| 1113 |
var overlay = container.find('.overlay'); |
| 1114 |
popup.addClass('active'); |
| 1115 |
overlay.addClass('active'); |
| 1116 |
self.activePopupContainer = container; |
| 1117 |
}, |
| 1118 |
// Dismiss the popup |
| 1119 |
dismissPopUp: function dismissPopUp() { |
| 1120 |
var self = this; |
| 1121 |
var container = self.activePopupContainer; |
| 1122 |
if (container === null) { |
| 1123 |
return; |
| 1124 |
} |
| 1125 |
var popup = container.find('.selector-popup'); |
| 1126 |
var overlay = container.find('.overlay'); |
| 1127 |
popup.removeClass('active'); |
| 1128 |
overlay.removeClass('active'); |
| 1129 |
self.activePopupContainer = null; |
| 1130 |
}, |
| 1131 |
// Initialize the accordion |
| 1132 |
initAccordion: function initAccordion() { |
| 1133 |
var container = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : $(document).find('.merchant-reviews-selector'); |
| 1134 |
var self = this; |
| 1135 |
container.each(function () { |
| 1136 |
var elements = $(this).find('.popup-content .product-item.product-item-has-reviews'); |
| 1137 |
elements.each(function () { |
| 1138 |
elements.accordion({ |
| 1139 |
collapsible: true, |
| 1140 |
heightStyle: "content", |
| 1141 |
icons: false, |
| 1142 |
// Disable icons |
| 1143 |
active: elements.hasClass('opened') ? 0 : false, |
| 1144 |
activate: function activate(event, ui) { |
| 1145 |
// Check if a new panel is being activated |
| 1146 |
if (ui.newPanel.length) { |
| 1147 |
// Add 'opened' class to the parent product item |
| 1148 |
ui.newPanel.parent().addClass('opened'); |
| 1149 |
} else { |
| 1150 |
// Remove 'opened' class from all product items |
| 1151 |
elements.removeClass('opened'); |
| 1152 |
} |
| 1153 |
} |
| 1154 |
}); |
| 1155 |
}); |
| 1156 |
elements.find('a').on('click', function (e) { |
| 1157 |
e.stopPropagation(); // Prevent event from bubbling up to the accordion header |
| 1158 |
}); |
| 1159 |
self.updateSelectedReviewsCheckboxState($(this)); |
| 1160 |
self.countSelectedReviewsInProduct($(this)); |
| 1161 |
}); |
| 1162 |
}, |
| 1163 |
// Destroy the accordion |
| 1164 |
destroyAccordion: function destroyAccordion(container) { |
| 1165 |
var elements = container.find('.selector-popup .product-item.product-item-has-reviews'); |
| 1166 |
elements.each(function () { |
| 1167 |
if ($(this).hasClass('ui-accordion')) { |
| 1168 |
// Check if it has the accordion class |
| 1169 |
$(this).accordion('destroy'); |
| 1170 |
} |
| 1171 |
}); |
| 1172 |
}, |
| 1173 |
// Toggle review selection |
| 1174 |
toggleReview: function toggleReview(e) { |
| 1175 |
var self = this; |
| 1176 |
var checked = $(e.target).prop('checked'); |
| 1177 |
var reviewsFieldContainer = $(e.target).closest('.merchant-reviews-selector'); |
| 1178 |
var review = $(e.target).closest('.product-review'); |
| 1179 |
var reviewId = review.attr('data-id'); |
| 1180 |
var selectedReviews = reviewsFieldContainer.find('.selected-reviews .product-reviews'); |
| 1181 |
var selectedReview = selectedReviews.find(".product-review[data-id=\"".concat(reviewId, "\"]")); |
| 1182 |
if (checked) { |
| 1183 |
if (selectedReview.length === 0) { |
| 1184 |
var newReview = review.clone(); |
| 1185 |
selectedReviews.append(newReview); |
| 1186 |
self.makeSelectedReviewsSortable(); |
| 1187 |
} |
| 1188 |
} else { |
| 1189 |
selectedReview.remove(); |
| 1190 |
} |
| 1191 |
setTimeout(function () { |
| 1192 |
self.saveModifications(reviewsFieldContainer); |
| 1193 |
}, 150); |
| 1194 |
}, |
| 1195 |
// Save modifications to the selected reviews |
| 1196 |
saveModifications: function saveModifications(container) { |
| 1197 |
var self = this; |
| 1198 |
var reviewsSavedIdsField = container.find('.review-saved-ids'); |
| 1199 |
var selectedReviews = container.find('.selected-reviews .product-reviews'); |
| 1200 |
var reviews = selectedReviews.find('.product-review'); |
| 1201 |
var reviewsIds = []; |
| 1202 |
reviews.each(function () { |
| 1203 |
var id = $(this).attr('data-id'); |
| 1204 |
reviewsIds.push(id); |
| 1205 |
}); |
| 1206 |
reviewsSavedIdsField.val(reviewsIds.join(',')); |
| 1207 |
reviewsSavedIdsField.trigger('change'); |
| 1208 |
// reviewsSavedIds; |
| 1209 |
self.updateSelectedReviewsCheckboxState(container); |
| 1210 |
self.countSelectedReviewsInProduct(container); |
| 1211 |
}, |
| 1212 |
// Mark selected reviews checkbox |
| 1213 |
updateSelectedReviewsCheckboxState: function updateSelectedReviewsCheckboxState(container) { |
| 1214 |
var popupContent = container.find('.selector-popup'); |
| 1215 |
var reviewsSavedIds = container.find('.review-saved-ids'); |
| 1216 |
var reviews = popupContent.find('.product-review'); |
| 1217 |
var reviewsIds = reviewsSavedIds.val().split(','); |
| 1218 |
reviews.each(function () { |
| 1219 |
var reviewId = $(this).attr('data-id'); |
| 1220 |
var checkbox = container.find(".product-review[data-id=\"".concat(reviewId, "\"] input[type=\"checkbox\"]")); |
| 1221 |
if (reviewsIds.includes(reviewId)) { |
| 1222 |
checkbox.prop('checked', true); |
| 1223 |
} else { |
| 1224 |
checkbox.prop('checked', false); |
| 1225 |
} |
| 1226 |
}); |
| 1227 |
}, |
| 1228 |
// Count selected reviews in product |
| 1229 |
countSelectedReviewsInProduct: function countSelectedReviewsInProduct(container) { |
| 1230 |
var self = this; |
| 1231 |
var products = container.find('.selector-popup .product-item'); |
| 1232 |
var reviewsSavedIds = container.find('.review-saved-ids'); |
| 1233 |
var reviewsIds = reviewsSavedIds.val().split(','); |
| 1234 |
products.each(function () { |
| 1235 |
var selectedReviews = 0; |
| 1236 |
var product = $(this); |
| 1237 |
var productReviews = product.find('.product-review'); |
| 1238 |
productReviews.each(function () { |
| 1239 |
var review = $(this); |
| 1240 |
var reviewId = review.attr('data-id'); |
| 1241 |
if (reviewsIds.includes(reviewId)) { |
| 1242 |
selectedReviews++; |
| 1243 |
} |
| 1244 |
}); |
| 1245 |
product.find('.selected-reviews-count .counter').text(selectedReviews); |
| 1246 |
}); |
| 1247 |
}, |
| 1248 |
// Request review images |
| 1249 |
requestReviewImages: function requestReviewImages(e) { |
| 1250 |
var reviewContainer = $(e.target).closest('.product-review'); |
| 1251 |
var image = $(e.target); |
| 1252 |
var reviewId = reviewContainer.attr('data-id'); |
| 1253 |
$.ajax({ |
| 1254 |
url: merchant_admin_options.ajaxurl, |
| 1255 |
type: 'POST', |
| 1256 |
data: { |
| 1257 |
action: 'merchant_get_review_images', |
| 1258 |
review_id: reviewId, |
| 1259 |
nonce: merchant_admin_options.ajaxnonce |
| 1260 |
}, |
| 1261 |
beforeSend: function beforeSend() { |
| 1262 |
// Show loading spinner |
| 1263 |
image.addClass('loading'); |
| 1264 |
}, |
| 1265 |
success: function success(response) { |
| 1266 |
if (response.success) { |
| 1267 |
$('body').append(response.data); |
| 1268 |
setTimeout(function () { |
| 1269 |
$('body').find('.review-photos-popup').addClass('active'); |
| 1270 |
}, 300); |
| 1271 |
} |
| 1272 |
}, |
| 1273 |
complete: function complete() { |
| 1274 |
// Hide loading spinner |
| 1275 |
image.removeClass('loading'); |
| 1276 |
} |
| 1277 |
}); |
| 1278 |
}, |
| 1279 |
// Dismiss the images gallery |
| 1280 |
dismissImagesGallery: function dismissImagesGallery() { |
| 1281 |
var gallery = $('body').find('.review-photos-popup'); |
| 1282 |
if (gallery.length) { |
| 1283 |
gallery.removeClass('active'); |
| 1284 |
setTimeout(function () { |
| 1285 |
gallery.remove(); |
| 1286 |
}, 300); |
| 1287 |
return true; |
| 1288 |
} |
| 1289 |
return false; |
| 1290 |
} |
| 1291 |
}; |
| 1292 |
|
| 1293 |
// Flexible Content. |
| 1294 |
var FlexibleContentField = { |
| 1295 |
init: function init(field) { |
| 1296 |
var self = this; |
| 1297 |
|
| 1298 |
// Update the values for all our input fields and initialise the sortable repeater. |
| 1299 |
$('.merchant-flexible-content-control').each(function () { |
| 1300 |
var hasAccordion = $(this).hasClass('has-accordion'), |
| 1301 |
$content = $(this).find('.merchant-flexible-content'); |
| 1302 |
var campaignId = params.get('campaign_id'); |
| 1303 |
var campaignIndex = 0; |
| 1304 |
|
| 1305 |
// Find the campaignIndex based on campaignId |
| 1306 |
if (campaignId) { |
| 1307 |
$content.find('input.flexible-id').each(function (index) { |
| 1308 |
if ($(this).val() === campaignId) { |
| 1309 |
campaignIndex = index; |
| 1310 |
return false; |
| 1311 |
} |
| 1312 |
}); |
| 1313 |
} |
| 1314 |
if (hasAccordion) { |
| 1315 |
$content.accordion({ |
| 1316 |
active: campaignIndex, |
| 1317 |
// Open the campaign with campaign index |
| 1318 |
collapsible: true, |
| 1319 |
//header: "> div > .layout-header", |
| 1320 |
header: function header(elem) { |
| 1321 |
return elem.find('.layout__inner > .layout-header'); |
| 1322 |
}, |
| 1323 |
heightStyle: "content", |
| 1324 |
beforeActivate: function beforeActivate(event, ui) { |
| 1325 |
// Prevent accordion toggle when clicking the status badge. |
| 1326 |
if (event.originalEvent && $(event.originalEvent.target).hasClass('layout-status')) { |
| 1327 |
event.preventDefault(); |
| 1328 |
return; |
| 1329 |
} |
| 1330 |
|
| 1331 |
// Hydrate deferred layout before the accordion animation |
| 1332 |
// so the panel opens with content already rendered. |
| 1333 |
if (ui.newPanel.length) { |
| 1334 |
var $layout = ui.newPanel.closest('.layout'); |
| 1335 |
if ($layout.attr('data-deferred') === '1') { |
| 1336 |
self.hydrateLayout($layout); |
| 1337 |
} |
| 1338 |
} |
| 1339 |
} |
| 1340 |
}).sortable({ |
| 1341 |
axis: 'y', |
| 1342 |
cursor: 'move', |
| 1343 |
helper: 'original', |
| 1344 |
handle: '.customize-control-flexible-content-move', |
| 1345 |
stop: function stop(event, ui) { |
| 1346 |
$content.trigger('merchant.sorted'); |
| 1347 |
self.refreshNumbers($content); |
| 1348 |
$content.accordion("refresh"); |
| 1349 |
} |
| 1350 |
}); |
| 1351 |
} else { |
| 1352 |
$content.sortable({ |
| 1353 |
axis: 'y', |
| 1354 |
cursor: 'move', |
| 1355 |
helper: 'original', |
| 1356 |
handle: '.customize-control-flexible-content-move', |
| 1357 |
stop: function stop(event, ui) { |
| 1358 |
$content.trigger('merchant.sorted'); |
| 1359 |
self.refreshNumbers($content); |
| 1360 |
$content.accordion("refresh"); |
| 1361 |
} |
| 1362 |
}); |
| 1363 |
} |
| 1364 |
}); |
| 1365 |
this.updateLayoutTitle(); |
| 1366 |
this.updateLayoutStatus(); |
| 1367 |
this.updateDiscountPercentMaxVal(); |
| 1368 |
// Events. |
| 1369 |
this.events(); |
| 1370 |
}, |
| 1371 |
updateLayoutTitle: function updateLayoutTitle() { |
| 1372 |
// Update the title for all layout header. |
| 1373 |
$('.merchant-flexible-content .layout').each(function () { |
| 1374 |
var title = $(this).find('.layout-title[data-title-field]'); |
| 1375 |
if (title.length) { |
| 1376 |
var input = $(this).find('.layout-body .merchant-field-' + title.data('title-field') + ' input'); |
| 1377 |
input.on('change keyup', function () { |
| 1378 |
title.text($(this).val()); |
| 1379 |
}); |
| 1380 |
title.text(input.val()); |
| 1381 |
} |
| 1382 |
}); |
| 1383 |
}, |
| 1384 |
/** |
| 1385 |
* Resolve a status field inside a layout, returning a unified adapter |
| 1386 |
* that works with select, radio, and switcher field types. |
| 1387 |
* |
| 1388 |
* @param {jQuery} $layout The .layout element. |
| 1389 |
* @param {string} fieldId The field identifier (e.g. 'campaign_status'). |
| 1390 |
* @returns {object|null} Adapter with getValue, getLabel, getOptions, setNext, onChange — or null. |
| 1391 |
*/ |
| 1392 |
resolveStatusField: function resolveStatusField($layout, fieldId) { |
| 1393 |
var $wrapper = $layout.find('.layout-body .merchant-field-' + fieldId); |
| 1394 |
if (!$wrapper.length) return null; |
| 1395 |
var $select = $wrapper.find('select'); |
| 1396 |
var $radios = $wrapper.find('input[type="radio"]'); |
| 1397 |
var $checkbox = $wrapper.find('input[type="checkbox"]'); |
| 1398 |
|
| 1399 |
// Select field adapter. |
| 1400 |
if ($select.length) { |
| 1401 |
return { |
| 1402 |
getValue: function getValue() { |
| 1403 |
return $select.val() || 'active'; |
| 1404 |
}, |
| 1405 |
getLabel: function getLabel() { |
| 1406 |
return $select.find('option:selected').text() || this.getValue(); |
| 1407 |
}, |
| 1408 |
getOptions: function getOptions() { |
| 1409 |
return $select.find('option').map(function () { |
| 1410 |
return $(this).val(); |
| 1411 |
}).get(); |
| 1412 |
}, |
| 1413 |
setNext: function setNext() { |
| 1414 |
var opts = $select.find('option'); |
| 1415 |
var idx = opts.index(opts.filter(':selected')); |
| 1416 |
var next = (idx + 1) % opts.length; |
| 1417 |
$select.val(opts.eq(next).val()).trigger('change'); |
| 1418 |
}, |
| 1419 |
onChange: function onChange(fn) { |
| 1420 |
$select.off('change.statusBadge').on('change.statusBadge', fn); |
| 1421 |
} |
| 1422 |
}; |
| 1423 |
} |
| 1424 |
|
| 1425 |
// Radio field adapter. |
| 1426 |
if ($radios.length) { |
| 1427 |
return { |
| 1428 |
getValue: function getValue() { |
| 1429 |
return $radios.filter(':checked').val() || 'active'; |
| 1430 |
}, |
| 1431 |
getLabel: function getLabel() { |
| 1432 |
var $checked = $radios.filter(':checked'); |
| 1433 |
var $label = $wrapper.find('label[for="' + $checked.attr('id') + '"]'); |
| 1434 |
return $label.length ? $label.text() : this.getValue(); |
| 1435 |
}, |
| 1436 |
getOptions: function getOptions() { |
| 1437 |
return $radios.map(function () { |
| 1438 |
return $(this).val(); |
| 1439 |
}).get(); |
| 1440 |
}, |
| 1441 |
setNext: function setNext() { |
| 1442 |
var vals = this.getOptions(); |
| 1443 |
var idx = vals.indexOf(this.getValue()); |
| 1444 |
var next = (idx + 1) % vals.length; |
| 1445 |
$radios.filter('[value="' + vals[next] + '"]').prop('checked', true).trigger('change'); |
| 1446 |
}, |
| 1447 |
onChange: function onChange(fn) { |
| 1448 |
$radios.off('change.statusBadge').on('change.statusBadge', fn); |
| 1449 |
} |
| 1450 |
}; |
| 1451 |
} |
| 1452 |
|
| 1453 |
// Switcher (checkbox) field adapter — on/off. |
| 1454 |
if ($checkbox.length) { |
| 1455 |
return { |
| 1456 |
getValue: function getValue() { |
| 1457 |
return $checkbox.is(':checked') ? 'active' : 'inactive'; |
| 1458 |
}, |
| 1459 |
getLabel: function getLabel() { |
| 1460 |
return $checkbox.is(':checked') ? 'Active' : 'Inactive'; |
| 1461 |
}, |
| 1462 |
getOptions: function getOptions() { |
| 1463 |
return ['active', 'inactive']; |
| 1464 |
}, |
| 1465 |
setNext: function setNext() { |
| 1466 |
$checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change'); |
| 1467 |
}, |
| 1468 |
onChange: function onChange(fn) { |
| 1469 |
$checkbox.off('change.statusBadge').on('change.statusBadge', fn); |
| 1470 |
} |
| 1471 |
}; |
| 1472 |
} |
| 1473 |
return null; |
| 1474 |
}, |
| 1475 |
updateLayoutStatus: function updateLayoutStatus() { |
| 1476 |
var self = this; |
| 1477 |
|
| 1478 |
// Sync status badge on each layout header with the field value. |
| 1479 |
$('.merchant-flexible-content .layout').each(function () { |
| 1480 |
var $badge = $(this).find('.layout-header .layout-status[data-status-field]'); |
| 1481 |
if (!$badge.length) return; |
| 1482 |
var fieldId = $badge.data('status-field'); |
| 1483 |
var adapter = self.resolveStatusField($(this), fieldId); |
| 1484 |
if (!adapter) return; |
| 1485 |
function syncBadge() { |
| 1486 |
var val = adapter.getValue(); |
| 1487 |
var label = adapter.getLabel(); |
| 1488 |
$badge.removeClass(function (i, cls) { |
| 1489 |
return (cls.match(/layout-status--\S+/g) || []).join(' '); |
| 1490 |
}).addClass('layout-status--' + val).text(label.trim()); |
| 1491 |
} |
| 1492 |
adapter.onChange(syncBadge); |
| 1493 |
syncBadge(); |
| 1494 |
}); |
| 1495 |
|
| 1496 |
// Delegated click handler for badge toggle — works on all rows including deferred. |
| 1497 |
$(document).off('click.statusToggle').on('click.statusToggle', '.layout-status[data-status-field]', function (e) { |
| 1498 |
e.stopPropagation(); |
| 1499 |
e.preventDefault(); |
| 1500 |
var $badge = $(this); |
| 1501 |
var fieldId = $badge.data('status-field'); |
| 1502 |
var $layout = $badge.closest('.layout'); |
| 1503 |
var adapter = self.resolveStatusField($layout, fieldId); |
| 1504 |
if (adapter) { |
| 1505 |
// Hydrated row: toggle via the field adapter. |
| 1506 |
adapter.setNext(); |
| 1507 |
} else { |
| 1508 |
// Deferred row: cycle through options stored in data-status-options, |
| 1509 |
// falling back to active/inactive. |
| 1510 |
var optionsAttr = $badge.attr('data-status-options'); |
| 1511 |
var options = optionsAttr ? optionsAttr.split(',') : ['active', 'inactive']; |
| 1512 |
var currentVal = $badge.attr('class').match(/layout-status--(\S+)/); |
| 1513 |
currentVal = currentVal ? currentVal[1] : options[0]; |
| 1514 |
var idx = options.indexOf(currentVal); |
| 1515 |
var nextIdx = (idx + 1) % options.length; |
| 1516 |
var newVal = options[nextIdx]; |
| 1517 |
var newLabel = newVal.charAt(0).toUpperCase() + newVal.slice(1); |
| 1518 |
$badge.removeClass('layout-status--' + currentVal).addClass('layout-status--' + newVal).text(newLabel); |
| 1519 |
|
| 1520 |
// Update data-fields-json so the value persists when hydrated. |
| 1521 |
var jsonAttr = $layout.attr('data-fields-json'); |
| 1522 |
if (jsonAttr) { |
| 1523 |
try { |
| 1524 |
var data = JSON.parse(jsonAttr); |
| 1525 |
if (!data.values) data.values = {}; |
| 1526 |
data.values[fieldId] = newVal; |
| 1527 |
$layout.attr('data-fields-json', JSON.stringify(data)); |
| 1528 |
} catch (ex) {/* ignore parse errors */} |
| 1529 |
} |
| 1530 |
|
| 1531 |
// Mark the form as changed. |
| 1532 |
$('.merchant-module-page-content').trigger('change.merchant'); |
| 1533 |
} |
| 1534 |
}); |
| 1535 |
}, |
| 1536 |
updateDiscountPercentMaxVal: function updateDiscountPercentMaxVal() { |
| 1537 |
$('.merchant-flexible-content .layout').each(function () { |
| 1538 |
var $layout = $(this); |
| 1539 |
var $discountType = $layout.find('.merchant-module-page-setting-field[data-id="discount_type"]'); |
| 1540 |
var $discountVal = $layout.find('.merchant-module-page-setting-field[data-id="discount_value"]'); |
| 1541 |
$discountVal = $discountVal.length ? $discountVal : $layout.find('.merchant-module-page-setting-field[data-id="discount"]'); |
| 1542 |
$discountVal = $discountVal.length ? $discountVal : $layout.find('.merchant-module-page-setting-field[data-id="discount_amount"]'); |
| 1543 |
var checkedValue = $discountType.find('input:checked').val(); |
| 1544 |
|
| 1545 |
// Set/Remove max value based on the discount type. |
| 1546 |
checkedValue === 'percentage_discount' || checkedValue === 'percentage' ? $discountVal.find('input').attr('max', 100) : $discountVal.find('input').removeAttr('max'); |
| 1547 |
}); |
| 1548 |
$('.merchant-module-page-setting-fields'); |
| 1549 |
}, |
| 1550 |
generateUUID: function generateUUID() { |
| 1551 |
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { |
| 1552 |
var r = Math.random() * 16 | 0; |
| 1553 |
var v = c === 'x' ? r : r & 0x3 | 0x8; |
| 1554 |
return v.toString(16); |
| 1555 |
}); |
| 1556 |
}, |
| 1557 |
events: function events() { |
| 1558 |
var self = this; |
| 1559 |
$(document).on('change', '.merchant-module-page-setting-field[data-id="discount_type"] input', function () { |
| 1560 |
self.updateDiscountPercentMaxVal(); |
| 1561 |
}); |
| 1562 |
|
| 1563 |
// Update "selected" attribute on select. It's Required for duplicating layouts. |
| 1564 |
$(document).on('change', 'select', function () { |
| 1565 |
var selectedValue = $(this).val(); |
| 1566 |
var isMultiple = $(this).prop('multiple'); |
| 1567 |
$(this).find('option').each(function () { |
| 1568 |
var currentValue = $(this).val(); |
| 1569 |
var isSelected = isMultiple ? selectedValue.includes(currentValue) : currentValue === selectedValue; |
| 1570 |
$(this).attr('selected', isSelected); |
| 1571 |
}); |
| 1572 |
}); |
| 1573 |
$('.customize-control-flexible-content-add-button').on('click', function (event) { |
| 1574 |
event.preventDefault(); |
| 1575 |
event.stopImmediatePropagation(); |
| 1576 |
if ($(this).parent().find('.customize-control-flexible-content-add-list a').length === 1) { |
| 1577 |
// If there is only one layout, trigger click on it. |
| 1578 |
$(this).parent().find('.customize-control-flexible-content-add-list a').trigger('click'); |
| 1579 |
return; |
| 1580 |
} |
| 1581 |
$(this).parent().find('.customize-control-flexible-content-add-list').toggleClass('active'); |
| 1582 |
}); |
| 1583 |
|
| 1584 |
// Add new item |
| 1585 |
$(document).on('click', '.customize-control-flexible-content-add', function (event) { |
| 1586 |
event.preventDefault(); |
| 1587 |
event.stopImmediatePropagation(); |
| 1588 |
var $field = $('.merchant-flexible-content-control[data-id=' + $(this).data('id') + ']'); |
| 1589 |
var $layouts = $field.find('.layouts'); |
| 1590 |
var $selected = $(this).data('layout'); |
| 1591 |
var $layout = $layouts.find('.layout[data-type=' + $selected + ']').clone(true); |
| 1592 |
var $content = $field.find('.merchant-flexible-content'); |
| 1593 |
var $items = $content.find('.layout'); |
| 1594 |
var uuid = self.generateUUID(); |
| 1595 |
$layout.find('input, select, textarea').each(function () { |
| 1596 |
if ($(this).data('name')) { |
| 1597 |
$(this).attr('name', $(this).data('name').replace('0', $items.length)); |
| 1598 |
} |
| 1599 |
if ($(this).is(':checkbox, :radio') && $(this).attr('checked')) { |
| 1600 |
$(this).prop('checked', true); |
| 1601 |
} |
| 1602 |
}); |
| 1603 |
$layout.attr('data-layout-id', uuid); |
| 1604 |
$layout.find('.layout-count').text($items.length + 1); |
| 1605 |
$layout.find('.flexible-id').val(uuid); |
| 1606 |
$content.append($layout); |
| 1607 |
$content.removeClass('empty'); |
| 1608 |
$(this).parent().removeClass('active'); |
| 1609 |
if ($layout.find('.merchant-module-page-setting-field-upload').length) { |
| 1610 |
initUploadField($layout.find('.merchant-module-page-setting-field-upload')); |
| 1611 |
} |
| 1612 |
if ($layout.find('.merchant-module-page-setting-field-select_ajax').length) { |
| 1613 |
initSelectAjax($layout.find('.merchant-module-page-setting-field-select_ajax')); |
| 1614 |
} |
| 1615 |
var parentDiv = $(this).closest('.merchant-flexible-content-control'), |
| 1616 |
hasAccordion = parentDiv.hasClass('has-accordion'); |
| 1617 |
if (hasAccordion) { |
| 1618 |
parentDiv.find('.merchant-flexible-content').accordion("refresh"); |
| 1619 |
// Expand the accordion last added |
| 1620 |
parentDiv.find('.merchant-flexible-content').accordion("option", "active", -1); |
| 1621 |
} |
| 1622 |
GroubField.init(); |
| 1623 |
$(document).trigger('merchant-flexible-content-added', [$layout]); |
| 1624 |
initMerchantRange(); |
| 1625 |
self.updateLayoutTitle(); |
| 1626 |
self.updateLayoutStatus(); |
| 1627 |
self.updateDiscountPercentMaxVal(); |
| 1628 |
$('.merchant-module-page-content').trigger('change.merchant'); |
| 1629 |
}); |
| 1630 |
|
| 1631 |
// Duplicate item |
| 1632 |
$(document).on('click', '.customize-control-flexible-content-duplicate', function (event) { |
| 1633 |
event.preventDefault(); |
| 1634 |
event.stopImmediatePropagation(); |
| 1635 |
var $duplicateBtn = $(this); |
| 1636 |
var $flexibleContentWrapper = $duplicateBtn.closest('.merchant-flexible-content-control[data-id=' + $duplicateBtn.data('id') + ']'); |
| 1637 |
var $flexibleContent = $flexibleContentWrapper === null || $flexibleContentWrapper === void 0 ? void 0 : $flexibleContentWrapper.find('.merchant-flexible-content'); |
| 1638 |
if (!$flexibleContentWrapper.length || !$flexibleContent.length) { |
| 1639 |
return; |
| 1640 |
} |
| 1641 |
var $sourceLayout = $duplicateBtn.closest('.layout'); |
| 1642 |
if (!$sourceLayout.length) { |
| 1643 |
return; |
| 1644 |
} |
| 1645 |
$sourceLayout.find('.layout-actions__inner').hide(); |
| 1646 |
|
| 1647 |
// Clone the layout without data & events. |
| 1648 |
var $clonedLayout = $sourceLayout.clone(); |
| 1649 |
var $items = $flexibleContent.find('.layout'); |
| 1650 |
var index = $sourceLayout.find('.layout-count').text(); |
| 1651 |
var uuid = self.generateUUID(); |
| 1652 |
$clonedLayout.attr('data-layout-id', uuid); |
| 1653 |
$clonedLayout.find('.flexible-id').val(uuid); |
| 1654 |
$clonedLayout.find('input, select, textarea').each(function () { |
| 1655 |
var $input = $(this); |
| 1656 |
var inputName = $input.attr('name'); |
| 1657 |
if (inputName) { |
| 1658 |
var prefix = inputName.split('[')[0]; |
| 1659 |
var indexPart = inputName.match(/\[(.*?)\]/g); |
| 1660 |
if (indexPart && indexPart.length > 1) { |
| 1661 |
indexPart[1] = '[' + index + ']'; |
| 1662 |
var newName = "".concat(prefix).concat(indexPart.join('')); |
| 1663 |
$input.attr('name', newName); |
| 1664 |
} |
| 1665 |
} |
| 1666 |
}); |
| 1667 |
|
| 1668 |
// Find select2, Remove and Re-init again. select.select2('destroy') doesn't work. |
| 1669 |
$clonedLayout.find('select').each(function () { |
| 1670 |
if ($(this).hasClass('select2-hidden-accessible')) { |
| 1671 |
$(this).removeClass('select2-hidden-accessible').removeAttr('data-live-search').removeAttr('data-select2-id').removeAttr('aria-hidden').removeAttr('tabindex'); |
| 1672 |
|
| 1673 |
// Remove the existing dropdown |
| 1674 |
$(this).nextAll('.select2-container').remove(); |
| 1675 |
|
| 1676 |
// Re-init |
| 1677 |
$(this).select2(); |
| 1678 |
} |
| 1679 |
}); |
| 1680 |
|
| 1681 |
// Removing copied style to make the accordion work properly. |
| 1682 |
$clonedLayout.find('.layout-body').removeAttr('style'); |
| 1683 |
|
| 1684 |
// Append the cloned layout right after the source one with fadeIn effect. |
| 1685 |
$clonedLayout.hide(); |
| 1686 |
$clonedLayout.insertAfter($sourceLayout); |
| 1687 |
$clonedLayout.fadeIn(); |
| 1688 |
if ($clonedLayout.find('.merchant-module-page-setting-field-upload').length) { |
| 1689 |
initUploadField($clonedLayout.find('.merchant-module-page-setting-field-upload')); |
| 1690 |
} |
| 1691 |
if ($clonedLayout.find('.merchant-module-page-setting-field-select_ajax').length) { |
| 1692 |
initSelectAjax($clonedLayout.find('.merchant-module-page-setting-field-select_ajax')); |
| 1693 |
} |
| 1694 |
self.refreshNumbers($flexibleContent); |
| 1695 |
$(document).trigger('merchant-flexible-content-added', [$clonedLayout]); |
| 1696 |
if ($flexibleContentWrapper.hasClass('has-accordion')) { |
| 1697 |
$flexibleContent.accordion('refresh'); |
| 1698 |
} |
| 1699 |
self.updateLayoutTitle(); |
| 1700 |
self.updateLayoutStatus(); |
| 1701 |
GroubField.init(); |
| 1702 |
$('.merchant-module-page-content').trigger('change.merchant'); |
| 1703 |
}); |
| 1704 |
|
| 1705 |
// Delete item |
| 1706 |
$(document).on('click', '.customize-control-flexible-content-delete', function (event) { |
| 1707 |
event.preventDefault(); |
| 1708 |
var $item = $(this).closest('.layout'); |
| 1709 |
var $content = $item.parent(); |
| 1710 |
$item.remove(); |
| 1711 |
if ($content.find('.layout').length === 0) { |
| 1712 |
$content.addClass('empty'); |
| 1713 |
} |
| 1714 |
self.refreshNumbers($content); |
| 1715 |
$(document).trigger('merchant-flexible-content-deleted', [$item]); |
| 1716 |
var parentDiv = $(this).closest('.merchant-flexible-content-control'), |
| 1717 |
hasAccordion = parentDiv.hasClass('has-accordion'); |
| 1718 |
if (hasAccordion) { |
| 1719 |
parentDiv.find('.merchant-flexible-content').accordion("refresh"); |
| 1720 |
} |
| 1721 |
$('.merchant-module-page-content').trigger('change.merchant'); |
| 1722 |
}); |
| 1723 |
|
| 1724 |
// Toggle Actions(delete/duplicate) |
| 1725 |
$(document).on('click', '.layout-actions__toggle', function (e) { |
| 1726 |
e.preventDefault(); |
| 1727 |
|
| 1728 |
// Hide other opened elements |
| 1729 |
hideOtherActions($(this).closest('.layout')); |
| 1730 |
|
| 1731 |
// Toggle the current element |
| 1732 |
$(this).closest('.layout-actions').find('.layout-actions__inner').stop().slideToggle(300); |
| 1733 |
}); |
| 1734 |
|
| 1735 |
// Hide Actions when collapse/open |
| 1736 |
$(document).on('click', '.layout-header', function () { |
| 1737 |
hideOtherActions($(this).closest('.layout')); |
| 1738 |
}); |
| 1739 |
$(document).on('merchant-flexible-content-added', function (e, $layout) { |
| 1740 |
hideOtherActions($layout); |
| 1741 |
}); |
| 1742 |
function hideOtherActions($layout) { |
| 1743 |
if ($layout && $layout.length) { |
| 1744 |
$layout.siblings().find('.layout-actions__inner').slideUp(300); |
| 1745 |
} |
| 1746 |
} |
| 1747 |
|
| 1748 |
// Dismiss actions menu on click outside or Escape. |
| 1749 |
$(document).on('click', function (e) { |
| 1750 |
if (!$(e.target).closest('.layout-actions').length) { |
| 1751 |
$('.layout-actions__inner').slideUp(300); |
| 1752 |
} |
| 1753 |
}); |
| 1754 |
$(document).on('keydown', function (e) { |
| 1755 |
if (e.key === 'Escape') { |
| 1756 |
$('.layout-actions__inner').slideUp(300); |
| 1757 |
} |
| 1758 |
}); |
| 1759 |
}, |
| 1760 |
/** |
| 1761 |
* Hydrate a deferred layout row. |
| 1762 |
* |
| 1763 |
* Clones the hidden template for the layout type, populates field values |
| 1764 |
* from the data-fields-json attribute, initialises all widgets, and |
| 1765 |
* replaces the empty layout-body content with the fully rendered fields. |
| 1766 |
* |
| 1767 |
* @param {jQuery} $layout The .layout element with data-deferred="1". |
| 1768 |
*/ |
| 1769 |
hydrateLayout: function hydrateLayout($layout) { |
| 1770 |
var jsonStr = $layout.attr('data-fields-json'); |
| 1771 |
if (!jsonStr) { |
| 1772 |
return; |
| 1773 |
} |
| 1774 |
var data; |
| 1775 |
try { |
| 1776 |
data = JSON.parse(jsonStr); |
| 1777 |
} catch (e) { |
| 1778 |
return; |
| 1779 |
} |
| 1780 |
var layoutType = $layout.attr('data-type'); |
| 1781 |
var $control = $layout.closest('.merchant-flexible-content-control'); |
| 1782 |
var $template = $control.find('.layouts .layout[data-type="' + layoutType + '"]'); |
| 1783 |
if (!$template.length) { |
| 1784 |
return; |
| 1785 |
} |
| 1786 |
|
| 1787 |
// Clone the template's layout-body content (the rendered fields). |
| 1788 |
var $clonedBody = $template.find('.layout-body').clone(); |
| 1789 |
|
| 1790 |
// Determine the correct row index from layout-count. |
| 1791 |
var rowIndex = parseInt($layout.find('.layout-count').text(), 10) - 1; |
| 1792 |
var fieldId = $control.attr('data-id'); |
| 1793 |
|
| 1794 |
// Fix name attributes: replace data-name with name, and index [0] with [rowIndex]. |
| 1795 |
$clonedBody.find('input, select, textarea').each(function () { |
| 1796 |
var dataName = $(this).attr('data-name'); |
| 1797 |
if (dataName) { |
| 1798 |
$(this).attr('name', dataName.replace('[0]', '[' + rowIndex + ']')); |
| 1799 |
$(this).removeAttr('data-name'); |
| 1800 |
} |
| 1801 |
}); |
| 1802 |
|
| 1803 |
// Populate field values from the resolved data. |
| 1804 |
this.populateFieldValues($clonedBody, data, fieldId, rowIndex); |
| 1805 |
|
| 1806 |
// Sync color picker preview boxes to reflect populated values. |
| 1807 |
$clonedBody.find('.merchant-color').each(function () { |
| 1808 |
var colorVal = $(this).find('.merchant-color-input').val(); |
| 1809 |
if (colorVal) { |
| 1810 |
$(this).find('.merchant-color-picker').css('background-color', colorVal); |
| 1811 |
} |
| 1812 |
}); |
| 1813 |
|
| 1814 |
// Sync range slider values from their sibling number inputs. |
| 1815 |
// populateFieldValues only sets the number input (it has the name attribute); |
| 1816 |
// the range slider (no name) still holds the template default. |
| 1817 |
$clonedBody.find('.merchant-range').each(function () { |
| 1818 |
var numVal = $(this).find('.merchant-range-number-input').val(); |
| 1819 |
if (numVal !== undefined && numVal !== '') { |
| 1820 |
$(this).find('.merchant-range-input').val(numVal); |
| 1821 |
} |
| 1822 |
}); |
| 1823 |
|
| 1824 |
// Swap the content of the layout-body (not the element itself) |
| 1825 |
// to preserve jQuery UI accordion's internal panel reference. |
| 1826 |
var $existingBody = $layout.find('.layout-body'); |
| 1827 |
$existingBody.empty().append($clonedBody.children()); |
| 1828 |
|
| 1829 |
// Initialise widgets within the hydrated layout. |
| 1830 |
if ($existingBody.find('.merchant-module-page-setting-field-upload').length) { |
| 1831 |
initUploadField($existingBody.find('.merchant-module-page-setting-field-upload')); |
| 1832 |
} |
| 1833 |
if ($existingBody.find('.merchant-module-page-setting-field-select_ajax').length) { |
| 1834 |
initSelectAjax($existingBody.find('.merchant-module-page-setting-field-select_ajax')); |
| 1835 |
} |
| 1836 |
|
| 1837 |
// Trigger events to re-initialise fields_group, color pickers, conditions, etc. |
| 1838 |
$(document).trigger('merchant-flexible-content-added', [$layout]); |
| 1839 |
GroubField.init(); |
| 1840 |
initMerchantRange(); |
| 1841 |
|
| 1842 |
// Init layout title binding. |
| 1843 |
var $title = $layout.find('.layout-title[data-title-field]'); |
| 1844 |
if ($title.length) { |
| 1845 |
var titleFieldId = $title.attr('data-title-field'); |
| 1846 |
var $titleInput = $layout.find('.layout-body .merchant-field-' + titleFieldId + ' input'); |
| 1847 |
$titleInput.on('change keyup', function () { |
| 1848 |
$title.text($(this).val()); |
| 1849 |
}); |
| 1850 |
} |
| 1851 |
|
| 1852 |
// Init status badge binding for hydrated row. |
| 1853 |
var $badge = $layout.find('.layout-header .layout-status[data-status-field]'); |
| 1854 |
if ($badge.length) { |
| 1855 |
var statusFieldId = $badge.data('status-field'); |
| 1856 |
var statusAdapter = this.resolveStatusField($layout, statusFieldId); |
| 1857 |
if (statusAdapter) { |
| 1858 |
var syncHydratedBadge = function syncHydratedBadge() { |
| 1859 |
var val = statusAdapter.getValue(); |
| 1860 |
var label = statusAdapter.getLabel(); |
| 1861 |
$badge.removeClass(function (i, cls) { |
| 1862 |
return (cls.match(/layout-status--\S+/g) || []).join(' '); |
| 1863 |
}).addClass('layout-status--' + val).text(label.trim()); |
| 1864 |
}; |
| 1865 |
statusAdapter.onChange(syncHydratedBadge); |
| 1866 |
syncHydratedBadge(); |
| 1867 |
} |
| 1868 |
} |
| 1869 |
|
| 1870 |
// Init discount max val. |
| 1871 |
this.updateDiscountPercentMaxVal(); |
| 1872 |
|
| 1873 |
// Remove the deferred flag and clean up. |
| 1874 |
$layout.removeAttr('data-deferred'); |
| 1875 |
$layout.removeAttr('data-fields-json'); |
| 1876 |
|
| 1877 |
// Refresh accordion to re-sync panel references after DOM change. |
| 1878 |
var $content = $control.find('.merchant-flexible-content'); |
| 1879 |
if ($content.data('ui-accordion')) { |
| 1880 |
$content.accordion('refresh'); |
| 1881 |
} |
| 1882 |
|
| 1883 |
// Trigger condition checks for the newly hydrated panel. |
| 1884 |
$(document).trigger('merchant-admin-check-fields'); |
| 1885 |
$(document).trigger('merchant-admin-check-color-fields'); |
| 1886 |
}, |
| 1887 |
/** |
| 1888 |
* Populate field values from deferred data into a cloned template body. |
| 1889 |
* |
| 1890 |
* @param {jQuery} $body The cloned .layout-body element. |
| 1891 |
* @param {Object} data The parsed data-fields-json object. |
| 1892 |
* @param {string} fieldId The flexible content field ID. |
| 1893 |
* @param {number} rowIndex The row index for name attribute construction. |
| 1894 |
*/ |
| 1895 |
populateFieldValues: function populateFieldValues($body, data, fieldId, rowIndex) { |
| 1896 |
var values = data.values || {}; |
| 1897 |
var selectOptions = data.select_options || {}; |
| 1898 |
var productOptions = data.product_options || {}; |
| 1899 |
var reviewOptions = data.review_options || {}; |
| 1900 |
|
| 1901 |
// Populate simple field values. |
| 1902 |
$body.find('input, select, textarea').each(function () { |
| 1903 |
var $el = $(this); |
| 1904 |
var name = $el.attr('name') || ''; |
| 1905 |
|
| 1906 |
// Handle checkbox_multiple: name ends with [] (e.g. merchant[fc][0][show_pages][]). |
| 1907 |
var arrayMatch = name.match(/\[([^\]]+)\]\[\]$/); |
| 1908 |
if (arrayMatch) { |
| 1909 |
var arrayKey = arrayMatch[1]; |
| 1910 |
var arrVal = values[arrayKey]; |
| 1911 |
if (Array.isArray(arrVal) && $el.is(':checkbox')) { |
| 1912 |
$el.prop('checked', arrVal.indexOf($el.val()) !== -1); |
| 1913 |
} |
| 1914 |
return; |
| 1915 |
} |
| 1916 |
|
| 1917 |
// Extract the field key from name="merchant[fieldId][rowIndex][fieldKey]". |
| 1918 |
var match = name.match(/\[([^\]]+)\]$/); |
| 1919 |
if (!match) { |
| 1920 |
return; |
| 1921 |
} |
| 1922 |
var fieldKey = match[1]; |
| 1923 |
var val = values[fieldKey]; |
| 1924 |
if (val === undefined || val === null) { |
| 1925 |
return; |
| 1926 |
} |
| 1927 |
if ($el.is(':checkbox') && !$el.is(':radio')) { |
| 1928 |
$el.prop('checked', !!parseInt(val, 10)); |
| 1929 |
} else if ($el.is(':radio')) { |
| 1930 |
$el.prop('checked', $el.val() === String(val)); |
| 1931 |
} else if ($el.is('select')) { |
| 1932 |
$el.val(val); |
| 1933 |
} else { |
| 1934 |
$el.val(_typeof(val) === 'object' && val !== null ? JSON.stringify(val) : val); |
| 1935 |
} |
| 1936 |
}); |
| 1937 |
|
| 1938 |
// Populate select_ajax pre-selected options. |
| 1939 |
$.each(selectOptions, function (selectFieldId, options) { |
| 1940 |
var $select = $body.find('[data-id="' + selectFieldId + '"] select'); |
| 1941 |
if (!$select.length) { |
| 1942 |
return; |
| 1943 |
} |
| 1944 |
|
| 1945 |
// Clear any existing options and add resolved ones. |
| 1946 |
$select.empty(); |
| 1947 |
$.each(options, function (_, opt) { |
| 1948 |
$select.append($('<option>').val(opt.id).text(opt.text).prop('selected', true)); |
| 1949 |
}); |
| 1950 |
}); |
| 1951 |
|
| 1952 |
// Populate products_selector pre-selected products. |
| 1953 |
$.each(productOptions, function (prodFieldId, products) { |
| 1954 |
var $container = $body.find('[data-id="' + prodFieldId + '"] .merchant-products-search-container'); |
| 1955 |
if (!$container.length) { |
| 1956 |
return; |
| 1957 |
} |
| 1958 |
var $preview = $container.find('.merchant-selected-products-preview ul'); |
| 1959 |
var ids = []; |
| 1960 |
$preview.empty(); |
| 1961 |
$.each(products, function (_, product) { |
| 1962 |
ids.push(product.id); |
| 1963 |
|
| 1964 |
// Build <li> matching the structure from Merchant_Admin_Ajax::product_data_li(). |
| 1965 |
var $li = $('<li>').addClass('product-item').attr('data-id', product.id).attr('data-name', product.title); |
| 1966 |
if (product.image) { |
| 1967 |
$li.append($('<span>').addClass('img').append($('<img>').attr('src', product.image).attr('alt', product.title).attr('width', 30).attr('height', 30))); |
| 1968 |
} |
| 1969 |
$li.append($('<span>').addClass('data').append($('<span>').addClass('name').text(product.title), $('<span>').addClass('info').html(product.price))); |
| 1970 |
if (product.type || product.id) { |
| 1971 |
var typeHtml = (product.type || '') + '<br>#' + product.id; |
| 1972 |
var $typeSpan = $('<span>').addClass('type'); |
| 1973 |
if (product.edit_url) { |
| 1974 |
$typeSpan.append($('<a>').attr('href', product.edit_url).attr('target', '_blank').html(typeHtml)); |
| 1975 |
} else { |
| 1976 |
$typeSpan.html(typeHtml); |
| 1977 |
} |
| 1978 |
$li.append($typeSpan); |
| 1979 |
} |
| 1980 |
$li.append($('<span>').addClass('remove hint--left').attr('aria-label', 'Remove').html('×')); |
| 1981 |
$preview.append($li); |
| 1982 |
}); |
| 1983 |
$container.find('.merchant-selected-products').val(ids.join(',')); |
| 1984 |
}); |
| 1985 |
|
| 1986 |
// Populate reviews_selector pre-selected reviews. |
| 1987 |
$.each(reviewOptions, function (reviewFieldId, reviews) { |
| 1988 |
var $container = $body.find('[data-id="' + reviewFieldId + '"] .merchant-reviews-selector'); |
| 1989 |
if (!$container.length) { |
| 1990 |
return; |
| 1991 |
} |
| 1992 |
var $selectedList = $container.find('.selected-reviews .product-reviews'); |
| 1993 |
var ids = []; |
| 1994 |
$selectedList.empty(); |
| 1995 |
$.each(reviews, function (_, review) { |
| 1996 |
ids.push(review.id); |
| 1997 |
var $reviewEl = $('<div>').addClass('product-review').attr('data-review-id', review.id); |
| 1998 |
$reviewEl.append($('<span>').addClass('review-author').text(review.author)); |
| 1999 |
$reviewEl.append($('<span>').addClass('review-content').text(review.content)); |
| 2000 |
$reviewEl.append($('<span>').addClass('review-rating').text('� |
| 2001 |
'.repeat(parseInt(review.rating) || 0))); |
| 2002 |
$reviewEl.append($('<span>').addClass('review-product').text(review.product_name)); |
| 2003 |
$reviewEl.append($('<span>').addClass('product-review-delete').html('×')); |
| 2004 |
$reviewEl.append($('<span>').addClass('product-review-move').html('⋮⋮')); |
| 2005 |
$selectedList.append($reviewEl); |
| 2006 |
}); |
| 2007 |
$container.find('.merchant-selected-reviews').val(ids.join(',')); |
| 2008 |
}); |
| 2009 |
|
| 2010 |
// Handle fields_group and compound (e.g. hook_select) nested values. |
| 2011 |
// Recursively walks the value tree to find and set inputs |
| 2012 |
// at any nesting depth (e.g. fields_group → hook_select → hook_name). |
| 2013 |
var _setNestedValues = function setNestedValues(obj, namePrefix) { |
| 2014 |
$.each(obj, function (key, val) { |
| 2015 |
if (_typeof(val) === 'object' && val !== null && !Array.isArray(val)) { |
| 2016 |
// Recurse into nested objects. |
| 2017 |
_setNestedValues(val, namePrefix + '[' + key + ']'); |
| 2018 |
} else if (Array.isArray(val)) { |
| 2019 |
// checkbox_multiple inside a nested object: check boxes whose value is in the array. |
| 2020 |
$body.find('[name="' + namePrefix + '[' + key + '][]"]').each(function () { |
| 2021 |
if ($(this).is(':checkbox')) { |
| 2022 |
$(this).prop('checked', val.indexOf($(this).val()) !== -1); |
| 2023 |
} |
| 2024 |
}); |
| 2025 |
} else { |
| 2026 |
var $field = $body.find('[name="' + namePrefix + '[' + key + ']"]'); |
| 2027 |
if (!$field.length) { |
| 2028 |
return; |
| 2029 |
} |
| 2030 |
if ($field.is(':checkbox') && !$field.is(':radio')) { |
| 2031 |
$field.prop('checked', !!parseInt(val, 10)); |
| 2032 |
} else if ($field.is(':radio')) { |
| 2033 |
$field.prop('checked', $field.val() === String(val)); |
| 2034 |
} else { |
| 2035 |
$field.val(val); |
| 2036 |
} |
| 2037 |
} |
| 2038 |
}); |
| 2039 |
}; |
| 2040 |
$.each(values, function (key, val) { |
| 2041 |
if (_typeof(val) === 'object' && val !== null && !Array.isArray(val)) { |
| 2042 |
_setNestedValues(val, 'merchant[' + fieldId + '][' + rowIndex + '][' + key + ']'); |
| 2043 |
} |
| 2044 |
}); |
| 2045 |
}, |
| 2046 |
refreshNumbers: function refreshNumbers($content) { |
| 2047 |
$content.find('.layout').each(function (index) { |
| 2048 |
var $count = $(this).find('.layout-count').text(); |
| 2049 |
var $inputIndex = parseInt($count) - 1; |
| 2050 |
$(this).find('.layout-count').text(index + 1); |
| 2051 |
$(this).find('input, select, textarea').each(function () { |
| 2052 |
if ($(this).attr('name')) { |
| 2053 |
$(this).attr('name', $(this).attr('name').replace('[' + $inputIndex + ']', '[*refreshed*' + index + ']')); |
| 2054 |
} |
| 2055 |
}); |
| 2056 |
}); |
| 2057 |
$content.find('.layout').each(function (index) { |
| 2058 |
$(this).find('input, select, textarea').each(function () { |
| 2059 |
// We've added *refreshed* to the attribute name in the prior loop as refreshing the numbers in the attribute can cause |
| 2060 |
// checked boxes to be unchecked due to similar attribute names during the change while sorting, within this loop we remove them |
| 2061 |
var nameAttr = $(this).attr('name'); |
| 2062 |
if (nameAttr) { |
| 2063 |
// Check if name attribute exists |
| 2064 |
$(this).attr('name', nameAttr.replace('*refreshed*', '')); |
| 2065 |
} |
| 2066 |
}); |
| 2067 |
}); |
| 2068 |
$content.parent().find('input').trigger('change.merchant'); |
| 2069 |
} |
| 2070 |
}; |
| 2071 |
|
| 2072 |
// Initialize Flexible Content. |
| 2073 |
FlexibleContentField.init(); |
| 2074 |
GroubField.init(); |
| 2075 |
ReviewsSelector.init(); |
| 2076 |
|
| 2077 |
// Products selector. |
| 2078 |
// Handle keyup event for the search input |
| 2079 |
var debounceTimer; |
| 2080 |
$(document).on('keyup', '.merchant-module-page-setting-field-products_selector .merchant-search-field', function () { |
| 2081 |
clearTimeout(debounceTimer); |
| 2082 |
var categories = []; |
| 2083 |
var $excluded = $(this).closest('[data-id="excluded_products"]'); |
| 2084 |
if ($excluded.length) { |
| 2085 |
var $layout = $(this).closest('.layout'); |
| 2086 |
var rules = $layout.find('.merchant-field-rules_to_apply select').val() || $layout.find('.merchant-field-rules_to_display select').val() || $layout.find('.merchant-field-display_rules select').val(); |
| 2087 |
if (rules === 'categories' || rules === 'by_category') { |
| 2088 |
categories = $layout.find('.merchant-field-category_slugs select').val() || $layout.find('.merchant-field-product_cats select').val(); |
| 2089 |
} |
| 2090 |
} |
| 2091 |
var parent = $(this).closest('.merchant-products-search-container'); |
| 2092 |
if ($(this).val() !== '') { |
| 2093 |
parent.find('.merchant-searching').addClass('active'); |
| 2094 |
var data = { |
| 2095 |
action: 'merchant_admin_products_search', |
| 2096 |
nonce: merchant_admin_options.ajaxnonce, |
| 2097 |
keyword: $(this).val(), |
| 2098 |
product_types: $(this).data('allowed-types'), |
| 2099 |
ids: parent.find('.merchant-selected-products').val(), |
| 2100 |
categories: categories |
| 2101 |
}; |
| 2102 |
debounceTimer = setTimeout(function () { |
| 2103 |
$.post(merchant_admin_options.ajaxurl, data, function (response) { |
| 2104 |
var results = parent.find('.merchant-selections-products-preview'); |
| 2105 |
results.show(); |
| 2106 |
results.html(response); |
| 2107 |
parent.find('.merchant-searching').removeClass('active'); |
| 2108 |
}); |
| 2109 |
}, 250); |
| 2110 |
} else { |
| 2111 |
parent.find('.merchant-selections-products-preview').html('').hide(); |
| 2112 |
} |
| 2113 |
}); |
| 2114 |
|
| 2115 |
// Products selector: dismiss search results on click outside or Escape. |
| 2116 |
$(document).on('click touch', function (e) { |
| 2117 |
if (!$(e.target).closest('.merchant-products-search-container').length) { |
| 2118 |
$('.merchant-selections-products-preview').html('').hide(); |
| 2119 |
$('.merchant-search-field').val(''); |
| 2120 |
} |
| 2121 |
}); |
| 2122 |
$(document).on('keydown', '.merchant-search-field', function (e) { |
| 2123 |
if (e.key === 'Escape') { |
| 2124 |
var parent = $(this).closest('.merchant-products-search-container'); |
| 2125 |
parent.find('.merchant-selections-products-preview').html('').hide(); |
| 2126 |
$(this).val('').blur(); |
| 2127 |
} |
| 2128 |
}); |
| 2129 |
|
| 2130 |
// Products selector. |
| 2131 |
// Handle click/touch event for the search results |
| 2132 |
$(document).on('click touch', '.merchant-module-page-setting-field-products_selector .merchant-selections-products-preview li', function () { |
| 2133 |
var parent = $(this).closest('.merchant-products-search-container'), |
| 2134 |
valueField = parent.find('.merchant-selected-products'), |
| 2135 |
oldValue = valueField.val(), |
| 2136 |
multiple = parent.data('multiple') === 'multiple'; |
| 2137 |
if (parent.find('.merchant-selected-products-preview ul li').length > 0 && !multiple) { |
| 2138 |
// replace the first item |
| 2139 |
parent.find('.merchant-selected-products-preview ul li').remove(); |
| 2140 |
valueField.val('').change(); |
| 2141 |
} |
| 2142 |
$(this).children('.remove').attr('aria-label', 'Remove').html('×'); |
| 2143 |
parent.find('.merchant-selected-products-preview ul').append($(this)); |
| 2144 |
parent.find('.merchant-selections-products-preview').html('').hide(); |
| 2145 |
parent.find('.merchant-search-field').val('').change(); |
| 2146 |
if (oldValue === '') { |
| 2147 |
valueField.val($(this).data('id')); |
| 2148 |
} else { |
| 2149 |
if (!multiple) { |
| 2150 |
valueField.val($(this).data('id')).change(); |
| 2151 |
} else { |
| 2152 |
var newValue = oldValue.split(','); |
| 2153 |
newValue.push($(this).data('id')); |
| 2154 |
valueField.val(newValue.join(',')).change(); |
| 2155 |
} |
| 2156 |
} |
| 2157 |
}); |
| 2158 |
|
| 2159 |
// Products selector. |
| 2160 |
// Handle click/touch event for the remove button. |
| 2161 |
$(document).on('click touch', '.merchant-selected-products-preview .remove', function () { |
| 2162 |
var removeButton = $(this); |
| 2163 |
var parent = removeButton.closest('.merchant-products-search-container'), |
| 2164 |
valueField = parent.find('.merchant-selected-products'), |
| 2165 |
id = removeButton.parent().data('id'); |
| 2166 |
removeButton.parent().remove(); |
| 2167 |
var currentValue = valueField.val().split(','); |
| 2168 |
if (currentValue.length > 0) { |
| 2169 |
for (var key in currentValue) { |
| 2170 |
if (parseInt(currentValue[key]) === parseInt(id)) { |
| 2171 |
currentValue.splice(key, 1); |
| 2172 |
} |
| 2173 |
} |
| 2174 |
} |
| 2175 |
valueField.val(currentValue.join(',')).change(); |
| 2176 |
valueField.trigger('change.merchant'); |
| 2177 |
}); |
| 2178 |
$(document).on('merchant-admin-check-fields merchant-flexible-content-added', function () { |
| 2179 |
$('.merchant-module-page-setting-field').each(function () { |
| 2180 |
var $field = $(this); |
| 2181 |
if ($field.data('condition') && $field.data('condition').length) { |
| 2182 |
var condition = $field.data('condition'); |
| 2183 |
var $target = $(this).closest('.layout-body').find('input[name*="' + condition[0] + '"],select[name*="' + condition[0] + '"]'); |
| 2184 |
if (!$target.length) { |
| 2185 |
$target = $('input[name="merchant[' + condition[0] + ']"],select[name="merchant[' + condition[0] + ']"]'); |
| 2186 |
} |
| 2187 |
if ($target.length) { |
| 2188 |
var passed = false; |
| 2189 |
switch (condition[1]) { |
| 2190 |
case '==': |
| 2191 |
if ($target.attr('type') === 'radio' || $target.attr('type') === 'checkbox') { |
| 2192 |
var checked = $target.parent().find('input:checked'); |
| 2193 |
if (checked.length && checked.val() === condition[2]) { |
| 2194 |
passed = true; |
| 2195 |
} |
| 2196 |
} |
| 2197 |
if ($target.is('select') && $target.val() == condition[2]) { |
| 2198 |
passed = true; |
| 2199 |
} |
| 2200 |
break; |
| 2201 |
case 'any': |
| 2202 |
if ($target.attr('type') === 'radio' || $target.attr('type') === 'checkbox') { |
| 2203 |
var _checked = $target.parent().find('input:checked'); |
| 2204 |
if (_checked.length && condition[2].split('|').includes(_checked.val())) { |
| 2205 |
passed = true; |
| 2206 |
} |
| 2207 |
} |
| 2208 |
if ($target.is('select') && condition[2].split('|').includes($target.val())) { |
| 2209 |
passed = true; |
| 2210 |
} |
| 2211 |
break; |
| 2212 |
} |
| 2213 |
if (passed) { |
| 2214 |
$field.removeClass('merchant-hide').addClass('merchant-show'); |
| 2215 |
} else { |
| 2216 |
$field.removeClass('merchant-show').addClass('merchant-hide'); |
| 2217 |
} |
| 2218 |
} |
| 2219 |
} |
| 2220 |
}); |
| 2221 |
}).trigger('merchant.change'); |
| 2222 |
$(document).on('merchant-admin-check-fields merchant-flexible-content-added change keyup', function () { |
| 2223 |
$(document).find('.merchant-module-page-setting-field').each(function () { |
| 2224 |
var $field = $(this); |
| 2225 |
if ($field.data('conditions')) { |
| 2226 |
var conditions = $field.data('conditions'), |
| 2227 |
passed = evaluateConditions(conditions, $field); |
| 2228 |
if (passed) { |
| 2229 |
$field.removeClass('merchant-hide').addClass('merchant-show'); |
| 2230 |
} else { |
| 2231 |
$field.removeClass('merchant-show').addClass('merchant-hide'); |
| 2232 |
} |
| 2233 |
} |
| 2234 |
}); |
| 2235 |
}).trigger('merchant.change'); |
| 2236 |
$(document).on('change', '.merchant-module-page-setting-field', function () { |
| 2237 |
$(document).trigger('merchant-admin-check-fields'); |
| 2238 |
}).trigger('merchant.change'); |
| 2239 |
$(document).trigger('merchant-admin-check-fields'); |
| 2240 |
$(document).on('merchant-admin-check-color-fields merchant-flexible-content-added', function () { |
| 2241 |
$('.merchant-color').each(function () { |
| 2242 |
var $color = $(this); |
| 2243 |
var $picker = $color.find('.merchant-color-picker'); |
| 2244 |
var $input = $color.find('.merchant-color-input'); |
| 2245 |
var inited = false; |
| 2246 |
var pickr; |
| 2247 |
$picker.off('click').on('click', function (e) { |
| 2248 |
e.preventDefault(); |
| 2249 |
e.stopPropagation(); |
| 2250 |
var $bodyHTML = $('body,html'); |
| 2251 |
$bodyHTML.addClass('merchant-height-auto'); |
| 2252 |
if (!inited) { |
| 2253 |
pickr = new Pickr({ |
| 2254 |
el: $picker.get(0), |
| 2255 |
container: 'body', |
| 2256 |
theme: 'merchant', |
| 2257 |
appClass: 'merchant-pcr-app', |
| 2258 |
default: $input.val() || $picker.data('default-color') || '#212121', |
| 2259 |
swatches: ['#000000', '#F44336', '#E91E63', '#673AB7', '#03A9F4', '#8BC34A', '#FFEB3B', '#FFC107', '#FFFFFF'], |
| 2260 |
sliders: 'h', |
| 2261 |
useAsButton: true, |
| 2262 |
components: { |
| 2263 |
hue: true, |
| 2264 |
preview: true, |
| 2265 |
opacity: true, |
| 2266 |
interaction: { |
| 2267 |
input: true, |
| 2268 |
clear: true |
| 2269 |
} |
| 2270 |
}, |
| 2271 |
i18n: { |
| 2272 |
'btn:clear': 'Default' |
| 2273 |
} |
| 2274 |
}); |
| 2275 |
pickr.on('change', function (color) { |
| 2276 |
var colorCode; |
| 2277 |
if (color.a === 1) { |
| 2278 |
pickr.setColorRepresentation('HEX'); |
| 2279 |
colorCode = color.toHEXA().toString(0); |
| 2280 |
} else { |
| 2281 |
pickr.setColorRepresentation('RGBA'); |
| 2282 |
colorCode = color.toRGBA().toString(0); |
| 2283 |
} |
| 2284 |
$picker.css({ |
| 2285 |
'background-color': colorCode |
| 2286 |
}); |
| 2287 |
if ($input.val() !== colorCode) { |
| 2288 |
$input.val(colorCode).trigger('change.merchant'); |
| 2289 |
} |
| 2290 |
$(document).trigger('merchant-color-picker-updated', [colorCode, $input]); |
| 2291 |
}); |
| 2292 |
pickr.on('clear', function () { |
| 2293 |
var defaultColor = $picker.data('default-color'); |
| 2294 |
if (defaultColor) { |
| 2295 |
pickr.setColor(defaultColor); |
| 2296 |
} else { |
| 2297 |
$picker.css({ |
| 2298 |
'background-color': 'white' |
| 2299 |
}); |
| 2300 |
$input.val(''); |
| 2301 |
} |
| 2302 |
}); |
| 2303 |
pickr.on('hide', function () { |
| 2304 |
$bodyHTML.removeClass('merchant-height-auto'); |
| 2305 |
}); |
| 2306 |
$picker.data('pickr', pickr); |
| 2307 |
setTimeout(function () { |
| 2308 |
pickr.show(); |
| 2309 |
}, 200); |
| 2310 |
inited = true; |
| 2311 |
} else { |
| 2312 |
pickr.setColor($input.val()); |
| 2313 |
} |
| 2314 |
}); |
| 2315 |
$input.on('change keyup', function () { |
| 2316 |
var colorCode = $(this).val(); |
| 2317 |
$picker.css({ |
| 2318 |
'background-color': colorCode |
| 2319 |
}); |
| 2320 |
}); |
| 2321 |
}); |
| 2322 |
}); |
| 2323 |
$(document).trigger('merchant-admin-check-color-fields'); |
| 2324 |
|
| 2325 |
// Create Page Control. |
| 2326 |
var CreatePageControl = { |
| 2327 |
init: function init() { |
| 2328 |
this.events(); |
| 2329 |
}, |
| 2330 |
events: function events() { |
| 2331 |
var self = this; |
| 2332 |
$(document).on('click', '.merchant-create-page-control-button', function (e) { |
| 2333 |
e.preventDefault(); |
| 2334 |
var $this = $(this), |
| 2335 |
$create_message = $this.parent().find('.merchant-create-page-control-create-message'), |
| 2336 |
$success_message = $this.parent().find('.merchant-create-page-control-success-message'), |
| 2337 |
initial_text = $this.text(), |
| 2338 |
creating_text = $this.data('creating-text'), |
| 2339 |
created_text = $this.data('created-text'), |
| 2340 |
page_title = $this.data('page-title'), |
| 2341 |
page_meta_key = $this.data('page-meta-key'), |
| 2342 |
page_meta_value = $this.data('page-meta-value'), |
| 2343 |
option_name = $this.data('option-name'), |
| 2344 |
nonce = $this.data('nonce'); |
| 2345 |
if (!page_title) { |
| 2346 |
return false; |
| 2347 |
} |
| 2348 |
$(this).text(creating_text); |
| 2349 |
$(this).attr('disabled', true); |
| 2350 |
$.ajax({ |
| 2351 |
type: 'post', |
| 2352 |
url: ajaxurl, |
| 2353 |
data: { |
| 2354 |
action: 'merchant_create_page_control', |
| 2355 |
page_title: page_title, |
| 2356 |
page_meta_key: page_meta_key, |
| 2357 |
page_meta_value: page_meta_value, |
| 2358 |
option_name: option_name, |
| 2359 |
nonce: nonce |
| 2360 |
}, |
| 2361 |
success: function success(response) { |
| 2362 |
self.ajaxResponseHandler(response, $this, $success_message, $create_message); |
| 2363 |
} |
| 2364 |
}); |
| 2365 |
}); |
| 2366 |
}, |
| 2367 |
ajaxResponseHandler: function ajaxResponseHandler(response, $this, $success_message, $create_message) { |
| 2368 |
if ('success' === response.status) { |
| 2369 |
var $editLink = $success_message.find('a').first(), |
| 2370 |
href = $editLink.attr('href'); |
| 2371 |
if (href) { |
| 2372 |
$editLink.attr('href', href.replace('?post=&', '?post=' + response.page_id + '&')); |
| 2373 |
} |
| 2374 |
$success_message.css('display', 'block'); |
| 2375 |
$create_message.remove(); |
| 2376 |
$this.remove(); |
| 2377 |
} |
| 2378 |
} |
| 2379 |
}; |
| 2380 |
|
| 2381 |
// Initialize Create Page Control. |
| 2382 |
CreatePageControl.init(); |
| 2383 |
$('.merchant-module-page-setting-field-gallery').each(function () { |
| 2384 |
var $this = $(this); |
| 2385 |
var $button = $this.find('.merchant-gallery-button'); |
| 2386 |
var $input = $this.find('.merchant-gallery-input'); |
| 2387 |
var $images = $this.find('.merchant-gallery-images'); |
| 2388 |
var $remove = $this.find('.merchant-gallery-remove'); |
| 2389 |
var wpMediaFrame; |
| 2390 |
var sortable = $images.sortable({ |
| 2391 |
helper: 'original', |
| 2392 |
update: function update(event, ui) { |
| 2393 |
var selectedIds = []; |
| 2394 |
$images.find('.merchant-gallery-image').each(function () { |
| 2395 |
selectedIds.push($(this).data('item-id')); |
| 2396 |
}); |
| 2397 |
$input.val(selectedIds.join(',')).trigger('change'); |
| 2398 |
} |
| 2399 |
}); |
| 2400 |
$remove.on('click', function (e) { |
| 2401 |
e.preventDefault(); |
| 2402 |
$(this).parent().remove(); |
| 2403 |
var selectedIds = []; |
| 2404 |
$images.find('.merchant-gallery-image').each(function () { |
| 2405 |
selectedIds.push($(this).data('item-id')); |
| 2406 |
}); |
| 2407 |
$input.val(selectedIds.join(',')).trigger('change'); |
| 2408 |
}); |
| 2409 |
$button.on('click', function (e) { |
| 2410 |
var $btn = $(this); |
| 2411 |
var ids = $input.val(); |
| 2412 |
var mode = ids ? 'edit' : 'add'; |
| 2413 |
e.preventDefault(); |
| 2414 |
if (typeof window.wp === 'undefined' || !window.wp.media || !window.wp.media.gallery) { |
| 2415 |
return; |
| 2416 |
} |
| 2417 |
if (mode === 'add') { |
| 2418 |
wpMediaFrame = window.wp.media({ |
| 2419 |
library: { |
| 2420 |
type: 'image' |
| 2421 |
}, |
| 2422 |
frame: 'post', |
| 2423 |
state: 'gallery', |
| 2424 |
multiple: true |
| 2425 |
}); |
| 2426 |
wpMediaFrame.open(); |
| 2427 |
} else { |
| 2428 |
wpMediaFrame = window.wp.media.gallery.edit('[gallery ids="' + ids + '"]'); |
| 2429 |
} |
| 2430 |
wpMediaFrame.on('update', function (selection) { |
| 2431 |
$images.empty(); |
| 2432 |
var selectedIds = selection.models.map(function (attachment) { |
| 2433 |
var item = attachment.toJSON(); |
| 2434 |
var thumb = item.sizes && item.sizes.thumbnail && item.sizes.thumbnail.url ? item.sizes.thumbnail.url : item.url; |
| 2435 |
$images.append('<div class="merchant-gallery-image" data-item-id="' + item.id + '"><i class="merchant-gallery-remove dashicons dashicons-no-alt"></i><img src="' + thumb + '" /></div>'); |
| 2436 |
return item.id; |
| 2437 |
}); |
| 2438 |
$input.val(selectedIds.join(',')).trigger('change'); |
| 2439 |
$this.find('.merchant-gallery-remove').on('click', function (e) { |
| 2440 |
e.preventDefault(); |
| 2441 |
$(this).parent().remove(); |
| 2442 |
var selectedIds = []; |
| 2443 |
$images.find('.merchant-gallery-image').each(function () { |
| 2444 |
selectedIds.push($(this).data('item-id')); |
| 2445 |
}); |
| 2446 |
$input.val(selectedIds.join(',')).trigger('change'); |
| 2447 |
}); |
| 2448 |
}); |
| 2449 |
}); |
| 2450 |
}); |
| 2451 |
var initUploadField = function initUploadField(element) { |
| 2452 |
var $this = element; |
| 2453 |
var $button = $this.find('.merchant-upload-button'); |
| 2454 |
var $input = $this.find('.merchant-upload-input'); |
| 2455 |
var $wrapper = $this.find('.merchant-upload-wrapper'); |
| 2456 |
var $remove = $this.find('.merchant-upload-remove'); |
| 2457 |
var wpMediaFrame; |
| 2458 |
$remove.on('click', function (e) { |
| 2459 |
e.preventDefault(); |
| 2460 |
$(this).parent().remove(); |
| 2461 |
$input.val('').trigger('change'); |
| 2462 |
}); |
| 2463 |
$button.on('click', function (e) { |
| 2464 |
e.preventDefault(); |
| 2465 |
if (typeof window.wp === 'undefined' || !window.wp.media) { |
| 2466 |
return; |
| 2467 |
} |
| 2468 |
if (!wpMediaFrame) { |
| 2469 |
wpMediaFrame = window.wp.media({ |
| 2470 |
library: { |
| 2471 |
type: 'image' |
| 2472 |
} |
| 2473 |
}); |
| 2474 |
} |
| 2475 |
wpMediaFrame.open(); |
| 2476 |
wpMediaFrame.on('select', function () { |
| 2477 |
$wrapper.empty(); |
| 2478 |
var item = wpMediaFrame.state().get('selection').first().attributes; |
| 2479 |
var thumb = item.sizes && item.sizes.thumbnail && item.sizes.thumbnail.url ? item.sizes.thumbnail.url : item.url; |
| 2480 |
var sizes = item.sizes ? JSON.stringify(item.sizes) : ''; |
| 2481 |
$wrapper.append('<div class="merchant-upload-image" data-sizes=\'' + sizes + '\'><i class="merchant-upload-remove dashicons dashicons-no-alt"></i><img src="' + thumb + '" /></div>'); |
| 2482 |
$input.val(item.id).trigger('change'); |
| 2483 |
$this.find('.merchant-upload-button-drag-drop').hide(); |
| 2484 |
$this.find('.merchant-upload-remove').on('click', function (e) { |
| 2485 |
e.preventDefault(); |
| 2486 |
$(this).parent().remove(); |
| 2487 |
$input.val('').trigger('change'); |
| 2488 |
$this.find('.merchant-upload-button-drag-drop').show(); |
| 2489 |
}); |
| 2490 |
}); |
| 2491 |
}); |
| 2492 |
}; |
| 2493 |
$('.merchant-module-page-setting-field-upload:not(.template)').each(function () { |
| 2494 |
initUploadField($(this)); |
| 2495 |
}); |
| 2496 |
|
| 2497 |
// Drag & Drag |
| 2498 |
var events = ['dragenter', 'dragover', 'dragleave', 'drop']; |
| 2499 |
jQuery.each(events, function (index, eventName) { |
| 2500 |
$(document).on(eventName, '.merchant-upload-button-drag-drop', function (e) { |
| 2501 |
e.preventDefault(); |
| 2502 |
e.stopPropagation(); |
| 2503 |
}); |
| 2504 |
}); |
| 2505 |
$(document).on('dragenter', '.merchant-upload-button-drag-drop', function (e) { |
| 2506 |
$(this).closest('.merchant-module-page-setting-field-upload').find('.merchant-upload-button').click(); |
| 2507 |
}); |
| 2508 |
var initSelectAjax = function initSelectAjax($selectAjax) { |
| 2509 |
var $select = $selectAjax.find('select'); |
| 2510 |
var $source = $select.data('source'); |
| 2511 |
var $config = window.merchant_admin_options; |
| 2512 |
var $object = { |
| 2513 |
width: '100%', |
| 2514 |
templateSelection: function templateSelection(category) { |
| 2515 |
return category.text.replace(/ -*\s*/g, '').trim(); |
| 2516 |
} |
| 2517 |
}; |
| 2518 |
if ($source === 'post' || $source === 'product' || $source === 'user') { |
| 2519 |
$object.minimumInputLength = 1; |
| 2520 |
$object.ajax = { |
| 2521 |
url: $config.ajaxurl, |
| 2522 |
dataType: 'json', |
| 2523 |
delay: 250, |
| 2524 |
cache: true, |
| 2525 |
data: function data(params) { |
| 2526 |
return { |
| 2527 |
action: 'merchant_admin_options_select_ajax', |
| 2528 |
nonce: $config.ajaxnonce, |
| 2529 |
term: params.term, |
| 2530 |
source: $source |
| 2531 |
}; |
| 2532 |
}, |
| 2533 |
processResults: function processResults(response, params) { |
| 2534 |
if (response.success) { |
| 2535 |
return { |
| 2536 |
results: response.data |
| 2537 |
}; |
| 2538 |
} |
| 2539 |
return {}; |
| 2540 |
} |
| 2541 |
}; |
| 2542 |
} |
| 2543 |
$select.select2($object); |
| 2544 |
$selectAjax.find('.select2-selection--multiple').append('<span class="merchant-select2-clear"></span>'); |
| 2545 |
}; |
| 2546 |
$('.merchant-module-page-setting-field-select_ajax:not(.template)').each(function () { |
| 2547 |
initSelectAjax($(this)); |
| 2548 |
}); |
| 2549 |
$('.merchant-module-page-settings-responsive').each(function () { |
| 2550 |
var $this = $(this); |
| 2551 |
var $button = $this.find('.merchant-module-page-settings-devices button'); |
| 2552 |
var $container = $this.find('.merchant-module-page-settings-device-container'); |
| 2553 |
$button.on('click', function (e) { |
| 2554 |
e.preventDefault(); |
| 2555 |
var $device = $(this).data('device'); |
| 2556 |
$button.removeClass('active'); |
| 2557 |
$container.removeClass('active'); |
| 2558 |
$(this).addClass('active'); |
| 2559 |
$container.each(function () { |
| 2560 |
if ($(this).data('device') === $device) { |
| 2561 |
$(this).addClass('active'); |
| 2562 |
} |
| 2563 |
}); |
| 2564 |
}); |
| 2565 |
}); |
| 2566 |
$('.merchant-animated-buttons').each(function () { |
| 2567 |
var $button = $(this).find('label'); |
| 2568 |
var $demo = $('.merchant-animation-demo'); |
| 2569 |
var animation; |
| 2570 |
var animationHover; |
| 2571 |
$button.on('click', function () { |
| 2572 |
$demo.removeClass('merchant-animation-' + animation); |
| 2573 |
$demo.removeClass('merchant-animation-' + animationHover); |
| 2574 |
animation = $(this).find('input').attr('value'); |
| 2575 |
setTimeout(function () { |
| 2576 |
$demo.addClass('merchant-animation-' + animation); |
| 2577 |
}, 100); |
| 2578 |
setTimeout(function () { |
| 2579 |
$demo.removeClass('merchant-animation-' + animation); |
| 2580 |
}, 1000); |
| 2581 |
}); |
| 2582 |
$button.mouseover(function () { |
| 2583 |
$demo.removeClass('merchant-animation-' + animation); |
| 2584 |
animationHover = $(this).find('input').attr('value'); |
| 2585 |
$demo.addClass('merchant-animation-' + animationHover); |
| 2586 |
}).mouseout(function () { |
| 2587 |
$demo.removeClass('merchant-animation-' + animationHover); |
| 2588 |
}); |
| 2589 |
}); |
| 2590 |
|
| 2591 |
// Notifications Sidebar |
| 2592 |
var $notificationsSidebar = $('.merchant-notifications-sidebar'); |
| 2593 |
if ($notificationsSidebar.length) { |
| 2594 |
var $notifications = $('.merchant-notifications'); |
| 2595 |
$notifications.on('click', function (e) { |
| 2596 |
e.preventDefault(); |
| 2597 |
var $notification = $(this); |
| 2598 |
var latestNotificationDate = $notificationsSidebar.find('.merchant-notification:first-child .merchant-notification-date').data('raw-date'); |
| 2599 |
$notificationsSidebar.toggleClass('opened'); |
| 2600 |
if (!$notification.hasClass('read')) { |
| 2601 |
$.post(window.merchant.ajax_url, { |
| 2602 |
action: 'merchant_notifications_read', |
| 2603 |
nonce: window.merchant.nonce, |
| 2604 |
latest_notification_date: latestNotificationDate |
| 2605 |
}, function (response) { |
| 2606 |
if (response.success) { |
| 2607 |
setTimeout(function () { |
| 2608 |
$notification.addClass('read'); |
| 2609 |
}, 2000); |
| 2610 |
} |
| 2611 |
}); |
| 2612 |
} |
| 2613 |
}); |
| 2614 |
|
| 2615 |
// Hide fixed changelog items |
| 2616 |
var merchantNotificationsContent = $('.merchant-notification-content'); |
| 2617 |
merchantNotificationsContent.each(function () { |
| 2618 |
var notificationContentItem = $(this); |
| 2619 |
// search for all spans that contains class "changelog-fixed" and then go to the parent li of that span and add class hidden |
| 2620 |
var fixedItems = notificationContentItem.find('span.changelog-fixed'); |
| 2621 |
fixedItems.each(function () { |
| 2622 |
var fixedItem = $(this); |
| 2623 |
fixedItem.closest('li').remove(); |
| 2624 |
}); |
| 2625 |
// now check if all li inside this notificationContentItem are hidden, so we need to look for the parent .merchant-notification and remove it |
| 2626 |
var allItems = notificationContentItem.find('li'); |
| 2627 |
var hiddenItems = notificationContentItem.find('li.hidden'); |
| 2628 |
if (allItems.length === hiddenItems.length) { |
| 2629 |
notificationContentItem.closest('.merchant-notification').remove(); |
| 2630 |
} |
| 2631 |
}); |
| 2632 |
$(window).on('scroll', function () { |
| 2633 |
if (window.pageYOffset > 60) { |
| 2634 |
$notificationsSidebar.addClass('closing'); |
| 2635 |
setTimeout(function () { |
| 2636 |
$notificationsSidebar.removeClass('opened'); |
| 2637 |
$notificationsSidebar.removeClass('closing'); |
| 2638 |
}, 300); |
| 2639 |
} |
| 2640 |
}); |
| 2641 |
|
| 2642 |
// Close Sidebar |
| 2643 |
$('.merchant-notifications-sidebar-close').on('click', function (e) { |
| 2644 |
e.preventDefault(); |
| 2645 |
$notificationsSidebar.addClass('closing'); |
| 2646 |
setTimeout(function () { |
| 2647 |
$notificationsSidebar.removeClass('opened'); |
| 2648 |
$notificationsSidebar.removeClass('closing'); |
| 2649 |
}, 300); |
| 2650 |
}); |
| 2651 |
} |
| 2652 |
|
| 2653 |
// Tabs Navigation. |
| 2654 |
var tabs = $('.merchant-tabs-nav'); |
| 2655 |
if (tabs.length) { |
| 2656 |
tabs.each(function () { |
| 2657 |
var tabWrapperId = $(this).data('tab-wrapper-id'); |
| 2658 |
$(this).find('.merchant-tabs-nav-link').on('click', function (e) { |
| 2659 |
e.preventDefault(); |
| 2660 |
var tabsNavLink = $(this).closest('.merchant-tabs-nav').find('.merchant-tabs-nav-link'), |
| 2661 |
to = $(this).data('tab-to'); |
| 2662 |
|
| 2663 |
// Tab Nav Item |
| 2664 |
tabsNavLink.each(function () { |
| 2665 |
$(this).closest('.merchant-tabs-nav-item').removeClass('active'); |
| 2666 |
}); |
| 2667 |
$(this).closest('.merchant-tabs-nav-item').addClass('active'); |
| 2668 |
|
| 2669 |
// Tab Content |
| 2670 |
var tabContentWrapper = $('.merchant-tab-content-wrapper[data-tab-wrapper-id="' + tabWrapperId + '"]'); |
| 2671 |
tabContentWrapper.find('> .merchant-tab-content').removeClass('active'); |
| 2672 |
tabContentWrapper.find('> .merchant-tab-content[data-tab-content-id="' + to + '"]').addClass('active'); |
| 2673 |
}); |
| 2674 |
}); |
| 2675 |
} |
| 2676 |
|
| 2677 |
// Module Alert |
| 2678 |
var $moduleAlert = $('.merchant-module-alert'); |
| 2679 |
if ($moduleAlert.length) { |
| 2680 |
$moduleAlert.find('.merchant-module-alert-close').on('click', function (e) { |
| 2681 |
e.preventDefault(); |
| 2682 |
$moduleAlert.removeClass('merchant-show'); |
| 2683 |
$(document).off('click.merchant-alert-close'); |
| 2684 |
}); |
| 2685 |
} |
| 2686 |
|
| 2687 |
// AI Prompt Examples Modal |
| 2688 |
var $aiPromptsModal = $('#merchant-ai-prompts-modal'); |
| 2689 |
if ($aiPromptsModal.length) { |
| 2690 |
var $aiPromptsList = $aiPromptsModal.find('.merchant-ai-prompts-modal-list'); |
| 2691 |
var copyHintText = window.merchant.ai_prompt_copy_hint || 'Click to copy'; |
| 2692 |
var copiedLabelText = window.merchant.ai_prompt_copied_label || 'Copied!'; |
| 2693 |
var copyIconSvg = '<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="5" y="5" width="8" height="8" rx="1.5" stroke="currentColor" stroke-width="1.3"/><path d="M3 10V3.5C3 2.67157 3.67157 2 4.5 2H10.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/></svg>'; |
| 2694 |
var checkIconSvg = '<svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.5 4.5L6 12L2.5 8.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'; |
| 2695 |
var copyTextToClipboard = function copyTextToClipboard(text, onCopied) { |
| 2696 |
if (navigator.clipboard && navigator.clipboard.writeText) { |
| 2697 |
navigator.clipboard.writeText(text).then(onCopied); |
| 2698 |
return; |
| 2699 |
} |
| 2700 |
var $temp = $('<textarea readonly></textarea>').val(text).css({ |
| 2701 |
position: 'fixed', |
| 2702 |
top: '-1000px' |
| 2703 |
}); |
| 2704 |
$('body').append($temp); |
| 2705 |
$temp[0].select(); |
| 2706 |
document.execCommand('copy'); |
| 2707 |
$temp.remove(); |
| 2708 |
onCopied(); |
| 2709 |
}; |
| 2710 |
$(document).on('click', '.merchant-ai-badge', function (e) { |
| 2711 |
e.preventDefault(); |
| 2712 |
e.stopPropagation(); |
| 2713 |
var prompts = $(this).data('aiPrompts'); |
| 2714 |
$aiPromptsModal.find('.merchant-ai-prompts-modal-title-text').text($(this).data('aiTitle') || ''); |
| 2715 |
$aiPromptsModal.find('.merchant-ai-prompts-modal-blurb').text($(this).data('aiBlurb') || ''); |
| 2716 |
$aiPromptsList.empty(); |
| 2717 |
if (Array.isArray(prompts)) { |
| 2718 |
prompts.forEach(function (prompt) { |
| 2719 |
var $row = $('<button type="button" class="merchant-ai-prompt-row"></button>').attr('data-prompt', prompt); |
| 2720 |
$row.append($('<span class="merchant-ai-prompt-text"></span>').text(prompt)); |
| 2721 |
$row.append($('<span class="merchant-ai-prompt-icon"></span>').html(copyIconSvg)); |
| 2722 |
$row.attr('aria-label', copyHintText); |
| 2723 |
$aiPromptsList.append($row); |
| 2724 |
}); |
| 2725 |
} |
| 2726 |
$aiPromptsModal.addClass('merchant-show'); |
| 2727 |
var $badgeIcon = $aiPromptsModal.find('.merchant-ai-prompts-modal-badge svg'); |
| 2728 |
$badgeIcon.removeClass('merchant-ai-badge-spin'); |
| 2729 |
void $badgeIcon[0].offsetWidth; |
| 2730 |
$badgeIcon.addClass('merchant-ai-badge-spin'); |
| 2731 |
}); |
| 2732 |
$(document).on('keydown', '.merchant-ai-badge', function (e) { |
| 2733 |
if (e.key === 'Enter' || e.key === ' ') { |
| 2734 |
e.preventDefault(); |
| 2735 |
$(this).trigger('click'); |
| 2736 |
} |
| 2737 |
}); |
| 2738 |
$aiPromptsList.on('click', '.merchant-ai-prompt-row', function () { |
| 2739 |
var $row = $(this); |
| 2740 |
if ($row.hasClass('is-copied')) { |
| 2741 |
return; |
| 2742 |
} |
| 2743 |
copyTextToClipboard($row.attr('data-prompt'), function () { |
| 2744 |
$row.addClass('is-copied').attr('aria-label', copiedLabelText); |
| 2745 |
$row.find('.merchant-ai-prompt-icon').html(checkIconSvg); |
| 2746 |
setTimeout(function () { |
| 2747 |
$row.removeClass('is-copied').attr('aria-label', copyHintText); |
| 2748 |
$row.find('.merchant-ai-prompt-icon').html(copyIconSvg); |
| 2749 |
}, 1600); |
| 2750 |
}); |
| 2751 |
}); |
| 2752 |
var closeAiPromptsModal = function closeAiPromptsModal() { |
| 2753 |
$aiPromptsModal.removeClass('merchant-show'); |
| 2754 |
$aiPromptsModal.find('.merchant-ai-prompts-modal-badge svg').removeClass('merchant-ai-badge-spin'); |
| 2755 |
}; |
| 2756 |
$(document).on('click', '.merchant-ai-prompts-modal-close', function () { |
| 2757 |
closeAiPromptsModal(); |
| 2758 |
}); |
| 2759 |
$aiPromptsModal.on('click', function (e) { |
| 2760 |
if ($(e.target).is($aiPromptsModal)) { |
| 2761 |
closeAiPromptsModal(); |
| 2762 |
} |
| 2763 |
}); |
| 2764 |
$(document).on('keydown', function (e) { |
| 2765 |
if (e.key === 'Escape' && $aiPromptsModal.hasClass('merchant-show')) { |
| 2766 |
closeAiPromptsModal(); |
| 2767 |
} |
| 2768 |
}); |
| 2769 |
} |
| 2770 |
}); |
| 2771 |
|
| 2772 |
/** |
| 2773 |
* Check if a string is numeric. |
| 2774 |
* |
| 2775 |
* @param str the string to check |
| 2776 |
* |
| 2777 |
* @returns {boolean} true if numeric, false otherwise |
| 2778 |
*/ |
| 2779 |
function isNumeric(str) { |
| 2780 |
if (typeof str != "string") return false; // we only process strings! |
| 2781 |
return !isNaN(str) && |
| 2782 |
// use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)... |
| 2783 |
!isNaN(parseFloat(str)); // ...and ensure strings of whitespace fail |
| 2784 |
} |
| 2785 |
|
| 2786 |
/** |
| 2787 |
* Evaluate conditional fields. |
| 2788 |
* |
| 2789 |
* @param conditions the array of conditions |
| 2790 |
* @param field the field that is being evaluated |
| 2791 |
* |
| 2792 |
* @returns {boolean} |
| 2793 |
*/ |
| 2794 |
function evaluateConditions(conditions) { |
| 2795 |
var field = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; |
| 2796 |
var passed = false; |
| 2797 |
if ('relation' in conditions) { |
| 2798 |
//loop through terms |
| 2799 |
var relation = conditions.relation.toUpperCase(); |
| 2800 |
// lowercase relation |
| 2801 |
if (relation === 'OR') { |
| 2802 |
for (var i = 0; i < conditions.terms.length; i++) { |
| 2803 |
var term = conditions.terms[i]; |
| 2804 |
passed = evaluateConditions(term, field); |
| 2805 |
if (passed) { |
| 2806 |
return true; |
| 2807 |
} |
| 2808 |
} |
| 2809 |
} else if (relation === 'AND') { |
| 2810 |
var n = 0; |
| 2811 |
for (var _i = 0; _i < conditions.terms.length; _i++) { |
| 2812 |
// check if inner terms are passed |
| 2813 |
var _term = conditions.terms[_i]; |
| 2814 |
if (evaluateConditions(_term, field)) { |
| 2815 |
n++; |
| 2816 |
} |
| 2817 |
} |
| 2818 |
if (n === conditions.terms.length) { |
| 2819 |
passed = true; |
| 2820 |
} |
| 2821 |
} |
| 2822 |
} else { |
| 2823 |
var condition = ''; |
| 2824 |
if ('terms' in conditions) { |
| 2825 |
condition = conditions.terms[0]; |
| 2826 |
} else { |
| 2827 |
condition = conditions; |
| 2828 |
} |
| 2829 |
var $target = $('input[name="merchant[' + condition.field + ']"],select[name="merchant[' + condition.field + ']"]'); |
| 2830 |
if (!$target.length) { |
| 2831 |
// check if inside flexible content |
| 2832 |
var flexibleContentParent = field.closest('.layout-body'); |
| 2833 |
if (flexibleContentParent.length > 0) { |
| 2834 |
$target = flexibleContentParent.find('.merchant-field-' + condition.field).find('input, select'); |
| 2835 |
} |
| 2836 |
} |
| 2837 |
if (!$target.length) { |
| 2838 |
// Maybe the field is a multiple field |
| 2839 |
$target = $('input[name="merchant[' + condition.field + '][]"],select[name="merchant[' + condition.field + '][]"]'); |
| 2840 |
} |
| 2841 |
if (!$target.length) { |
| 2842 |
// Maybe the field is inside fields group |
| 2843 |
$target = $('.merchant-group-fields-container').find('.merchant-field-' + condition.field + ' input[name*="' + condition.field + '"],.merchant-field-' + condition.field + ' select[name*="' + condition.field + '"]'); |
| 2844 |
} |
| 2845 |
var value = $target.val(); |
| 2846 |
if ($target.attr('type') === 'checkbox') { |
| 2847 |
value = $target.is(':checked'); |
| 2848 |
} |
| 2849 |
if ($target.attr('type') === 'radio') { |
| 2850 |
value = $target.filter(':checked').val(); |
| 2851 |
} |
| 2852 |
|
| 2853 |
// check if the field is multiple checkbox |
| 2854 |
if ($target.attr('type') === 'checkbox' && $target.length > 1) { |
| 2855 |
value = []; |
| 2856 |
$target.each(function () { |
| 2857 |
if ($(this).is(':checked')) { |
| 2858 |
value.push($(this).val()); |
| 2859 |
} |
| 2860 |
}); |
| 2861 |
} |
| 2862 |
|
| 2863 |
// cast value as string if numeric |
| 2864 |
if (isNumeric(value)) { |
| 2865 |
value = Number(value); |
| 2866 |
} |
| 2867 |
|
| 2868 |
// check if is array condition.value |
| 2869 |
if (Array.isArray(condition.value)) { |
| 2870 |
condition.value = condition.value.map(function (item) { |
| 2871 |
if (isNumeric(item)) { |
| 2872 |
return Number(item); |
| 2873 |
} |
| 2874 |
return item; |
| 2875 |
}); |
| 2876 |
} |
| 2877 |
if (condition.operator === '===' && value === condition.value) { |
| 2878 |
passed = true; |
| 2879 |
} else if (condition.operator === '!==' && value !== condition.value) { |
| 2880 |
passed = true; |
| 2881 |
} else if (condition.operator === '>' && value > condition.value) { |
| 2882 |
passed = true; |
| 2883 |
} else if (condition.operator === '<' && value < condition.value) { |
| 2884 |
passed = true; |
| 2885 |
} else if (condition.operator === '>=' && value >= condition.value) { |
| 2886 |
passed = true; |
| 2887 |
} else if (condition.operator === '<=' && value <= condition.value) { |
| 2888 |
passed = true; |
| 2889 |
} else if (condition.operator === 'in' && condition.value.includes(value)) { |
| 2890 |
passed = true; |
| 2891 |
} else if (condition.operator === '!in' && !condition.value.includes(value)) { |
| 2892 |
passed = true; |
| 2893 |
} else if (condition.operator === 'contains' && Array.isArray(value) && value.includes(condition.value)) { |
| 2894 |
passed = true; |
| 2895 |
} else if (condition.operator === '!contains' && Array.isArray(value) && !value.includes(condition.value)) { |
| 2896 |
passed = true; |
| 2897 |
} |
| 2898 |
} |
| 2899 |
return passed; |
| 2900 |
} |
| 2901 |
})(jQuery, window, document); |
| 2902 |
|
| 2903 |
// Extend jQuery to add getPath func to get accurate dynamic selector to an element. |
| 2904 |
jQuery.fn.extend({ |
| 2905 |
getPath: function getPath() { |
| 2906 |
var pathes = []; |
| 2907 |
this.each(function (index, element) { |
| 2908 |
var path, |
| 2909 |
$node = jQuery(element); |
| 2910 |
while ($node.length) { |
| 2911 |
var realNode = $node.get(0), |
| 2912 |
name = realNode.localName; |
| 2913 |
if (!name) { |
| 2914 |
break; |
| 2915 |
} |
| 2916 |
name = name.toLowerCase(); |
| 2917 |
var parent = $node.parent(); |
| 2918 |
var sameTagSiblings = parent.children(name); |
| 2919 |
if (sameTagSiblings.length > 1) { |
| 2920 |
var allSiblings = parent.children(); |
| 2921 |
var index = allSiblings.index(realNode) + 1; |
| 2922 |
if (index > 0) { |
| 2923 |
name += ':nth-child(' + index + ')'; |
| 2924 |
} |
| 2925 |
} |
| 2926 |
path = name + (path ? ' > ' + path : ''); |
| 2927 |
$node = parent; |
| 2928 |
} |
| 2929 |
pathes.push(path); |
| 2930 |
}); |
| 2931 |
return pathes.join(','); |
| 2932 |
} |
| 2933 |
}); |