| 1 |
/* eslint-disable prefer-object-spread */ |
| 2 |
/** |
| 3 |
* @member {Object} wcpn |
| 4 |
* @property {Object} wcpn.actions |
| 5 |
* @property {{export: String, add_shipments: String, add_return: String, get_labels: String, modal_dialog: String}} |
| 6 |
* wcpn.actions |
| 7 |
* @property {String} wcpn.api_url - The API Url we use in PostNL requests. |
| 8 |
* @property {String} wcpn.ajax_url |
| 9 |
* @property {String} wcpn.ask_for_print_position |
| 10 |
* @property {Object} wcpn.bulk_actions |
| 11 |
* @property {String} wcpn.download_display |
| 12 |
* @property {String} wcpn.nonce |
| 13 |
* @property {Object.<String, String>} wcpn.strings |
| 14 |
*/ |
| 15 |
|
| 16 |
/** |
| 17 |
* @typedef {Object} Dependency |
| 18 |
* @property {String} name |
| 19 |
* @property {Condition} condition |
| 20 |
* @property {HTMLInputElement} node |
| 21 |
*/ |
| 22 |
|
| 23 |
/** |
| 24 |
* @typedef {Object} Condition |
| 25 |
* @property {Object<String,*>} parents |
| 26 |
* @property {String|Number} set_value |
| 27 |
*/ |
| 28 |
|
| 29 |
/** |
| 30 |
* Object.assign() polyfill. |
| 31 |
* |
| 32 |
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill |
| 33 |
*/ |
| 34 |
if (typeof Object.assign !== 'function') { |
| 35 |
/* Must be writable: true, enumerable: false, configurable: true */ |
| 36 |
Object.defineProperty(Object, 'assign', { |
| 37 |
value: function assign(target) { |
| 38 |
if (target === null || target === undefined) { |
| 39 |
throw new TypeError('Cannot convert undefined or null to object'); |
| 40 |
} |
| 41 |
|
| 42 |
var to = Object(target); |
| 43 |
|
| 44 |
for (var index = 1; index < arguments.length; index++) { |
| 45 |
var nextSource = arguments[index]; |
| 46 |
|
| 47 |
if (nextSource !== null && nextSource !== undefined) { |
| 48 |
for (var nextKey in nextSource) { |
| 49 |
/* Avoid bugs when hasOwnProperty is shadowed */ |
| 50 |
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { |
| 51 |
to[nextKey] = nextSource[nextKey]; |
| 52 |
} |
| 53 |
} |
| 54 |
} |
| 55 |
} |
| 56 |
return to; |
| 57 |
}, |
| 58 |
writable: true, |
| 59 |
configurable: true, |
| 60 |
}); |
| 61 |
} |
| 62 |
|
| 63 |
/** |
| 64 |
* Array.find() polyfill. |
| 65 |
* |
| 66 |
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find#Polyfill |
| 67 |
*/ |
| 68 |
if (!Array.prototype.find) { |
| 69 |
Object.defineProperty(Array.prototype, 'find', { |
| 70 |
value: function(predicate) { |
| 71 |
// 1. Let O be ? ToObject(this value). |
| 72 |
if (this == null) { |
| 73 |
throw TypeError('"this" is null or not defined'); |
| 74 |
} |
| 75 |
|
| 76 |
var o = Object(this); |
| 77 |
|
| 78 |
// 2. Let len be ? ToLength(? Get(O, "length")). |
| 79 |
var len = o.length >>> 0; |
| 80 |
|
| 81 |
// 3. If IsCallable(predicate) is false, throw a TypeError exception. |
| 82 |
if (typeof predicate !== 'function') { |
| 83 |
throw TypeError('predicate must be a function'); |
| 84 |
} |
| 85 |
|
| 86 |
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined. |
| 87 |
var thisArg = arguments[1]; |
| 88 |
|
| 89 |
// 5. Let k be 0. |
| 90 |
var k = 0; |
| 91 |
|
| 92 |
// 6. Repeat, while k < len |
| 93 |
while (k < len) { |
| 94 |
/* |
| 95 |
* a. Let Pk be ! ToString(k). |
| 96 |
* b. Let kValue be ? Get(O, Pk). |
| 97 |
* c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). |
| 98 |
* d. If testResult is true, return kValue. |
| 99 |
*/ |
| 100 |
var kValue = o[k]; |
| 101 |
if (predicate.call(thisArg, kValue, k, o)) { |
| 102 |
return kValue; |
| 103 |
} |
| 104 |
// e. Increase k by 1. |
| 105 |
k++; |
| 106 |
} |
| 107 |
|
| 108 |
// 7. Return undefined. |
| 109 |
return undefined; |
| 110 |
}, |
| 111 |
configurable: true, |
| 112 |
writable: true, |
| 113 |
}); |
| 114 |
} |
| 115 |
|
| 116 |
/** |
| 117 |
* Object.values() polyfill. |
| 118 |
* |
| 119 |
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values#Polyfill |
| 120 |
*/ |
| 121 |
if (!Object.values) { |
| 122 |
Object.values = function(obj) { |
| 123 |
var values = []; |
| 124 |
|
| 125 |
for (var i in obj) { |
| 126 |
if (obj.hasOwnProperty(i)) { |
| 127 |
values.push(obj[i]); |
| 128 |
} |
| 129 |
} |
| 130 |
|
| 131 |
return values; |
| 132 |
}; |
| 133 |
} |
| 134 |
|
| 135 |
/* eslint-disable-next-line max-lines-per-function */ |
| 136 |
jQuery(function($) { |
| 137 |
/** |
| 138 |
* @type {Boolean} |
| 139 |
*/ |
| 140 |
var askForPrintPosition = Boolean(parseInt(wcpn.ask_for_print_position)); |
| 141 |
|
| 142 |
var skeletonHtml |
| 143 |
= '<table class="wcpn__skeleton-loader">' |
| 144 |
+ '<tr><td><div></div></td><td><div></div></td></tr>'.repeat(5) |
| 145 |
+ '</table>'; |
| 146 |
|
| 147 |
var selectors = { |
| 148 |
bulkSpinner: '.wcpn__bulk-spinner', |
| 149 |
notice: '.wcpn__notice', |
| 150 |
offsetDialog: '.wcpn__offset-dialog', |
| 151 |
offsetDialogButton: '.wcpn__offset-dialog__button', |
| 152 |
offsetDialogClose: '.wcpn__offset-dialog__close', |
| 153 |
offsetDialogInputOffset: '.wcpn__offset-dialog__offset', |
| 154 |
orderAction: '.wcpn__action', |
| 155 |
orderActionImage: '.wcpn__action__img', |
| 156 |
printQueue: '.wcpn__print-queue', |
| 157 |
printQueueOffset: '.wcpn__print-queue__offset', |
| 158 |
shipmentOptions: '.wcpn__shipment-options', |
| 159 |
shipmentOptionsDialog: '.wcpn__shipment-options-dialog', |
| 160 |
shipmentOptionsSaveButton: '.wcpn__shipment-options__save', |
| 161 |
shipmentOptionsShowButton: '.wcpn__shipment-options__show', |
| 162 |
shipmentSettingsWrapper: '.wcpn__shipment-settings-wrapper', |
| 163 |
shipmentSummaryList: '.wcpn__shipment-summary__list', |
| 164 |
showShipmentSummaryList: '.wcpn__shipment-summary__show', |
| 165 |
spinner: '.wcpn__spinner', |
| 166 |
toggle: '.wcpn__toggle', |
| 167 |
tipTipHolder: '#tiptip_holder', |
| 168 |
tipTipContent: '#tiptip_content', |
| 169 |
}; |
| 170 |
|
| 171 |
var spinner = { |
| 172 |
loading: 'loading', |
| 173 |
success: 'success', |
| 174 |
failed: 'failed', |
| 175 |
}; |
| 176 |
|
| 177 |
addListeners(); |
| 178 |
runTriggers(); |
| 179 |
addDependencies(); |
| 180 |
printQueuedLabels(); |
| 181 |
|
| 182 |
var timeoutAfterRequest = 200; |
| 183 |
var baseEasing = 300; |
| 184 |
|
| 185 |
/** |
| 186 |
* Add event listeners. |
| 187 |
*/ |
| 188 |
function addListeners() { |
| 189 |
/** |
| 190 |
* Click offset dialog button (single export). |
| 191 |
*/ |
| 192 |
$(selectors.offsetDialog + ' button').click(printOrder); |
| 193 |
|
| 194 |
$(selectors.offsetDialogClose).click(hideOffsetDialog); |
| 195 |
|
| 196 |
/** |
| 197 |
* Show and enable options when clicked. |
| 198 |
*/ |
| 199 |
$(selectors.shipmentOptionsShowButton).click(showShipmentOptionsForm); |
| 200 |
|
| 201 |
/** |
| 202 |
* Show summary when clicked. |
| 203 |
*/ |
| 204 |
$(selectors.showShipmentSummaryList).click(showShipmentSummaryList); |
| 205 |
|
| 206 |
/** |
| 207 |
* Bulk actions. |
| 208 |
*/ |
| 209 |
$('#doaction, #doaction2').click(doBulkAction); |
| 210 |
|
| 211 |
/** |
| 212 |
* Add offset dialog when address labels option is selected. |
| 213 |
*/ |
| 214 |
$('select[name=\'action\'], select[name=\'action2\']').change(showBulkOffsetDialog); |
| 215 |
|
| 216 |
/** |
| 217 |
* Single actions click. The .wc_actions .single_wc_actions for support wc > 3.3.0. |
| 218 |
*/ |
| 219 |
$(selectors.orderAction).click(onActionClick); |
| 220 |
|
| 221 |
$(window).bind('tb_unload', onThickBoxUnload); |
| 222 |
|
| 223 |
addToggleListeners(); |
| 224 |
} |
| 225 |
|
| 226 |
/** |
| 227 |
* Run the things that need to be done on load. |
| 228 |
*/ |
| 229 |
function runTriggers() { |
| 230 |
/* init options on settings page and in bulk form */ |
| 231 |
$('#wcpn_settings :input, .wcpn__bulk-options :input').change(); |
| 232 |
|
| 233 |
/** |
| 234 |
* Move the shipment options form and the shipment summary from the actions column to the shipping address column. |
| 235 |
* |
| 236 |
* @see includes/admin/class-wcpn-admin.php:49 |
| 237 |
*/ |
| 238 |
$(selectors.shipmentSettingsWrapper).each(function() { |
| 239 |
var shippingAddressColumn = $(this) |
| 240 |
.closest('tr') |
| 241 |
.find('td.shipping_address'); |
| 242 |
|
| 243 |
$(this).appendTo(shippingAddressColumn); |
| 244 |
$(this).show(); |
| 245 |
}); |
| 246 |
} |
| 247 |
|
| 248 |
/** |
| 249 |
* Add dependencies for form elements with conditions. |
| 250 |
*/ |
| 251 |
function addDependencies() { |
| 252 |
/** |
| 253 |
* Get all nodes with a data-conditions attribute. |
| 254 |
*/ |
| 255 |
var nodesWithConditions = document.querySelectorAll('[data-conditions]'); |
| 256 |
|
| 257 |
/** |
| 258 |
* Dependency object. |
| 259 |
* |
| 260 |
* @type {Object.<String, Dependency[]>} |
| 261 |
*/ |
| 262 |
var dependencies = {}; |
| 263 |
|
| 264 |
/** |
| 265 |
* Loop through the classes to create a dependency like this: { [parent]: [{condition: Condition, node: Node}] }. |
| 266 |
*/ |
| 267 |
nodesWithConditions.forEach(function(node) { |
| 268 |
var conditions = node.getAttribute('data-conditions'); |
| 269 |
conditions = JSON.parse(conditions); |
| 270 |
|
| 271 |
conditions |
| 272 |
.forEach(function(condition) { |
| 273 |
Object |
| 274 |
.keys(condition.parents) |
| 275 |
.forEach(function(parent) { |
| 276 |
/** |
| 277 |
* @type {Dependency} |
| 278 |
*/ |
| 279 |
var data = { |
| 280 |
condition: condition, |
| 281 |
node: node, |
| 282 |
}; |
| 283 |
|
| 284 |
if (dependencies.hasOwnProperty(parent)) { |
| 285 |
dependencies[parent].push(data); |
| 286 |
} else { |
| 287 |
// Or create the list with the node inside it |
| 288 |
dependencies[parent] = [data]; |
| 289 |
} |
| 290 |
}); |
| 291 |
}); |
| 292 |
}); |
| 293 |
|
| 294 |
createDependencies(dependencies); |
| 295 |
} |
| 296 |
|
| 297 |
/** |
| 298 |
* Loops through dependants and collects changes that need to be done in queue. |
| 299 |
* |
| 300 |
* @param {Object<String, Dependency[]>} dependencies |
| 301 |
* @param {HTMLInputElement|Node} input |
| 302 |
* @param {?Number} level |
| 303 |
* @param {?Object[]} queue |
| 304 |
* |
| 305 |
* @returns {Object[]} - Queue. |
| 306 |
*/ |
| 307 |
function checkDependenciesRecursively(dependencies, input, level, queue) { |
| 308 |
if (level >= 20) { |
| 309 |
throw new Error('Depth limit of ' + level + ' exceeded (probably an infinite loop)'); |
| 310 |
} |
| 311 |
|
| 312 |
if (!dependencies.hasOwnProperty(input.name)) { |
| 313 |
return queue; |
| 314 |
} |
| 315 |
|
| 316 |
dependencies[input.name] |
| 317 |
.forEach(function(dependency) { |
| 318 |
var data = handleDependency(dependency, level); |
| 319 |
|
| 320 |
queue.push({ |
| 321 |
name: dependency.node.name.replace(/postnl_options\[\d+\]/, ''), |
| 322 |
parent: input, |
| 323 |
node: dependency.node, |
| 324 |
type: dependency.condition.type, |
| 325 |
setValue: data.setValue, |
| 326 |
toggle: data.toggle, |
| 327 |
}); |
| 328 |
|
| 329 |
if (dependencies.hasOwnProperty(dependency.node.name)) { |
| 330 |
var dependantInput = document.querySelector('[name="' + dependency.node.name + '"]'); |
| 331 |
|
| 332 |
queue = checkDependenciesRecursively(dependencies, dependantInput, level + 1, queue); |
| 333 |
} |
| 334 |
}); |
| 335 |
|
| 336 |
return queue; |
| 337 |
} |
| 338 |
|
| 339 |
/** |
| 340 |
* Executes a set of changes on an element and its parent. |
| 341 |
* |
| 342 |
* @param {Object} data |
| 343 |
* @param {HTMLInputElement} data.node |
| 344 |
* @param {HTMLInputElement} data.parent |
| 345 |
* @param {*} data.setValue |
| 346 |
* @param {Boolean} data.toggle |
| 347 |
* @param {String} data.type |
| 348 |
* @param {Number} easing |
| 349 |
*/ |
| 350 |
function toggleElement(data, easing) { |
| 351 |
var node = data.node; |
| 352 |
var setValue = data.setValue; |
| 353 |
var toggle = data.toggle; |
| 354 |
var elementContainer = $(node).closest('tr'); |
| 355 |
|
| 356 |
switch (data.type) { |
| 357 |
case 'show': |
| 358 |
elementContainer[toggle ? 'hide' : 'show'](easing); |
| 359 |
break; |
| 360 |
case 'readonly': |
| 361 |
$(elementContainer).attr('data-readonly', toggle); |
| 362 |
$(node).prop('readonly', toggle); |
| 363 |
break; |
| 364 |
case 'disable': |
| 365 |
$(elementContainer).attr('data-disabled', toggle); |
| 366 |
$(node).prop('disabled', toggle); |
| 367 |
break; |
| 368 |
} |
| 369 |
|
| 370 |
if (toggle && setValue) { |
| 371 |
node.value = setValue; |
| 372 |
node.dispatchEvent(new Event('change')); |
| 373 |
// Sync toggles here as well as in the createDependencies because not all inputs listen to the change event. |
| 374 |
syncToggle(node); |
| 375 |
} |
| 376 |
|
| 377 |
data.parent.setAttribute('data-toggled', toggle.toString()); |
| 378 |
node.setAttribute('data-toggled', toggle.toString()); |
| 379 |
} |
| 380 |
|
| 381 |
function toggleElement2(data, easing) { |
| 382 |
var node = data.node; |
| 383 |
var setValue = data.setValue; |
| 384 |
// var toggle = data.toggle; |
| 385 |
var elementContainer = $(node).closest('tr'); |
| 386 |
|
| 387 |
data.changes.forEach(function(change) { |
| 388 |
var toggle = change.toggle; |
| 389 |
var type = change.type; |
| 390 |
|
| 391 |
switch (type) { |
| 392 |
case 'show': |
| 393 |
elementContainer[toggle ? 'hide' : 'show'](easing); |
| 394 |
data.parent.setAttribute('data-toggled', toggle.toString()); |
| 395 |
node.setAttribute('data-toggled', toggle.toString()); |
| 396 |
break; |
| 397 |
case 'readonly': |
| 398 |
$(elementContainer).attr('data-readonly', toggle); |
| 399 |
$(node).prop('readonly', toggle); |
| 400 |
break; |
| 401 |
case 'disable': |
| 402 |
$(elementContainer).attr('data-disabled', toggle); |
| 403 |
$(node).prop('disabled', toggle); |
| 404 |
break; |
| 405 |
} |
| 406 |
}) |
| 407 |
|
| 408 |
// Hacky use of vars here |
| 409 |
if (toggle && setValue) { |
| 410 |
node.value = setValue; |
| 411 |
node.dispatchEvent(new Event('change')); |
| 412 |
// Sync toggles here as well as in the createDependencies because not all inputs listen to the change event. |
| 413 |
syncToggle(node); |
| 414 |
} |
| 415 |
} |
| 416 |
|
| 417 |
|
| 418 |
/** |
| 419 |
* Sync the appearance of toggle elements with the value their hidden input. |
| 420 |
* |
| 421 |
* @param {EventTarget} target |
| 422 |
*/ |
| 423 |
function syncToggle(target) { |
| 424 |
var element = $(target); |
| 425 |
var toggle = element.siblings('.woocommerce-input-toggle'); |
| 426 |
|
| 427 |
if (element.attr('data-type') !== 'toggle') { |
| 428 |
return; |
| 429 |
} |
| 430 |
|
| 431 |
var mismatch0 = element.val() === '0' && toggle.hasClass('woocommerce-input-toggle--enabled'); |
| 432 |
var mismatch1 = element.val() === '1' && toggle.hasClass('woocommerce-input-toggle--disabled'); |
| 433 |
|
| 434 |
if (mismatch0 || mismatch1) { |
| 435 |
toggle.toggleClass('woocommerce-input-toggle--disabled'); |
| 436 |
toggle.toggleClass('woocommerce-input-toggle--enabled'); |
| 437 |
} |
| 438 |
} |
| 439 |
|
| 440 |
/** |
| 441 |
* Handle showing and hiding of settings. |
| 442 |
* |
| 443 |
* @param {Object<String, Dependency[]>} dependencies - Dependency names and all the nodes that depend on them. |
| 444 |
*/ |
| 445 |
function createDependencies(dependencies) { |
| 446 |
Object |
| 447 |
.keys(dependencies) |
| 448 |
.forEach(function(name) { |
| 449 |
var inputSelector = '[name="' + name + '"]'; |
| 450 |
var input = document.querySelector(inputSelector); |
| 451 |
|
| 452 |
if (!input) { |
| 453 |
// eslint-disable-next-line no-console |
| 454 |
console.error('Element ' + inputSelector + ' not found.'); |
| 455 |
return; |
| 456 |
} |
| 457 |
|
| 458 |
/** |
| 459 |
* Loop through all the dependencies. |
| 460 |
* |
| 461 |
* @param {Event|null} event - Event. |
| 462 |
* @param {Number} easing - Amount of easing. |
| 463 |
*/ |
| 464 |
function handle(event, easing) { |
| 465 |
if (easing === undefined) { |
| 466 |
easing = baseEasing; |
| 467 |
} |
| 468 |
|
| 469 |
if (event) { |
| 470 |
syncToggle(event.target); |
| 471 |
} |
| 472 |
|
| 473 |
var updateQueue = checkDependenciesRecursively(dependencies, input, 1, []); |
| 474 |
|
| 475 |
// Executes all needed updates gathered by checkDependenciesRecursively. |
| 476 |
updateQueue.forEach(function(dependency) { |
| 477 |
toggleElement(dependency, easing); |
| 478 |
}); |
| 479 |
} |
| 480 |
|
| 481 |
input.addEventListener('change', handle); |
| 482 |
|
| 483 |
// Do this on load too. |
| 484 |
handle(null, 0); |
| 485 |
}); |
| 486 |
} |
| 487 |
|
| 488 |
/** |
| 489 |
* Determines if an element should be toggled and if its value should change by checking all parent elements' values. |
| 490 |
* |
| 491 |
* @param {Dependency} dependency |
| 492 |
* @param {Number} level |
| 493 |
* |
| 494 |
* @returns {Object} |
| 495 |
*/ |
| 496 |
function handleDependency(dependency, level) { |
| 497 |
var parents = dependency.condition.parents; |
| 498 |
var setValue = dependency.condition.set_value || null; |
| 499 |
var toggle = false; |
| 500 |
|
| 501 |
Object |
| 502 |
.keys(parents) |
| 503 |
.forEach(function(parent) { |
| 504 |
var parentInput = document.getElementsByName(parent)[0]; |
| 505 |
var localToggle; |
| 506 |
var wantedValue = parents[parent] || '1'; |
| 507 |
|
| 508 |
var parentToggled = parentInput.getAttribute('data-toggled') === 'true'; |
| 509 |
var dependantToggled = dependency.node.getAttribute('data-toggled') === 'true'; |
| 510 |
|
| 511 |
if (parentToggled && !dependantToggled && level > 1) { |
| 512 |
localToggle = true; |
| 513 |
} else if (typeof wantedValue === 'string') { |
| 514 |
localToggle = parentInput.value !== wantedValue; |
| 515 |
} else { |
| 516 |
localToggle = wantedValue.indexOf(parentInput.value) === -1; |
| 517 |
} |
| 518 |
|
| 519 |
if (localToggle === true) { |
| 520 |
toggle = true; |
| 521 |
} |
| 522 |
}); |
| 523 |
|
| 524 |
return { |
| 525 |
toggle: toggle, |
| 526 |
setValue: setValue, |
| 527 |
}; |
| 528 |
} |
| 529 |
|
| 530 |
/** |
| 531 |
* Add event listeners to all toggle elements. |
| 532 |
*/ |
| 533 |
function addToggleListeners() { |
| 534 |
$(selectors.toggle).each(function() { |
| 535 |
$(this).on('click', handleToggle); |
| 536 |
}); |
| 537 |
} |
| 538 |
|
| 539 |
/** |
| 540 |
* Print queued labels. |
| 541 |
*/ |
| 542 |
function printQueuedLabels() { |
| 543 |
var printData = $(selectors.printQueue).val(); |
| 544 |
|
| 545 |
if (printData) { |
| 546 |
printLabel(JSON.parse(printData)); |
| 547 |
} |
| 548 |
} |
| 549 |
|
| 550 |
/** |
| 551 |
* Show the shipment options form on the Woo Orders page. |
| 552 |
* |
| 553 |
* @param {Event} event - Click event. |
| 554 |
*/ |
| 555 |
function showShipmentOptionsForm(event) { |
| 556 |
event.preventDefault(); |
| 557 |
var button = $(this); |
| 558 |
var orderId = button.data('order-id'); |
| 559 |
|
| 560 |
var form = $(selectors.shipmentOptionsDialog); |
| 561 |
var isSameAsLast = form.data('order-id') === orderId; |
| 562 |
var isVisible = form.is(':visible'); |
| 563 |
|
| 564 |
if (isVisible) { |
| 565 |
document.removeEventListener('click', hideShipmentOptionsForm); |
| 566 |
|
| 567 |
// Close form on second "details" click |
| 568 |
if (isSameAsLast) { |
| 569 |
form.slideUp(100); |
| 570 |
return; |
| 571 |
} |
| 572 |
|
| 573 |
// Hide other opened form before opening new one |
| 574 |
form.hide(0); |
| 575 |
} |
| 576 |
|
| 577 |
// Set the position for the dialog to be under the clicked "Details" link. |
| 578 |
var position = button.offset(); |
| 579 |
position.top -= button.height(); |
| 580 |
form.css(position); |
| 581 |
|
| 582 |
// Set the data-order-id attribute on the dialog to keep track of which dialog was last opened. |
| 583 |
form.data('order-id', orderId); |
| 584 |
|
| 585 |
doRequest.bind(this)({ |
| 586 |
url: wcpn.ajax_url, |
| 587 |
data: { |
| 588 |
action: 'wcpn_get_shipment_options', |
| 589 |
orderId: orderId, |
| 590 |
security: wcpn.nonce, |
| 591 |
}, |
| 592 |
onStart: function() { |
| 593 |
form.html(skeletonHtml); |
| 594 |
form.slideDown(100); |
| 595 |
}, |
| 596 |
|
| 597 |
/** |
| 598 |
* Show the correct data in the form and add event listeners for handling saving and clicking outside the form. |
| 599 |
* |
| 600 |
* @param {String} response - Html to put in the form. |
| 601 |
*/ |
| 602 |
afterDone: function(response) { |
| 603 |
form.html(response); |
| 604 |
|
| 605 |
addDependencies(); |
| 606 |
addToggleListeners(); |
| 607 |
|
| 608 |
$(selectors.shipmentOptionsSaveButton).on('click', saveShipmentOptions); |
| 609 |
document.addEventListener('click', hideShipmentOptionsForm); |
| 610 |
// Trigger WooCommerce's event to init any tipTips. |
| 611 |
document.body.dispatchEvent(new Event('init_tooltips')); |
| 612 |
}, |
| 613 |
afterFail: function() { |
| 614 |
form.slideUp(100); |
| 615 |
}, |
| 616 |
}); |
| 617 |
} |
| 618 |
|
| 619 |
/** |
| 620 |
* @param {Node} element |
| 621 |
* @param {String} state |
| 622 |
*/ |
| 623 |
function setSpinner(element, state) { |
| 624 |
var baseSelector = selectors.spinner.replace('.', ''); |
| 625 |
var spinner = $(element).find(selectors.spinner); |
| 626 |
|
| 627 |
if (state) { |
| 628 |
spinner |
| 629 |
.removeClass() |
| 630 |
.addClass(baseSelector) |
| 631 |
.addClass(baseSelector + '--' + state) |
| 632 |
.show(); |
| 633 |
} else { |
| 634 |
spinner |
| 635 |
.removeClass() |
| 636 |
.addClass(baseSelector) |
| 637 |
.hide(); |
| 638 |
} |
| 639 |
} |
| 640 |
|
| 641 |
/** |
| 642 |
* Save the shipment options in the bulk form. |
| 643 |
*/ |
| 644 |
function saveShipmentOptions() { |
| 645 |
var form = $(selectors.shipmentOptionsDialog); |
| 646 |
|
| 647 |
doRequest.bind(this)({ |
| 648 |
url: wcpn.ajax_url, |
| 649 |
data: { |
| 650 |
action: 'wcpn_save_shipment_options', |
| 651 |
form_data: form.find(':input').serialize(), |
| 652 |
security: wcpn.nonce, |
| 653 |
}, |
| 654 |
afterDone: function() { |
| 655 |
setTimeout(function() { |
| 656 |
form.slideUp(); |
| 657 |
}, timeoutAfterRequest); |
| 658 |
}, |
| 659 |
}); |
| 660 |
} |
| 661 |
|
| 662 |
/** |
| 663 |
* @param {Event} event - Click event. |
| 664 |
*/ |
| 665 |
function doBulkAction(event) { |
| 666 |
var action = document.querySelector('[name="action"]').value; |
| 667 |
var spinnerWrapper = $(this).parent('.bulkactions'); |
| 668 |
|
| 669 |
/** |
| 670 |
* Check the selected action is ours. |
| 671 |
*/ |
| 672 |
if (!Object.values(wcpn.bulk_actions).includes(action)) { |
| 673 |
return; |
| 674 |
} |
| 675 |
|
| 676 |
event.preventDefault(); |
| 677 |
|
| 678 |
/* |
| 679 |
* Remove notices |
| 680 |
*/ |
| 681 |
$(selectors.notice).remove(); |
| 682 |
var order_ids = []; |
| 683 |
var rows = []; |
| 684 |
|
| 685 |
/* |
| 686 |
* Get array of selected order_ids |
| 687 |
*/ |
| 688 |
$('tbody th.check-column input[type="checkbox"]:checked').each( |
| 689 |
function() { |
| 690 |
order_ids.push($(this).val()); |
| 691 |
rows.push('.post-' + $(this).val()); |
| 692 |
} |
| 693 |
); |
| 694 |
|
| 695 |
$(rows.join(', ')).addClass('wcpn__loading'); |
| 696 |
|
| 697 |
if (!order_ids.length) { |
| 698 |
alert(wcpn.strings.no_orders_selected); |
| 699 |
return; |
| 700 |
} |
| 701 |
|
| 702 |
switch (action) { |
| 703 |
|
| 704 |
/** |
| 705 |
* Export orders. |
| 706 |
*/ |
| 707 |
case wcpn.bulk_actions.export: |
| 708 |
exportToPostNL.bind(spinnerWrapper)(order_ids); |
| 709 |
break; |
| 710 |
|
| 711 |
/** |
| 712 |
* Print labels. |
| 713 |
*/ |
| 714 |
case wcpn.bulk_actions.print: |
| 715 |
printLabel.bind(spinnerWrapper)({ |
| 716 |
order_ids: order_ids, |
| 717 |
}); |
| 718 |
break; |
| 719 |
|
| 720 |
/** |
| 721 |
* Export and print. |
| 722 |
*/ |
| 723 |
case wcpn.bulk_actions.export_print: |
| 724 |
exportToPostNL.bind(spinnerWrapper)(order_ids, 'after_reload'); |
| 725 |
break; |
| 726 |
} |
| 727 |
} |
| 728 |
|
| 729 |
/** |
| 730 |
* Do an ajax request. |
| 731 |
* |
| 732 |
* @param {Object} request - Request object. |
| 733 |
*/ |
| 734 |
function doRequest(request) { |
| 735 |
var button = this; |
| 736 |
|
| 737 |
$(button).prop('disabled', true); |
| 738 |
setSpinner(button, spinner.loading); |
| 739 |
|
| 740 |
if (!request.url) { |
| 741 |
request.url = wcpn.ajax_url; |
| 742 |
} |
| 743 |
|
| 744 |
if (request.hasOwnProperty('onStart') && typeof request.onStart === 'function') { |
| 745 |
request.onStart(); |
| 746 |
} |
| 747 |
|
| 748 |
$.ajax({ |
| 749 |
url: request.url, |
| 750 |
method: request.method || 'POST', |
| 751 |
data: request.data || {}, |
| 752 |
}) |
| 753 |
.done(function(res) { |
| 754 |
setSpinner(button, spinner.success); |
| 755 |
|
| 756 |
if (request.hasOwnProperty('afterDone') && typeof request.afterDone === 'function') { |
| 757 |
request.afterDone(res); |
| 758 |
} |
| 759 |
}) |
| 760 |
|
| 761 |
.fail(function(res) { |
| 762 |
setSpinner(button, spinner.failed); |
| 763 |
|
| 764 |
if (request.hasOwnProperty('afterFail') && typeof request.afterFail === 'function') { |
| 765 |
request.afterFail(res); |
| 766 |
} |
| 767 |
}) |
| 768 |
|
| 769 |
.always(function(res) { |
| 770 |
$(button).prop('disabled', false); |
| 771 |
|
| 772 |
if (request.hasOwnProperty('afterAlways') && typeof request.afterAlways === 'function') { |
| 773 |
request.afterAlways(res); |
| 774 |
} |
| 775 |
}); |
| 776 |
} |
| 777 |
|
| 778 |
/** |
| 779 |
* @param name |
| 780 |
* @param url |
| 781 |
*/ |
| 782 |
function getParameterByName(name, url) { |
| 783 |
if (!url) { |
| 784 |
url = window.location.href; |
| 785 |
} |
| 786 |
name = name.replace(/[\[\]]/g, '\\$&'); |
| 787 |
|
| 788 |
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'); |
| 789 |
var results = regex.exec(url); |
| 790 |
|
| 791 |
if (!results) { |
| 792 |
return null; |
| 793 |
} |
| 794 |
|
| 795 |
if (!results[2]) { |
| 796 |
return ''; |
| 797 |
} |
| 798 |
|
| 799 |
return decodeURIComponent(results[2].replace(/\+/g, ' ')); |
| 800 |
} |
| 801 |
|
| 802 |
/** |
| 803 |
* On clicking the actions in a single order. |
| 804 |
* |
| 805 |
* @param {Event} event - Click event. |
| 806 |
*/ |
| 807 |
function onActionClick(event) { |
| 808 |
var button = this; |
| 809 |
|
| 810 |
var request = getParameterByName('request', button.href); |
| 811 |
var order_ids = getParameterByName('order_ids', button.href); |
| 812 |
|
| 813 |
if (!wcpn.actions.hasOwnProperty(request)) { |
| 814 |
return; |
| 815 |
} |
| 816 |
|
| 817 |
event.preventDefault(); |
| 818 |
|
| 819 |
switch (request) { |
| 820 |
case wcpn.actions.add_shipments: |
| 821 |
exportToPostNL.bind(button)(); |
| 822 |
break; |
| 823 |
case wcpn.actions.get_labels: |
| 824 |
if (askForPrintPosition && !$(button).hasClass('wcpn__offset-dialog__button')) { |
| 825 |
showOffsetDialog.bind(button)(); |
| 826 |
} else { |
| 827 |
printLabel.bind(button)(); |
| 828 |
} |
| 829 |
break; |
| 830 |
case wcpn.actions.add_return: |
| 831 |
postnl_modal_dialog(order_ids, 'return'); |
| 832 |
break; |
| 833 |
} |
| 834 |
} |
| 835 |
|
| 836 |
/** |
| 837 |
* Show the offset dialog before printing. |
| 838 |
* |
| 839 |
* @param {String?} position - To position the dialog `left` or `right` relative to the bound element. |
| 840 |
* @param {String?} context - Context in which the dialog was created. Ex. 'bulk'. |
| 841 |
*/ |
| 842 |
function showOffsetDialog(position, context) { |
| 843 |
position = position || 'left'; |
| 844 |
|
| 845 |
var parent = this; |
| 846 |
var offsetDialog = $(selectors.offsetDialog); |
| 847 |
var dialogButton = $(selectors.offsetDialogButton); |
| 848 |
var parentOffset = $(parent).offset(); |
| 849 |
|
| 850 |
/** |
| 851 |
* Position it to the bottom left or right of the clicked button. |
| 852 |
*/ |
| 853 |
if (position === 'left') { |
| 854 |
offsetDialog.css({ |
| 855 |
left: parentOffset.left - offsetDialog.width(), |
| 856 |
top: parentOffset.top, |
| 857 |
}); |
| 858 |
} else { |
| 859 |
offsetDialog.css(parentOffset); |
| 860 |
} |
| 861 |
|
| 862 |
dialogButton.attr('href', parent.href); |
| 863 |
|
| 864 |
/** |
| 865 |
* Reset input(s). |
| 866 |
*/ |
| 867 |
offsetDialog.find('input').val(0); |
| 868 |
|
| 869 |
/** |
| 870 |
* Make sure button is not shown and there is no input listener to update it if context is bulk. |
| 871 |
*/ |
| 872 |
if (context === 'bulk') { |
| 873 |
dialogButton.hide(); |
| 874 |
$(selectors.offsetDialogInputOffset).off('blur update change', onUpdateOffset); |
| 875 |
} else { |
| 876 |
dialogButton.show(); |
| 877 |
$(selectors.offsetDialogInputOffset).on('blur update change', onUpdateOffset); |
| 878 |
} |
| 879 |
|
| 880 |
/** |
| 881 |
* Finally, show the dialog. |
| 882 |
*/ |
| 883 |
offsetDialog.slideDown(); |
| 884 |
} |
| 885 |
|
| 886 |
/** |
| 887 |
* Hide the offset dialog and remove the input listener. |
| 888 |
* |
| 889 |
* @param {Event?} event - Click event if called from a button. |
| 890 |
*/ |
| 891 |
function hideOffsetDialog(event) { |
| 892 |
if (event) { |
| 893 |
event.preventDefault(); |
| 894 |
} |
| 895 |
|
| 896 |
$(selectors.offsetDialogInputOffset).off('blur update change', onUpdateOffset); |
| 897 |
$(selectors.offsetDialog).slideUp(); |
| 898 |
} |
| 899 |
|
| 900 |
/** |
| 901 |
* On changing the offset value in the dialog, update the offset parameter in the dialog button's href attribute. |
| 902 |
*/ |
| 903 |
function onUpdateOffset() { |
| 904 |
var dialogButton = $(selectors.offsetDialogButton); |
| 905 |
var hasOffset = dialogButton.attr('href').indexOf('offset=') > -1; |
| 906 |
var newOffset = this.value; |
| 907 |
|
| 908 |
if (hasOffset) { |
| 909 |
dialogButton.attr('href', dialogButton.attr('href').replace(/([?&]offset=)\d*/, '$1' + newOffset)); |
| 910 |
} else { |
| 911 |
dialogButton.attr('href', dialogButton.attr('href') + '&offset=' + newOffset); |
| 912 |
} |
| 913 |
} |
| 914 |
|
| 915 |
/** |
| 916 |
* Show the offset dialog for bulk options that allow it. |
| 917 |
*/ |
| 918 |
function showBulkOffsetDialog() { |
| 919 |
if ([wcpn.bulk_actions.print, wcpn.bulk_actions.export_print].indexOf(this.value) === -1) { |
| 920 |
hideOffsetDialog(); |
| 921 |
return; |
| 922 |
} |
| 923 |
|
| 924 |
showOffsetDialog.bind(this)('right', 'bulk'); |
| 925 |
} |
| 926 |
|
| 927 |
/** |
| 928 |
* |
| 929 |
*/ |
| 930 |
function printOrder() { |
| 931 |
var dialog = $(this).parent(); |
| 932 |
|
| 933 |
/* set print variables */ |
| 934 |
var order_ids = [dialog.find('input.order_id').val()]; |
| 935 |
var offset = dialog.find(selectors.offsetDialogInputOffset).val(); |
| 936 |
|
| 937 |
/* hide dialog */ |
| 938 |
dialog.hide(); |
| 939 |
|
| 940 |
/* print labels */ |
| 941 |
printLabel({ |
| 942 |
order_ids: order_ids, |
| 943 |
offset: offset, |
| 944 |
}); |
| 945 |
} |
| 946 |
|
| 947 |
/* export orders to PostNL via AJAX */ |
| 948 |
/** |
| 949 |
* @param order_ids |
| 950 |
* @param print |
| 951 |
*/ |
| 952 |
function exportToPostNL(order_ids, print) { |
| 953 |
var url; |
| 954 |
var data; |
| 955 |
|
| 956 |
if (typeof print === 'undefined') { |
| 957 |
print = 'no'; |
| 958 |
} |
| 959 |
|
| 960 |
if (this.href) { |
| 961 |
url = this.href; |
| 962 |
} else { |
| 963 |
data = { |
| 964 |
action: wcpn.actions.export, |
| 965 |
request: wcpn.actions.add_shipments, |
| 966 |
offset: getPrintOffset(), |
| 967 |
order_ids: order_ids, |
| 968 |
print: print, |
| 969 |
_wpnonce: wcpn.nonce, |
| 970 |
}; |
| 971 |
} |
| 972 |
|
| 973 |
doRequest.bind(this)({ |
| 974 |
url: url, |
| 975 |
data: data || {}, |
| 976 |
afterDone: function(response) { |
| 977 |
var redirect_url = updateUrlParameter(window.location.href, 'postnl_done', 'true'); |
| 978 |
|
| 979 |
if (print === 'no' || print === 'after_reload') { |
| 980 |
/* refresh page, admin notices are stored in options and will be displayed automatically */ |
| 981 |
window.location.href = redirect_url; |
| 982 |
} else { |
| 983 |
/* when printing, output notices directly so that we can init print in the same run */ |
| 984 |
if (response !== null && typeof response === 'object' && 'error' in response) { |
| 985 |
postnl_admin_notice(response.error, 'error'); |
| 986 |
} |
| 987 |
|
| 988 |
if (response !== null && typeof response === 'object' && 'success' in response) { |
| 989 |
postnl_admin_notice(response.success, 'success'); |
| 990 |
} |
| 991 |
|
| 992 |
/* load PDF */ |
| 993 |
printLabel({ |
| 994 |
order_ids: order_ids, |
| 995 |
}); |
| 996 |
} |
| 997 |
}, |
| 998 |
}); |
| 999 |
} |
| 1000 |
|
| 1001 |
/** |
| 1002 |
* @param order_ids |
| 1003 |
* @param dialog |
| 1004 |
*/ |
| 1005 |
function postnl_modal_dialog(order_ids, dialog) { |
| 1006 |
var data = { |
| 1007 |
action: wcpn.actions.export, |
| 1008 |
request: wcpn.actions.modal_dialog, |
| 1009 |
height: 380, |
| 1010 |
width: 720, |
| 1011 |
order_ids: order_ids, |
| 1012 |
dialog: dialog, |
| 1013 |
_wpnonce: wcpn.nonce, |
| 1014 |
// LEAVE THIS AT THE BOTTOM! The awful code behind the thickbox splits the url on "TB_" for some reason. |
| 1015 |
TB_iframe: true, |
| 1016 |
}; |
| 1017 |
|
| 1018 |
var url = wcpn.ajax_url + '?' + $.param(data); |
| 1019 |
|
| 1020 |
/* disable background scrolling */ |
| 1021 |
$('body').css({overflow: 'hidden'}); |
| 1022 |
|
| 1023 |
tb_show('', url); |
| 1024 |
} |
| 1025 |
|
| 1026 |
/** |
| 1027 |
* Re-enable scrolling after closing thickbox. |
| 1028 |
*/ |
| 1029 |
function onThickBoxUnload() { |
| 1030 |
$('body').css({overflow: 'inherit'}); |
| 1031 |
} |
| 1032 |
|
| 1033 |
/** |
| 1034 |
* Open given pdf link. Depending on the link it will be either downloaded or viewed. Refreshes the original window. |
| 1035 |
* |
| 1036 |
* @param {String} pdfUrl - The url of the created pdf. |
| 1037 |
* @param {Boolean?} waitForOnload - Wait for onload to refresh the original window. Refreshes immediately if false. |
| 1038 |
* |
| 1039 |
*/ |
| 1040 |
function openPdf(pdfUrl, waitForOnload) { |
| 1041 |
var pdfWindow = window.open(pdfUrl, '_blank'); |
| 1042 |
|
| 1043 |
if (waitForOnload) { |
| 1044 |
/* |
| 1045 |
* When the pdf window is loaded reload the main window. If we reload earlier the track & trace code won't be |
| 1046 |
* ready yet and can't be shown. |
| 1047 |
*/ |
| 1048 |
pdfWindow.onload = function() { |
| 1049 |
window.location.reload(); |
| 1050 |
}; |
| 1051 |
} else { |
| 1052 |
/* For when there is no onload event or there is no need to wait. */ |
| 1053 |
window.location.reload(); |
| 1054 |
} |
| 1055 |
} |
| 1056 |
|
| 1057 |
/** |
| 1058 |
* Get the offset from the offset dialog if it's present. Otherwise return 0. |
| 1059 |
* |
| 1060 |
* @returns {Number} |
| 1061 |
*/ |
| 1062 |
function getPrintOffset() { |
| 1063 |
return parseInt(askForPrintPosition ? $(selectors.offsetDialogInputOffset).val() : 0); |
| 1064 |
} |
| 1065 |
|
| 1066 |
/* Request PostNL labels */ |
| 1067 |
/** |
| 1068 |
* @param data |
| 1069 |
*/ |
| 1070 |
function printLabel(data) { |
| 1071 |
var button = this; |
| 1072 |
var request; |
| 1073 |
|
| 1074 |
if (button.href) { |
| 1075 |
request = { |
| 1076 |
url: button.href, |
| 1077 |
}; |
| 1078 |
} else { |
| 1079 |
request = { |
| 1080 |
data: Object.assign({ |
| 1081 |
action: wcpn.actions.export, |
| 1082 |
request: wcpn.actions.get_labels, |
| 1083 |
offset: getPrintOffset(), |
| 1084 |
_wpnonce: wcpn.nonce, |
| 1085 |
}, data), |
| 1086 |
}; |
| 1087 |
} |
| 1088 |
|
| 1089 |
request.afterDone = function(response) { |
| 1090 |
var isDisplay = wcpn.download_display === 'display'; |
| 1091 |
var isDownload = wcpn.download_display === 'download'; |
| 1092 |
var isPdf = response.includes('PDF'); |
| 1093 |
var isApi = response.includes('api.myparcel.nl'); |
| 1094 |
|
| 1095 |
if (isDisplay && isPdf) { |
| 1096 |
handlePDF(request); |
| 1097 |
} |
| 1098 |
|
| 1099 |
if (isDownload && isApi) { |
| 1100 |
openPdf(response); |
| 1101 |
} |
| 1102 |
|
| 1103 |
window.location.reload(); |
| 1104 |
}; |
| 1105 |
|
| 1106 |
doRequest.bind(button)(request); |
| 1107 |
} |
| 1108 |
|
| 1109 |
/** |
| 1110 |
* @param request |
| 1111 |
*/ |
| 1112 |
function handlePDF(request) { |
| 1113 |
var url; |
| 1114 |
|
| 1115 |
if (request.hasOwnProperty('data')) { |
| 1116 |
url = wcpn.ajax_url + '?' + $.param(request.data); |
| 1117 |
} else { |
| 1118 |
url = request.url; |
| 1119 |
} |
| 1120 |
|
| 1121 |
openPdf(url, true); |
| 1122 |
} |
| 1123 |
|
| 1124 |
/** |
| 1125 |
* @param message |
| 1126 |
* @param type |
| 1127 |
*/ |
| 1128 |
function postnl_admin_notice(message, type) { |
| 1129 |
var mainHeader = $('#wpbody-content > .wrap > h1:first'); |
| 1130 |
var notice = '<div class="' + selectors.notice + ' notice notice-' + type + '"><p>' + message + '</p></div>'; |
| 1131 |
mainHeader.after(notice); |
| 1132 |
$('html, body').animate({scrollTop: 0}, 'slow'); |
| 1133 |
} |
| 1134 |
|
| 1135 |
/* Add / Update a key-value pair in the URL query parameters */ |
| 1136 |
|
| 1137 |
/* https://gist.github.com/niyazpk/f8ac616f181f6042d1e0 */ |
| 1138 |
/** |
| 1139 |
* @param uri |
| 1140 |
* @param key |
| 1141 |
* @param value |
| 1142 |
*/ |
| 1143 |
function updateUrlParameter(uri, key, value) { |
| 1144 |
/* remove the hash part before operating on the uri */ |
| 1145 |
var i = uri.indexOf('#'); |
| 1146 |
var hash = i === -1 ? '' : uri.substr(i); |
| 1147 |
uri = i === -1 ? uri : uri.substr(0, i); |
| 1148 |
|
| 1149 |
var re = new RegExp('([?&])' + key + '=.*?(&|$)', 'i'); |
| 1150 |
var separator = uri.indexOf('?') !== -1 ? '&' : '?'; |
| 1151 |
if (uri.match(re)) { |
| 1152 |
uri = uri.replace(re, '$1' + key + '=' + value + '$2'); |
| 1153 |
} else { |
| 1154 |
uri = uri + separator + key + '=' + value; |
| 1155 |
} |
| 1156 |
return uri + hash; /* finally append the hash as well */ |
| 1157 |
} |
| 1158 |
|
| 1159 |
/** |
| 1160 |
* |
| 1161 |
*/ |
| 1162 |
function showShipmentSummaryList() { |
| 1163 |
var summaryList = $(this).next(selectors.shipmentSummaryList); |
| 1164 |
|
| 1165 |
if (summaryList.is(':hidden')) { |
| 1166 |
summaryList.slideDown(); |
| 1167 |
document.addEventListener('click', hideShipmentSummaryList); |
| 1168 |
} else { |
| 1169 |
} |
| 1170 |
|
| 1171 |
if (summaryList.data('loaded') === '') { |
| 1172 |
summaryList.addClass('ajax-waiting'); |
| 1173 |
summaryList.find(selectors.spinner).show(); |
| 1174 |
|
| 1175 |
var data = { |
| 1176 |
security: wcpn.nonce, |
| 1177 |
action: 'wcpn_get_shipment_summary_status', |
| 1178 |
order_id: summaryList.data('order_id'), |
| 1179 |
shipment_id: summaryList.data('shipment_id'), |
| 1180 |
}; |
| 1181 |
|
| 1182 |
$.ajax({ |
| 1183 |
type: 'POST', |
| 1184 |
url: wcpn.ajax_url, |
| 1185 |
data: data, |
| 1186 |
context: summaryList, |
| 1187 |
success: function(response) { |
| 1188 |
this.removeClass('ajax-waiting'); |
| 1189 |
this.html(response); |
| 1190 |
this.data('loaded', true); |
| 1191 |
}, |
| 1192 |
}); |
| 1193 |
} |
| 1194 |
} |
| 1195 |
|
| 1196 |
/** |
| 1197 |
* @param {MouseEvent} event - The click event. |
| 1198 |
* @param {Element} event.target - Click target. |
| 1199 |
*/ |
| 1200 |
function hideShipmentOptionsForm(event) { |
| 1201 |
handleClickOutside.bind(hideShipmentOptionsForm)(event, { |
| 1202 |
main: selectors.shipmentOptionsDialog, |
| 1203 |
wrappers: [ |
| 1204 |
selectors.shipmentOptions, |
| 1205 |
selectors.shipmentOptionsShowButton, |
| 1206 |
// Add the tipTip ids as well so clicking a tipTip inside shipment options won't close the form. |
| 1207 |
selectors.tipTipHolder, |
| 1208 |
selectors.tipTipContent, |
| 1209 |
], |
| 1210 |
}); |
| 1211 |
} |
| 1212 |
|
| 1213 |
/** |
| 1214 |
* Main: The element that will be hidden. |
| 1215 |
* Wrappers: Elements which don't count as "outside" when clicked. |
| 1216 |
* |
| 1217 |
* @param {MouseEvent} event - Click event. |
| 1218 |
* @property {Element} event.target |
| 1219 |
*/ |
| 1220 |
function hideShipmentSummaryList(event) { |
| 1221 |
handleClickOutside.bind(hideShipmentSummaryList)(event, { |
| 1222 |
main: selectors.shipmentSummaryList, |
| 1223 |
wrappers: [selectors.shipmentSummaryList, selectors.showShipmentSummaryList], |
| 1224 |
}); |
| 1225 |
} |
| 1226 |
|
| 1227 |
/** |
| 1228 |
* Hide any element by checking if the element clicked is not in the list of wrapper elements and not inside the |
| 1229 |
* element itself. |
| 1230 |
* |
| 1231 |
* @param {MouseEvent} event - The click event. |
| 1232 |
* @param {Object} elements - The elements to show/hide and check inside. |
| 1233 |
* @property {Node[]} elements.wrappers |
| 1234 |
* @property {Node} elements.main |
| 1235 |
*/ |
| 1236 |
function handleClickOutside(event, elements) { |
| 1237 |
event.preventDefault(); |
| 1238 |
var listener = this; |
| 1239 |
var clickedOutside = true; |
| 1240 |
|
| 1241 |
elements.wrappers.forEach(function(cls) { |
| 1242 |
if (clickedOutside && event.target.matches(cls) || event.target.closest(elements.main)) { |
| 1243 |
clickedOutside = false; |
| 1244 |
} |
| 1245 |
}); |
| 1246 |
|
| 1247 |
if (clickedOutside) { |
| 1248 |
$(elements.main).slideUp(); |
| 1249 |
document.removeEventListener('click', listener); |
| 1250 |
} |
| 1251 |
} |
| 1252 |
|
| 1253 |
/** |
| 1254 |
* On clicking a toggle. Doesn't do anything if the parent row has data-readonly or data-disabled set to true. |
| 1255 |
*/ |
| 1256 |
function handleToggle() { |
| 1257 |
var disabledClass = 'woocommerce-input-toggle--disabled'; |
| 1258 |
var enabledClass = 'woocommerce-input-toggle--enabled'; |
| 1259 |
var row = $(this).closest('tr'); |
| 1260 |
var input = $(this).find('input')[0]; |
| 1261 |
var toggle = $(this).find('.woocommerce-input-toggle'); |
| 1262 |
|
| 1263 |
var rowReadOnly = row.attr('data-readonly') === 'true'; |
| 1264 |
var rowDisabled = row.attr('data-disabled') === 'true'; |
| 1265 |
|
| 1266 |
if (rowReadOnly || rowDisabled) { |
| 1267 |
return; |
| 1268 |
} |
| 1269 |
|
| 1270 |
input.value = toggle.hasClass(disabledClass) ? '1' : '0'; |
| 1271 |
toggle.toggleClass(disabledClass); |
| 1272 |
toggle.toggleClass(enabledClass); |
| 1273 |
|
| 1274 |
// To trigger event listeners |
| 1275 |
input.dispatchEvent(new Event('change')); |
| 1276 |
} |
| 1277 |
}); |
| 1278 |
|