1//Important note 2//If you want to trigger the events of the module elements, you must use 3//the exported variable window.jqueryOPC and not the jQuery of the template, for example: 4//window.jqueryOPC.find('#id_state').trigger('change'); 5 6function initOpcCompatibilities() { 7 if (typeof prestashop === typeof undefined) { 8 return; 9 } 10 11 //Defensa: si el JS del tema falló antes de ejecutarse core.js (ej. SyntaxError en el 12 //bundle CCC bottom-*.js), `prestashop` queda como el objeto plano del inline del <head> 13 //y nunca se convierte en EventEmitter â `prestashop.on is not a function` rompe OPC. 14 //Polyfill mÃnimo on/off/once/emit para que el checkout siga operativo aunque el tema falle. 15 if (typeof prestashop.on !== 'function' || typeof prestashop.emit !== 'function') { 16 (function (ps) { 17 console.warn('[OPC] prestashop event emitter missing â theme JS likely failed (check for a SyntaxError in the theme bundle); using internal fallback'); 18 19 const listeners = {}; 20 21 ps.on = function (event, callback) { 22 (listeners[event] = listeners[event] || []).push(callback); 23 return ps; 24 }; 25 26 ps.off = ps.removeListener = function (event, callback) { 27 if (listeners[event]) { 28 listeners[event] = listeners[event].filter( 29 (cb) => cb !== callback && cb._opcOnce !== callback 30 ); 31 } 32 return ps; 33 }; 34 35 ps.once = function (event, callback) { 36 const wrapper = function () { 37 ps.off(event, wrapper); 38 callback.apply(ps, arguments); 39 }; 40 wrapper._opcOnce = callback; 41 return ps.on(event, wrapper); 42 }; 43 44 ps.emit = function (event) { 45 const args = Array.prototype.slice.call(arguments, 1); 46 (listeners[event] || []).slice().forEach(function (callback) { 47 try { 48 callback.apply(ps, args); 49 } catch (error) { 50 console.error('[OPC] Listener error on "' + event + '":', error); 51 } 52 }); 53 return ps; 54 }; 55 })(prestashop); 56 } 57 58 // support module: colissimo - v2.2.4 - PrestaShop SA 59 // El widget de punto de recogida (widget.js) consulta puntos usando el objeto global 60 // `colissimoDeliveryAddress` (dirección de entrega, renderizada server-side desde 61 // $cart->id_address_delivery). En OPC unified el VISITANTE tiene una dirección temporal 62 // centinela en el cart (city='NA', postcode vacÃo) hasta confirmar, asà que colissimo la 63 // emite vacÃa y la WS responde errorCode 104 "Code postal absent" â "Aucun point trouvé". 64 // Refrescamos `colissimoDeliveryAddress` con lo que el visitante escribió EN VIVO en el 65 // form de entrega de OPC justo antes de abrir el widget, mutando sus PROPIEDADES por 66 // nombre desnudo (funciona aunque sea `const`). Es un fallback para el caso "clic antes de 67 // que el refresh AJAX guarde/re-renderice"; el fix principal es server-side: ShippingService 68 // convierte el `const colissimoDeliveryAddress` de colissimo a `var` en cada render (SSR y 69 // AJAX) para que el refresh tras guardar la dirección actualice el objeto sin colisionar. 70 const opcColissimoIsSentinelValue = (value) => { 71 const normalized = (value || '').trim().toUpperCase(); 72 return normalized === '' || normalized === 'NA' || normalized === 'NA NA' || normalized === '00000'; 73 }; 74 75 const opcUpdateColissimoDeliveryAddressFromForm = () => { 76 // Referencia por nombre desnudo (no window.*): en unified el shipping SSR declara 77 // `const colissimoDeliveryAddress` como binding LÃXICO global (no propiedad de window), 78 // mientras que en los refresh AJAX es `var`. El bare-name resuelve ambos por scope 79 // léxico. Mutamos sus PROPIEDADES (permitido aunque sea const), nunca reasignamos. 80 if (typeof colissimoDeliveryAddress === 'undefined' 81 || !colissimoDeliveryAddress 82 || typeof colissimoDeliveryAddress !== 'object' 83 ) { 84 return; 85 } 86 const address = colissimoDeliveryAddress; 87
88 // El wrapper del rol de entrega lleva data-type-address="delivery" en cualquier layout. 89 const $deliveryForm = $('[data-type-address="delivery"]').first(); 90 if (!$deliveryForm.length) { 91 return; 92 } 93 94 const readField = (name) => { 95 const $field = $deliveryForm.find('[name="' + name + '"]').first(); 96 return $field.length ? String($field.val() || '').trim() : ''; 97 }; 98 99 const postcode = readField('postcode'); 100 const city = readField('city'); 101 const address1 = readField('address1'); 102 103 // Solo sobreescribir con valores reales del form; nunca degradar a centinela/vacÃo. 104 if (!opcColissimoIsSentinelValue(postcode)) { 105 address.zipcode = postcode; 106 } 107 if (!opcColissimoIsSentinelValue(city)) { 108 address.city = city; 109 } 110 if (!opcColissimoIsSentinelValue(address1)) { 111 address.address = address1; 112 } 113 }; 114 115 // Captura (fase capture) para correr ANTES del handler delegado de colissimo, que lee 116 // `colissimoDeliveryAddress` al abrir el widget. Enlace único por request. 117 if (!window._opcColissimoAddrBound) { 118 window._opcColissimoAddrBound = true; 119 document.addEventListener('click', (event) => { 120 if (event.target && event.target.closest && event.target.closest('#colissimo-select-pickup-point')) { 121 opcUpdateColissimoDeliveryAddressFromForm(); 122 } 123 }, true); 124 } 125 126 // colissimo - modal duplicado. widget.js mueve su modal (.colissimo-front-widget-17 / 127 // .colissimo-osm-front-widget) a <body> en su `ready` (que NO se re-ejecuta). OPC 128 // re-inyecta el bloque del carrier en cada refresh, dejando OTRA copia del modal en el 129 // paso de envÃo. Al abrir, `.modal('show')` matchea ambas â se apilan DOS .modal-backdrop 130 // que tapan el contenido y bloquean el clic (solo se cierra). Conservamos la copia que ya 131 // está en <body> (la que usa widget.js) y quitamos las duplicadas re-inyectadas. 132 const opcDedupeColissimoModals = () => { 133 ['.colissimo-front-widget-17', '.colissimo-osm-front-widget'].forEach((selector) => { 134 const $modals = $(selector); 135 if ($modals.length <= 1) { 136 return; 137 } 138 139 const $inBody = $modals.filter((_, el) => el.parentNode === document.body); 140 if ($inBody.length) { 141 $modals.not($inBody.get(0)).remove(); 142 } else { 143 // Ninguna en <body>: dejar una y moverla a <body> como hace widget.js. 144 $modals.slice(0, -1).remove(); 145 $modals.last().appendTo('body'); 146 } 147 }); 148 }; 149 150 let intervalPaymentToggleOrderButtonValidation = null; 151 152 //uso de la funcion compareVersion: 153 //console.log(compareVersion('1.0.0', '1.0.1')); // -1 (v1 < v2) 154 //console.log(compareVersion('1.0.2', '1.0.1')); // 1 (v1 > v2) 155 //console.log(compareVersion('1.0.2', '1.0.2')); // 0 (v1 == v2) 156 const compareVersion = (v1, v2) => { 157 if (typeof v1 !== 'string') return false; 158 if (typeof v2 !== 'string') return false; 159 v1 = v1.split('.'); 160 v2 = v2.split('.'); 161 const k = Math.min(v1.length, v2.length); 162 for (let i = 0; i < k; ++i) { 163 v1[i] = parseInt(v1[i], 10); 164 v2[i] = parseInt(v2[i], 10); 165 if (v1[i] > v2[i]) return 1; 166 if (v1[i] < v2[i]) return -1; 167 } 168 return v1.length === v2.length ? 0 : v1.length < v2.length ? -1 : 1; 169 }; 170 171 // App - Events 172 prestashop.on('opc-app-init', (params) => { 173 const { JqueryElementMain } = params; 174 175 $(document).on('ajaxSend', (e, jqXHR, settings) => { 176 if (settings.url === window.prestashop.urls.pages.order 177 && settings.data 178 && settings.data.includes('action=checkCartStillOrderable') 179 ) { 180 $('#opc-checkout-finalize').prop('disabled', true); 181 } 182 }); 183 184 $(document).on('ajaxComplete', (e, jqXHR, settings) => { 185 if (settings.url === window.prestashop.urls.pages.order 186 && settings.data 187 && settings.data.includes('action=checkCartStillOrderable') 188 ) { 189 $('#opc-checkout-finalize').prop('disabled', false); 190 } 191 192 }); 193 194 //support module: mondialrelay - v4.1.2/v4.1.4/v4.2.0 - ScaleDEV / sendcloudv2 - v2.0.24 - Sendcloud 195 //Ambos módulos persisten el punto de recogida vÃa XHR propio tras seleccionarlo: 196 // - mondialrelay â action=saveSelectedRelay (v4.1.2 XHR nativo; v4.1.4+ $.ajax al click del botón único; v4.2.0 sin cambios en el front de checkout) 197 // - sendcloudv2 â controller ServicePointSelection (responde 204 No Content) 198 //OPC no detecta esos XHR con ajaxComplete, por lo que el paso de pagos no se recarga 199 //y el cliente no puede continuar la compra. 200 //Interceptamos XHR y forzamos la recarga de pagos al completar la llamada. 201 //IMPORTANTE: la evaluación se hace en el evento `load` (no en send) usando 202 //`this.responseURL` como fallback de URL: scripts dinámicos del widget de MR 203 //pueden re-sobrescribir XMLHttpRequest.prototype.open SIN encadenar, perdiendo 204 //la URL capturada (`_opcPickupPointUrl`) y rompiendo el match (visto con v4.1.4). 205 //Gate mondialrelay: la jsDef MONDIALRELAY_AJAX_CHECKOUT_URL viene inline en el HTML 206 //inicial (Media::addJsDef) siempre que el módulo está activo. NO usar solo el tag 207 //script: el del widget CDN se inyecta dinámicamente al inicializar el widget (no 208 //existe aún en la 1ª carga sin carrier MR elegido) y el JS local del módulo puede 209 //ir combinado por CCC en bottom-*.js (src sin "mondialrelay"). 210 const hasMondialRelay = typeof window.MONDIALRELAY_AJAX_CHECKOUT_URL !== 'undefined' 211 || document.querySelector('script[src*="mondialrelay"]') !== null; 212 const hasSendcloudV2 = document.querySelector('script[src*="servicepoints.sendcloud.sc/shops/prestashop_v2"]') !== null; 213 if (hasMondialRelay || hasSendcloudV2) { 214 (function () { 215 const _xhrOpen = XMLHttpRequest.prototype.open; 216 const _xhrSend = XMLHttpRequest.prototype.send; 217 218 XMLHttpRequest.prototype.open = function (method, url) { 219 this._opcPickupPointUrl = typeof url === 'string' ? url : null; 220 return _xhrOpen.apply(this, arguments); 221 }; 222 223 XMLHttpRequest.prototype.send = function (body) { 224 const bodyStr = typeof body === 'string' ? body : ''; 225 226 this.addEventListener('load', function () { 227 const url = this._opcPickupPointUrl || this.responseURL || ''; 228 //mondialrelay: si la URL no sobrevivió (open pisado por otro script), 229 //el body urlencoded `action=saveSelectedRelay` es suficientemente único. 230 const isMondialRelay = bodyStr.includes('saveSelectedRelay') 231 && (url === '' || url.includes('mondialrelay')); 232 const isSendcloudV2 = url.includes('sendcloudv2') 233 && url.includes('ServicePointSelection'); 234 235 if (!isMondialRelay && !isSendcloudV2) { 236 return; 237 } 238 239 //Debounce: módulos con listeners duplicados disparan múltiples XHR 240 //en ráfaga (ej. sendcloudv2 tras re-renders). Emitimos
240solo una vez. 241 if (window._opcPickupPointEmitTimer) { 242 clearTimeout(window._opcPickupPointEmitTimer); 243 } 244 window._opcPickupPointEmitTimer = setTimeout(function () { 245 prestashop.emit('opc-payment-getPaymentList', {}); 246 window._opcPickupPointEmitTimer = null; 247 }, 300); 248 }); 249 250 return _xhrSend.apply(this, arguments); 251 }; 252 })(); 253 } 254 }); 255 256 // Field - Events 257 prestashop.on('opc-field-save-complete', (params) => { 258 const { JqueryElementForm } = params; 259 }); 260 261 // Login - Events 262 prestashop.on('opc-login_customer-loginCustomer-beforeSend', (params) => { 263 const { JqueryElementMain, JqueryElement } = params; 264 }); 265 266 prestashop.on('opc-login-loginCustomer-error', (params) => { 267 const { JqueryElement } = params; 268 269 // idxrdefender - v1.3.2 - innovadeluxe 270 if (typeof IdxrdefenderFront !== typeof undefined) { 271 JqueryElement.find('#g-recaptcha-response-login').val(''); 272 let defender = new IdxrdefenderFront(); 273 defender.bindsCaptcha(); 274 } 275 }); 276 277 // Personal Information - Events 278 prestashop.on('opc-personal_information-saveCustomer-beforeSend', (params) => { 279 const { JqueryElementMain, JqueryElement } = params; 280 }); 281 282 prestashop.on('opc-personal_information-saveCustomer-error', (params) => { 283 const { JqueryElement } = params; 284 285 // idxrdefender - v1.3.2 - innovadeluxe 286 if (typeof IdxrdefenderFront !== typeof undefined) { 287 JqueryElement.find('#g-recaptcha-response-login').val(''); 288 let defender = new IdxrdefenderFront(); 289 defender.bindsCaptcha(); 290 } 291 }); 292 293 // Addresses - Events 294 prestashop.on('opc-addresses-initEventFormAddress', (params) => { 295 const { formAddress } = params; 296 297 // chilexpress_oficial - v1.2.9 - Chilexpress 298 if (typeof chilexpress_oficial_ajax_url !== typeof undefined) { 299 const typeAddress = formAddress.data('component'); 300 let countrIsoCode = OPC.Addresses.country.iso_code; 301 302 const $countrySelector = formAddress.find('[name=id_country]'); 303 if ($countrySelector.length > 0) { 304 countrIsoCode = $countrySelector.find('option:selected').data('iso-code'); 305 } 306 307 if (countrIsoCode == 'CL') { 308 const $stateSelector = formAddress.find('[name=id_state]'); 309 310 $stateSelector.addClass('reloadAddresses'); 311 312 const $citySelector = formAddress.find('[name=city]'); 313 let $cityListSelector = formAddress.find('[name=city_list]'); 314 315 $citySelector.off('blur'); 316 317 $.ajax({ 318 type: 'POST', 319 url: chilexpress_oficial_ajax_url.replace(/&/gi,'&'), 320 cache: false, 321 dataType: 'json', 322 data: { 323 action : 'obtener_comunas_con_id', 324 region : $stateSelector.val(), 325 ajax: true 326 }, 327 success: function (result) { 328 if (typeof result.comunas !== undefined) { 329 if ($cityListSelector.length <= 0) { 330 $cityListSelector = $('<select/>') 331 .attr({ 332 id: typeAddress + '_city_list', 333 class: 'form-control input-sm ' + typeAddress 334 }) 335 .on('change', function(event) { 336 const optionSelected = $(event.currentTarget).find('option:selected'); 337 338 $citySelector.val( 339 $(optionSelected).attr('value') 340 ).trigger('blur'); 341 } 342 ); 343 } else { 344 $cityListSelector.empty().hide(); 345 } 346 347 $('<option/>') 348 .attr({ 349 value: '' 350 }) 351 .append('--') 352 .appendTo($cityListSelector); 353 354 $.each(result.comunas, function(key, city) { 355 $('<option/>') 356 .attr({ 357 'value': key 358 }) 359 .append(city) 360 .appendTo($cityListSelector); 361 }); 362 363 $citySelector.parents('.form-group') 364 .append($cityListSelector); 365 366 if ($cityListSelector.find('option[value="' + $citySelector.val() + '"]').length > 0) { 367 $cityListSelector.val($citySelector.val()); 368 } else { 369 $citySelector.val(''); 370 } 371 372 $cityListSelector.show(); 373 $citySelector.hide(); 374 } 375 376 // if (typeof result.comunas !== undefined) { 377 // $citySelector.typeahead({ 378 // highlight: true, 379 // items : 'all', 380 // showHintOnFocus: false, 381 // autoSelect: true, 382 // minLength: 0, 383 // source: function (query, process) { 384 // process($.map(result.comunas, function (city) { 385 // return { 386 // name: city, 387 // value: city 388 // }; 389 // })); 390 // }, 391 // updater: function (item) { 392 // //$citySelector.val(item.name); 393 394 // return item.value; 395 // } 396 // }); 397 // } 398 }, 399 }); 400 } 401 } 402 }); 403 prestashop.on('opc-addresses-getAddresses-complete', (params) => { 404 const { JqueryElementMain, JqueryElement } = params; 405 406 //facturare - v4.1.5 - Daniel Patilea 407 if (typeof initListen !== typeof undefined) { 408 initListen(); 409 } 410 }); 411 prestashop.on('opc-addresses-getAddresses-complete-edit', (params) => { 412 const { JqueryElementMain, JqueryElement } = params; 413 }); 414 prestashop.on('opc-addresses-getAddresses-complete-add', (params) => { 415 const { JqueryElementMain, JqueryElement } = params; 416 }); 417 418 // Shipping - Events 419 prestashop.on('opc-shipping-getCarrierList-complete', (params) => { 420 const { JqueryElement, JqueryElementContent } = params; 421 422 // colissimo - v2.2.4 - deja la dirección de entrega del widget alineada con el form 423 // en vivo tras cada refresh (cubre el modo OSM auto-abierto, que no espera al click) 424 // y elimina los modales duplicados que re-inyecta OPC (evita doble backdrop). 425 opcUpdateColissimoDeliveryAddressFromForm(); 426 opcDedupeColissimoModals(); 427 428 // econtdelivery - v2.0.3 - ITPartner 429 // Econt usa un iframe externo que envÃa postMessage con el precio de envÃo tras seleccionar 430 // un punto de entrega. El JS inline original del módulo se ejecutó en DOMContentLoaded y no 431 // se re-ejecuta cuando OPC re-renderiza los carriers vÃa AJAX. Registramos el listener de 432 // postMessage aquà (cuando el iframe ya existe en el DOM) para: 433 // 1) Guardar la info de envÃo vÃa AJAX (mismo endpoint que usa Econt) 434 // 2) Disparar updateCarrier para que OPC re-lea getOrderShippingCost (ahora con dato en BD) 435 // 3) Marcar que el punto de entrega fue seleccionado (para validación del botón de pago) 436 if (JqueryElement.find('#econt_delivery_customer_info_frame').length > 0) { 437 // Solo inicializar el flag si no está definido (evita resetearlo tras updateCarrier) 438 if (typeof window._opcEcontDeliverySelected === 'undefined') { 439 window._opcEcontDeliverySelected = false;
440 } 441 442 $(window).off('message.opcEcontShipping').on('message.opcEcontShipping', function (e) { 443 const data = e.originalEvent.data; 444 445 if (!data || typeof data !== 'object' || !data.shipping_price) { 446 return; 447 } 448 449 // Guardar info de envÃo en BD via actionDispatcherBefore del módulo Econt. 450 // El endpoint /module/econtdelivery/ajax solo guarda en SESSION/cookie, 451 // pero getOrderShippingCost() lee de la tabla econt_delivery_cart_info (BD). 452 // Enviamos ajax_econt_save + econt_data a cualquier URL de PS para que 453 // hookActionDispatcherBefore lo intercepte y guarde en BD. 454 $.ajax({ 455 url: prestashop.urls.pages.order, 456 type: 'POST', 457 data: { 458 ajax: 1, 459 ajax_econt_save: 1, 460 econt_data: JSON.stringify(data) 461 }, 462 dataType: 'json', 463 complete: function () { 464 window._opcEcontDeliverySelected = true; 465 466 // Actualizar el hidden input del módulo Econt 467 $('#econt_delivery_info').val(JSON.stringify(data)); 468 469 // Forzar a OPC a refrescar el carrier (re-lee precio de BD) 470 prestashop.emit('opc-cart-updateCarrier'); 471 } 472 }); 473 }); 474 } 475 476 // boxnow - v3.1.0 - BoxNow 477 // El script inline del módulo (boxnow.tpl) solo carga el widget real del mapa
478 // (widget-cdn.boxnow.gr/map-widget/client/v5.js) dentro de un listener 479 // `window.addEventListener('load', ...)`. Ese evento del navegador dispara UNA 480 // sola vez, en la carga inicial de la página; OPC inyecta el bloque de shipping 481 // (y por tanto este script) siempre vÃa AJAX ($shippingContainer.html(...), incluso 482 // en el primer render), asà que cuando se ejecuta el `load` ya pasó hace rato y el 483 // callback nunca corre â v5.js (el que realmente pinta el mapa) nunca se solicita y 484 // el contenedor queda vacÃo. Mismo patrón que gelproximity/advanceddateofdelivery/ 485 // shaim_dpdparcelshop más abajo: recargamos el script del widget manualmente en cada 486 // refresh para que se monte sobre el contenedor recién inyectado. 487 if (JqueryElement.find('#boxnow-map-container').length > 0 && typeof window._bn_map_widget_config !== typeof undefined) { 488 let _script = document.createElement('script'); 489 _script.type = 'text/javascript';
490 _script.src = 'https://widget-cdn.boxnow.gr/map-widget/client/v5.js'; 491 JqueryElementContent.append(_script); 492 } 493 494 //support module: mondialrelay - v3.3.3 - ScaleDEV 495 //se hizo modificacion a medida en dicho modulo para poder funcionar. 496 if (typeof window.mondialrelayWidget !== typeof undefined) { 497 if (typeof MONDIALRELAY_SELECTED_RELAY_IDENTIFIER !== typeof undefined) { 498 window.mondialrelayWidget.savedRelay = MONDIALRELAY_SELECTED_RELAY_IDENTIFIER; 499 } 500 501 $(window.mondialrelayWidget).trigger('mondialrelay.ready'); 502 503 // if (typeof window.mondialrelayWidget !== typeof undefined) { 504 // $(document).off('click', window.mondialrelayWidget.save_button); 505 // } 506 507 // let _script = document.createElement('script'); 508 // _script.type = 'text/javascript'; 509 // _script.src = prestashop.urls.base_url + 'modules/mondialrelay/views/js/front/checkout/checkout-17.js'; 510 // JqueryElementContent.append(_script); 511 512 // if (typeof window.mondialrelayWidget !== typeof undefined) { 513 // if (typeof MONDIALRELAY_SELECTED_RELAY_IDENTIFIER !== typeof undefined) { 514 // window.mondialrelayWidget.savedRelay = MONDIALRELAY_SELECTED_RELAY_IDENTIFIER; 515 // } 516 517 // $(window.mondialrelayWidget).trigger('mondialrelay.ready'); 518 // } 519 } 520 521 //gelproximity - v1.2.9 - iModuli.it 522 if (typeof gelprx_conf !== typeof undefined) { 523 let _script = document.createElement('script'); 524 _script.type = 'text/javascript'; 525 _script.src = prestashop.urls.base_url + 'modules/gelproximity/views/js/8/front.js'; 526 JqueryElementContent.append(_script); 527 } 528 529 /* Smartpost Shipping - v1.2.13 - Mijora.lt */ 530 if (typeof ItellaModule !== typeof undefined) { 531 ItellaModule.init(); 532 } 533 534 /* Venipak Shipping - v1.1.5 - mijora.lt */ 535 if (typeof venipak_custom_modal !== typeof undefined) { 536 venipak_custom_modal(); 537 } 538 539 //swastarkencl - v3.6.0 - Softwareagil 540 if (typeof swastarkenclRunOnReady !== typeof undefined) { 541 swastarkenclRunOnReady(); 542 } 543 544 /* advanceddateofdelivery - v3.1.0 - Créatym */ 545 if (typeof cartDatesDelivery !== typeof undefined) { 546 JqueryElementContent.find('.delivery-options').after($('#advanceddateofdelivery')); 547 548 let _script = document.createElement('script'); 549 _script.type = 'text/javascript'; 550 _script.src = prestashop.urls.base_url + 'modules/advanceddateofdelivery/views/js/cart.js'; 551 JqueryElement.append(_script); 552 } 553 554 /* shaim_dpdparcelshop - v2.3.10 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 555 if (typeof updateCarrierExtraContentDpdparcelshop !== typeof undefined) { 556 let _script = document.createElement('script'); 557 _script.type = 'text/javascript'; 558 _script.src = prestashop.urls.base_url + '/modules/shaim_dpdparcelshop/shaim_dpdparcelshop.js'; 559 JqueryElement.append(_script); 560 561 updateCarrierExtraContentDpdparcelshop(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 562 563 $('.psmoduly_balikovy_modul').on('show.bs.modal', (e) => { 564 $(e.currentTarget).find('.close').attr('data-bs-dismiss', 'modal'); 565 }); 566 } 567 568 /* shaim_zasilkovna_widget - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 569 if (typeof updateCarrierExtraContentZasilkovnaWidget !== typeof undefined) { 570 let _script = document.createElement('script'); 571 _script.type = 'text/javascript'; 572 _script.src = prestashop.urls.base_url + '/modules/shaim_zasilkovna_widget/shaim_zasilkovna_widget.js'; 573 JqueryElement.append(_script); 574 575 LoadNowZasilkovnaWidget(); 576 577 updateCarrierExtraContentZasilkovnaWidget(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 578 } 579 580 /* shaim_baliknapostu - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 581 if (typeof updateCarrierExtraContentBalikNaPostu !== typeof undefined) { 582 let _script = document.createElement('script'); 583 _script.type = 'text/javascript'; 584 _script.src = prestashop.urls.base_url + '/modules/shaim_baliknapostu/shaim_baliknapostu.js';
585 JqueryElement.append(_script); 586 587 LoadNowBaliknapostu(); 588 589 updateCarrierExtraContentBalikNaPostu(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 590 } 591 592 /* shaim_balikovna - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 593 if (typeof updateCarrierExtraContentBalikovna !== typeof undefined) { 594 let _script = document.createElement('script'); 595 _script.type = 'text/javascript'; 596 _script.src = prestashop.urls.base_url + '/modules/shaim_balikovna/shaim_balikovna.js'; 597 JqueryElement.append(_script); 598 599 LoadNowBalikovna(); 600 601 updateCarrierExtraContentBalikovna(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 602 } 603 604 /* shaim_glsparcelshop - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 605 if (typeof updateCarrierExtraContentGLSParcelShop !== typeof undefined) { 606 let _script = document.createElement('script'); 607 _script.type = 'text/javascript'; 608 _script.src = prestashop.urls.base_url + '/modules/shaim_glsparcelshop/shaim_glsparcelshop.js'; 609 JqueryElement.append(_script); 610 611 LoadNowGlsparcelshop(); 612 613 updateCarrierExtraContentGlsparcelshop(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 614 } 615 616 /* shaim_pplparcelshop - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 617 if (typeof updateCarrierExtraContentPplparcelshop !== typeof undefined) { 618 let _script = document.createElement('script'); 619 _script.type = 'text/javascript'; 620 _script.src = prestashop.urls.base_url + '/modules/shaim_pplparcelshop/shaim_pplparcelshop.js'; 621 JqueryElement.append(_script); 622 623 LoadNowPplparcelshop(); 624 625 updateCarrierExtraContentPplparcelshop(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 626 } 627 628 /* shaim_intime - v2.3.7 - Dominik Shaim Ulrich (www.psmoduly.cz / www.openservis.cz) */ 629 if (typeof updateCarrierExtraContentIntime !== typeof undefined) { 630 let _script = document.createElement('script'); 631 _script.type = 'text/javascript'; 632 _script.src = prestashop.urls.base_url + '/modules/shaim_intime/shaim_intime.js'; 633 JqueryElement.append(_script); 634 635 LoadNowIntime(); 636 637 updateCarrierExtraContentIntime(parseInt(JqueryElement.find(".delivery_option_radio:checked").val())); 638 } 639 640 //gmparcellocker - v1.4.1 - GreenMouseStudio.com 641 if (typeof gmParcelLockerAjaxUrl !== typeof undefined && JqueryElement.find('#easypack-widget').length > 0) { 642 window.easyPack.dropdownWidget('easypack-widget', function (point) { 643 const pointData = point.name + '| ' + point.address.line1 + '| ' + point.address.line2; 644 $.ajax({ 645 url: gmParcelLockerAjaxUrl, 646 type: 'POST', 647 crossDomain: true, 648 data: {cartId: gmCartId, pointData: pointData}, 649 async: true, 650 dataType: "json", 651 headers: {"cache-control": "no-cache"}, 652 success: function (data) { 653 if (data.msg == 'OK') { 654 $('.chosen-parcel').html(pointData.split('|').join(',')); 655 656 if (typeof window.checkGmParcellocker !== typeof undefined) { 657 window.gmparcellockerCheckStoreSelected(JqueryElement); 658 } 659 } 660 }, 661 error: function (jqXHR, textStatus) { 662 console.log(jqXHR.responseText); 663 } 664 }); 665 }); 666 667 window.gmparcellockerCheckStoreSelected = function(JqueryElement) { 668 if (JqueryElement.find('.gmparcellocker-button').is(':visible')) { 669 window.checkGmParcellocker = true; 670 if (JqueryElement.find('.chosen-parcel').text().length > 3) { 671 window.gmparcellockerStoreSelected = true; 672 } else { 673 window.gmparcellockerStoreSelected = false;
674 } 675 } else { 676 if (window.checkGmParcellocker) { 677 window.gmparcellockerStoreSelected = true; 678 window.checkGmParcellocker = false; 679 } 680 } 681 } 682 683 const gmparcellocker = window.setInterval(function () { 684 if (typeof window.checkGmParcellocker !== typeof undefined) { 685 window.gmparcellockerCheckStoreSelected(JqueryElement); 686 687 clearInterval(gmparcellocker); 688 } 689 }, 500); 690 } 691 692 //support module: sendcloud - v1.5.2 - Sendcloud _ sendcloudv2 - v2.0.24 - Sendcloud 693 const scriptSendCloud = $('script[src^="https://servicepoints.sendcloud.sc/shops/prestashop/"]').attr('src'); 694 const scriptSendCloudV2 = $('script[src^="https://servicepoints.sendcloud.sc/shops/prestashop_v2/"]').attr('src'); 695 if (typeof scriptSendCloud !== typeof undefined || typeof scriptSendCloudV2 !== typeof undefined) { 696 let _script = document.createElement('script'); 697 _script.type = 'text/javascript'; 698 _script.src = scriptSendCloud || scriptSendCloudV2; 699 JqueryElementContent.append(_script); 700 } 701 702 // correosoficial - v1.2.8.0 - Grupo Correos 703 if ($('.carrier-extra-content .correos_oficial').length > 0) { 704 let _script = document.createElement('script'); 705 _script.type = 'text/javascript'; 706 _script.src = prestashop.urls.base_url + '/modules/correosoficial/js/checkout.js'; 707 JqueryElement.append(_script); 708 } 709 710 //support module: seur - v2.5.9 - de Seur 711 if (typeof initSeurCarriers !== typeof undefined) { 712 initSeurCarriers(); 713 } 714 715 // payseradelivery - v1.5.2- Paysera 716 if ($('.paysera-delivery-gateway-content').length > 0) { 717 let _script = document.createElement('script'); 718 _script.type = 'text/javascript'; 719 _script.src = prestashop.urls.base_url + '/modules/payseradelivery/views/js/frontend/terminal_selection.js'; 720 JqueryElement.append(_script); 721 } 722 723 // darique - v3.0.10 - Silbersaiten 724 if (typeof dariqueModule !== typeof undefined) { 725 dariqueModule.initLoaderBlock(); 726 dariqueModule.loadPresentsBlock(0, []); 727 } 728 729 /* chronopost - v7.6.4 - Chronopost Official */ 730 if (typeof cleanContainers !== 'undefined' && typeof toggleRelaisMap !== 'undefined') { 731 // Reset stale Leaflet state (OPC AJAX rebuilds the DOM, relais_map queda huérfano) 732 if (typeof relais_map !== 'undefined' && relais_map !== null) { 733 try { relais_map.remove(); } catch (e) {} 734 relais_map = null; 735 } 736 if (typeof marker_group !== 'undefined') marker_group = null; 737 if (typeof map_markers !== 'undefined') map_markers = {}; 738 // Reset interaction guards (evita que initRelaisMap() quede bloqueado) 739 if (typeof lastMapInteractionTime !== 'undefined') lastMapInteractionTime = 0; 740 if (typeof userIsInteractingWithMap !== 'undefined') userIsInteractingWithMap = false; 741 if (typeof userJustSelectedRelayPoint !== 'undefined') userJustSelectedRelayPoint = false; 742 743 // cleanContainers() N veces: cada dummy extra genera un contenedor duplicado 744 const chronoDummies = document.querySelectorAll('[id="chronorelais_dummy_container"]').length; 745 for (let i = 0; i < Math.max(chronoDummies, 1); i++) { 746 cleanContainers(); 747 } 748 toggleRelaisMap( 749 typeof cust_address_clean !== 'undefined' ? cust_address_clean : '', 750 typeof cust_codePostal !== 'undefined' ? cust_codePostal : '', 751 typeof cust_city !== 'undefined' ? cust_city : '' 752 ); 753 754 // Fallback RELAISEUROPE_ID=-1: toggleRelaisMap oculta el contenedor, forzamos show 755 const $chronoContainer = $('#chronorelais_container'); 756 if ($chronoContainer.length && !$chronoContainer.is(':visible')) { 757 const isInsideSelectedOption = $chronoContainer.closest('.delivery-option-selected').length > 0 758 || $chronoContainer.parent('.carrier-extra-content').prev('.delivery-option-selected').length > 0; 759 760 if (isInsideSelectedOption && typeof initRelaisMap !== 'undefined') { 761 $chronoContainer.show(); 762 $('#chrono_postcode_controls').show(); 763 initRelaisMap( 764 typeof cust_address_clean !== 'undefined' ? cust_address_clean : null, 765 typeof cust_codePostal !== 'undefined' ? cust_codePostal : null, 766 typeof cust_city !== 'undefined' ? cust_city : null, 767 typeof cust_country !== 'undefined' ? cust_country : null 768 ); 769 } 770 } 771
772 // OPC fix: Leaflet cachea el tamaño del contenedor al inicializarse. Como 773 // OPC inyecta el HTML del carrier por AJAX y cleanContainers() mueve 774 // #chronorelais_container al delivery-option activo, el mapa se crea antes 775 // de que el contenedor tenga su ancho final â queda "pequeño" (tiles solo 776 // en una esquina) hasta que un resize de ventana fuerza invalidateSize(). 777 // El setTimeout(invalidateSize, 200) propio de chronopost corre una sola 778 // vez y a menudo antes de que el layout se estabilice. Un ResizeObserver 779 // reproduce el efecto del resize manual: cada vez que #chronorelais_map 780 // cambia de tamaño (0â500, reflow de columna, expand del delivery-option) 781 // reejecuta invalidateSize() sobre el mapa vigente. 782 const chronoMapEl = document.getElementById('chronorelais_map'); 783 if (chronoMapEl && typeof ResizeObserver !== 'undefined') { 784 if (window._opcChronoMapRO) { 785 try { window._opcChronoMapRO.disconnect(); } catch (e) {} 786 } 787 let chronoMapRaf = null; 788 window._opcChronoMapRO = new ResizeObserver(function () { 789 if (chronoMapRaf) cancelAnimationFrame(chronoMapRaf); 790 chronoMapRaf = requestAnimationFrame(function () { 791 if (typeof relais_map !== 'undefined' && relais_map 792 && typeof relais_map.invalidateSize === 'function') { 793 relais_map.invalidateSize({ animate: false }); 794 } 795 }); 796 }); 797 window._opcChronoMapRO.observe(chronoMapEl); 798 } 799 800 // Handler directo en #change_postcode: dispara antes que el delegado de body, 801 // resetea guards y evita doble AJAX via stopPropagation 802 const $changePostcodeBtn = $('#change_postcode'); 803 if ($changePostcodeBtn.length && typeof initRelaisMap !== 'undefined') { 804 $changePostcodeBtn.off('click.opc').on('click.opc', function (e) { 805 e.stopPropagation(); 806 e.preventDefault(); 807 if (typeof lastMapInteractionTime !== 'undefined') lastMapInteractionTime = 0; 808 if (typeof userIsInteractingWithMap !== 'undefined') userIsInteractingWithMap = false; 809 if (typeof userJustSelectedRelayPoint !== 'undefined') userJustSelectedRelayPoint = false; 810 const newPostcode = $('#relais_postcode').val(); 811 if (!newPostcode) return; 812 initRelaisMap(null, newPostcode, null, typeof cust_country !== 'undefined' ? cust_country : null); 813 }); 814 } 815 } 816 817 // dpdfrance - v6.5.1 - DPD France S.A.S 818 const _dpdFranceDisplay = typeof dpdFranceDisplayMethodBlock !== 'undefined' 819 ? dpdFranceDisplayMethodBlock 820 : (typeof dpdfrance_display !== 'undefined' ? dpdfrance_display : null); 821 if (_dpdFranceDisplay) { 822 _dpdFranceDisplay(); 823 // OPC fix: los métodos de display de dpdfrance intentan appendTo('.delivery-option > label') 824 // que no existe en OPC. Si algún bloque .dpdfrance_fo sigue atrapado en el contenedor 825 // oculto (#hook-hookDisplayAfterCarrier), lo movemos al .delivery-option activo. 826 const $activeOption = $('[id^=delivery_option]:checked').closest('.delivery-option'); 827 $('#hook-hookDisplayAfterCarrier .dpdfrance_fo').each(function () { 828 $activeOption.append($(this)); 829 }); 830 831 // OPC fix: dpdfrance vincula su listener de click en DOMContentLoaded sobre los elementos 832 // de tabla originales. Cuando OPC re-renderiza la sección de carriers esos elementos son 833 // destruidos y los listeners se pierden. Re-vinculamos con delegación en document para que 834 // sobreviva a futuros re-renders (namespace .dpdfrance-opc evita duplicados). 835 $(document).off('click.dpdfrance-opc').on( 836 'click.dpdfrance-opc', 837 '#dpdfrance_retail_point_table tr.dpdfrance_lignepr,' + 838 '#dpdfrance_locker_point_table tr.dpdfrance_lignepr,' + 839 '#dpdfrance_relais_point_table tr.dpdfrance_lignepr', 840 function (event) { 841 event.preventDefault(); 842 const pudoId = $(this).data('relayId'); 843 if (typeof dpdFranceRegisterPudo !== 'undefined') { 844 dpdFranceRegisterPudo(pudoId); 845 } else if (typeof dpdfrance_registerPudo !== 'undefined') { 846 dpdfrance_registerPudo(pudoId); 847 } 848 } 849 ); 850 } 851 }); 852 853 // Payment - Events 854 prestashop.on('opc-payment-getPaymentList-complete', (params) => { 855 const { JqueryElement } = params; 856 857 let paymentList = {}; 858 JqueryElement.find('.payment_radio').each(function (i, item) { 859 paymentList[$(item).val()] = $(item).data('module-version'); 860 }); 861 862 // Deferred Checkout â pre-deferred (visitante en layout unified, sin `?ck=`): 863 // los módulos en `paymentListNeedDeferredPage` se muestran solo como preview 864 // (sólo tÃtulo + logo, ver `payment/_partials/list.tpl`). Excluirlos aquà 865 // evita que las compatibilidades de cada módulo (Stripe, Redsys, PayPal, 866 // Mollieâ¦) carguen sus SDKs/iframes e instalen listeners que mostrarÃan el 867 // form al hacer click en el radio. Tras Buy Now, la página deferred se 868 // recarga con `?ck=` activo y `OPC.Checkout.deferredPayment.active=true`, 869 // por lo que este filtro se desactiva y el SDK se monta normalmente. 870 (function () { 871 const opcConfig = window.OPC || {}; 872 const deferredList = (opcConfig.Payment 873 && opcConfig.Payment.paymentListNeedDeferredPage) || []; 874 if (deferredList.length === 0) { 875 return; 876 } 877 const isUnified = !!(opcConfig.General && opcConfig.General.isUnifiedLayout); 878 const isDeferredPage = !!(opcConfig.Checkout 879 && opcConfig.Checkout.deferredPayment 880 && opcConfig.Checkout.deferredPayment.active); 881 const isVisitor = !(opcConfig.General && opcConfig.General.isLogged); 882 if (!isVisitor || !isUnified || isDeferredPage) { 883 return; 884 } 885 deferredList.forEach((moduleName) => { 886 delete paymentList[moduleName]; 887 }); 888 })(); 889 890 // prevents rage clicking on submit button and related issues 891 const forms = JqueryElement.find('form'); 892 forms.each(function() { 893 const $form = $(this); 894 const $submitButton = $form.find('button[type="submit"]'); 895 896 $form.on('submit', function(e) { 897 if ($form.data('submitting')) { 898 e.preventDefault(); 899 return false;
900 } 901 902 $form.data('submitting', true); 903 $submitButton.prop('disabled', true).addClass('disabled'); 904 905 // Re-enable form after 5 seconds in case of error 906 setTimeout(function() { 907 $form.data('submitting', false); 908 $submitButton.prop('disabled', false).removeClass('disabled'); 909 }, 5000); 910 }); 911 }); 912 913 /* cryptopaymentslight - v1.0.3 - UNAS Holding */ 914 if (typeof initCryptoPaymentsLightUI === 'function') { 915 initCryptoPaymentsLightUI(); 916 } 917 918 /* wkauthorizepayment - v1.0.1 - Webkul */ 919 if ($('#wkauthorize_pay').length > 0) { 920 $('#payment-confirmation > .ps-shown-by-js > button').click(function(e) { 921 e.preventDefault(); 922 var is_authorize_slected = $('.payment-options').find("input[data-module-name='wkauthorizepayment']").is(':checked'); 923 if (is_authorize_slected) { 924 $('#wkauthorize_pay').click(); 925 return false; 926 } 927 }); 928 } 929 930 //ps_checkout - v2.15.4+ - PrestaShop 931 // En layouts unified pre-deferred + visitante (carrito sin cliente/dirección 932 // reales), `payment.tpl` suprime los hooks `displayPaymentTop` y 933 // `displayPaymentByBinaries`. Sin secciones `.js-payment-ps_checkout` no hay 934 // nada que renderizar â saltamos para no ocultar las radios de preview ni 935 // disparar `renderCheckout()` sobre un DOM vacÃo. El SDK arrancará con cart 936 // completo en la página deferred. 937 if (typeof window.ps_checkout !== typeof undefined && typeof JqueryElement !== typeof undefined 938 && JqueryElement.find('.js-payment-ps_checkout').length > 0 939 ) { 940 // Sólo crear el bus de eventos si el SDK aún no lo expuso. Sobrescribirlo 941 // tras la inicialización del SDK perderÃa la referencia interna que el SDK 942 // usa para emitir `payment-option-active`, y los contenedores ocultos 943 // abajo nunca se volverÃan a mostrar. 944 if (!window.ps_checkout.events) { 945 window.ps_checkout.events = new EventTarget(); 946 } 947 window.ps_checkout.events.addEventListener('payment-option-active', ({ detail }) => { 948 const { HTMLElementContainer } = detail; 949 $(HTMLElementContainer).parent().show(); 950 }); 951 952 JqueryElement.find('.payment_radio').each(function (i, item) { 953 const $item = $(item); 954 const moduleName = $item.data('module-name'); 955 const $container = $item.parents('#' + $item.prop('id') + '-container'); 956 957 if (-1 !== moduleName.search('ps_checkout')) { 958 $container.parent().hide(); 959 } 960 }); 961 962 if (typeof window.ps_checkout.renderCheckout === 'function') { 963 window.ps_checkout.renderCheckout(); 964 } 965 } 966 967 //paytpv - v7.7.1 - Paycomet 968 if (typeof paytpv_initialize !== typeof undefined) { 969 paytpv_initialize(); 970 } 971 972 /* Fmmgiftwrapping - v1.1.1 - FMM Modules */ 973 if (typeof initColorBox !== typeof undefined) { 974 initColorBox(); 975 } 976 977 //pts_stripe - v4.0.6 - PresTeamShop 978 if (typeof AppPTSS !== typeof undefined) { 979 AppPTSS.setup(); 980 } 981 982 //klarnapaymentsofficial - v2.2.3 - Prestaworks AB 983 if (typeof paymentList['klarnapaymentsofficial'] !== typeof undefined /*typeof kp_client_token !== typeof undefined*/) { 984 const klarna_options = $('.klarna-container'); 985 986 for (const element of klarna_options) { 987 const payment_category = element.id.substring(26, element.id.length); 988 $('input[data-module-name=klarnapayments_' + payment_category + '_module]') 989 .click(function () { 990 initiateKlarnaWidget(kp_client_token, '#' + element.id, payment_category); 991 }) 992 .parents('.module_payment_container').click(function () { 993 initiateKlarnaWidget(kp_client_token, '#' + element.i
993d, payment_category); 994 }); 995 } 996 997 $('#payment-confirmation > .ps-shown-by-js button').click(function(event) { 998 for (const element of klarna_options) { 999 const payment_category = element.id.substring(26, element.id.length); 1000 if ($('input[data-module-name=klarnapayments_' + payment_category + '_module]').is(':checked')) { 1001 event.preventDefault(); 1002 setupKlarnaAuthCall(payment_category); 1003 return; 1004 } 1005 } 1006 }); 1007 } 1008 1009 //redsysinsite - v2.2.2 - idnovate 1010 if (typeof paymentList['redsysinsite'] !== typeof undefined) { 1011 let _script = document.createElement('script'); 1012 _script.type = 'text/javascript'; 1013 _script.src = prestashop.urls.base_url + 'modules/redsysinsite/views/js/front.js'; 1014 JqueryElement.append(_script); 1015 } 1016 1017 //klarnapayment - v1.0.4 - Invertus 1018 if (typeof paymentList['klarnapayment'] !== typeof undefined) { 1019 let _script = document.createElement('script'); 1020 _script.type = 'text/javascript'; 1021 _script.src = prestashop.urls.base_url + 'modules/klarnapayment/views/js/front/payment/payment.js'; 1022 JqueryElement.append(_script); 1023 } 1024 1025 if (typeof prestashop !== typeof undefined && typeof prestashop.emit !== typeof undefined) { 1026 prestashop.emit('steco_event_updated'); 1027 } 1028 1029 //stripejs - v4.3.9 - NTS 1030 if (typeof paymentList['stripejs'] !== typeof undefined && typeof stripe_allow_cards !== typeof undefined && typeof StripePubKey !== typeof undefined) { 1031 let _script = document.createElement('script'); 1032 _script.type = 'text/javascript'; 1033 _script.src = prestashop.urls.base_url + 'modules/stripejs/views/js/stripe-prestashop.js'; 1034 JqueryElement.append(_script); 1035 } 1036 1037 //kf_paypal - v2.1.7 - KForge 1038 if (typeof paymentList['kfpaypal'] !== typeof undefined) { 1039 let _script = document.createElement('script'); 1040 _script.type = 'text/javascript'; 1041 _script.src = prestashop.urls.base_url + 'modules/kf_paypal/views/js/front.js'; 1042 JqueryElement.append(_script); 1043 } 1044 1045 //mollie - v5.0.1 - Mollie B.V. 1046 if (typeof paymentList['mollie'] !== typeof undefined) { 1047 let $mollieContainers = JqueryElement.find('.mollie-iframe-container'); 1048 if ($mollieContainers.length) { 1049 let _script = document.createElement('script'); 1050 _script.type = 'text/javascript'; 1051 _script.src = prestashop.urls.base_url + 'modules/mollie/views/js/front/mollie_iframe.js'; 1052 JqueryElement.append(_script); 1053 } 1054 } 1055 1056 //a4pauthorizenet - v2.0.1 - Addons4Presta 1057 if (typeof paymentList['a4pauthorizenet'] !== typeof undefined) { 1058 let _script = document.createElement('script'); 1059 _script.type = 'text/javascript'; 1060 _script.src = prestashop.urls.base_url + '/modules/a4pauthorizenet/views/js/a4pauthorizenet.js'; 1061 JqueryElement.append(_script); 1062 } 1063 1064 //braintreejs - v3.1.2 - Bellini Services 1065 if (typeof paymentList['braintreejs'] !== typeof undefined) { 1066 if (typeof braintreeHostedSetup !== typeof undefined) { 1067 braintreeHostedSetup(); 1068 } else if (typeof braintreePaypalSetup !== typeof undefined) { 1069 braintreePaypalSetup(); 1070 } else if (typeof braintreeSetup !== typeof undefined) { 1071 braintreeSetup(); 1072 } 1073 } 1074 1075 //support braintreeofficial: v1.2.5 - 202 ecommerce 1076 if (typeof BraintreeSubmitPayment !== typeof undefined) { 1077 var _script = document.createElement('script'); 1078 _script.type = 'text/javascript'; 1079 _script.src = prestashop.urls.base_url + 'modules/braintreeofficial/views/js/payment_bt.js';
1080 JqueryElement.append(_script); 1081 } else if (typeof BraintreePaypalSubmitPayment !== typeof undefined) { 1082 var _script = document.createElement('script'); 1083 _script.type = 'text/javascript'; 1084 _script.src = prestashop.urls.base_url + 'modules/braintreeofficial/views/js/payment_pbt.js'; 1085 JqueryElement.append(_script); 1086 } 1087 1088 //support paypalplus: v1.2.3 - PixelWeb 1089 if (typeof paymentList['paypalplus'] !== typeof undefined && typeof paypalplus !== typeof undefined) { 1090 $(document).ready(_paypalplus.ready); 1091 } 1092 1093 //revolutpayment - v2.2.4 - Revolut 1094 if (typeof paymentList['revolutpayment'] !== typeof undefined) { 1095 let _script = document.createElement('script'); 1096 _script.type = 'text/javascript'; 1097 _script.src = prestashop.urls.base_url + '/modules/revolutpayment/views/js/version17/revolut.payment.js'; 1098 JqueryElement.append(_script); 1099 1100 let _script2 = document.createElement('script'); 1101 _script2.type = 'text/javascript'; 1102 _script2.src = prestashop.urls.base_url + '/modules/revolutpayment/views/js/upsell.banner.js'; 1103 JqueryElement.append(_script2); 1104 } 1105 1106 //smallpay - v4.0.0 - SmallPay Srl 1107 if (typeof paymentList['smallpay'] !== typeof undefined) { 1108 let _script = document.createElement('script'); 1109 _script.type = 'text/javascript'; 1110 _script.src = prestashop.urls.base_url + '/modules/smallpay/views/js/front.js'; 1111 JqueryElement.append(_script); 1112 } 1113 1114 //payoutloud - v1.2.2 - AdsOutLoud LLC 1115 if (typeof paymentList['Google Pay'] !== typeof undefined && typeof paymentList['Apple Pay'] !== typeof undefined) { 1116 let _script = document.createElement('script'); 1117 _script.type = 'text/javascript'; 1118 _script.src = prestashop.urls.base_url + '/modules/payoutloud/views/js/payment_request.js'; 1119 JqueryElement.append(_script); 1120 } 1121 1122 // verifone - v3.2.1 - Connectors Team - Verifone 1123 if (typeof paymentList['verifone'] !== typeof undefined) { 1124 let _script = document.createElement('script'); 1125 _script.type = 'text/javascript'; 1126 _script.src = prestashop.urls.base_url + '/modules/verifone/views/assets/js/iframe.js'; 1127 JqueryElement.append(_script); 1128 } 1129 1130 // windcavepayments - v2.0.2 - Windcave 1131 if (typeof paymentList['windcavepayments'] !== typeof undefined) { 1132 let _script = document.createElement('script'); 1133 _script.type = 'text/javascript'; 1134 _script.src = prestashop.urls.base_url + '/modules/windcavepayments/views/js/iframe_payment.js'; 1135 JqueryElement.append(_script); 1136 } 1137 1138 // stripe_official - v3.7.2 (compatible v3.0.5+) - Stripe 1139 // Recargar checkout.js sin precaución duplica listeners nativos sobre los 1140 // radios â 2-3 mounts del paymentElement por click. Solución: 1141 // - Clonar payment-option con $.clone(true,true) (preserva jQuery, descarta 1142 // addEventListener nativo) y reemplazar. 1143 // - Dejarlas unchecked: el IIFE recargado evita su self-trigger de 5s y 1144 // dispara init exactamente 1 vez vÃa paymentFormInput.click() interno. 1145 // - Inyectar <label for="<radio-id>"> oculto: la tpl PS envuelve el radio 1146 // en <label> sin for=, lo que rompÃa setPaymentOptionsLoading() y dejaba 1147 // los radios disabled. 1148 // - Spinner sibling de #js-stripe-payment-element (un MutationObserver lo 1149 // retira al aparecer el iframe) para feedback de carga. 1150 if (typeof paymentList['stripe_official'] !== typeof undefined && compareVersion(paymentList["stripe_official"], '3.0.4') === 1) { 1151 window.stripe_official_loaded = false; 1152 1153 $(document).off('change.stripe_opc').on('change.stripe_opc', '[data-module-name="stripe_official"]', function() { 1154 if (window.stripe_official_loaded) { 1155 return; 1156 } 1157 window.stripe_official_loaded = true; 1158 1159 $('input[name="payment-option"]').each(function () { 1160 const $original = $(this); 1161 const $clone = $original.clone(true, true); 1162 $original.replaceWith($clone); 1163 1164 const radioId = $clone.attr('id'); 1165 if (radioId && !document.querySelector('label[for="' + radioId + '"]')) { 1166 const $hiddenLabel = $('<label aria-hidden="true" style="display:none"></label>') 1167 .attr('for', radioId); 1168 const $wrappingLabel = $clone.closest('label'); 1169 const $insertionPoint = $wrappingLabel.length ? $wrappingLabel : $clone; 1170 $insertionPoint.after($hiddenLabel); 1171 } 1172 }); 1173 1174 // Stripe cross-compat: the clone+replaceWith above replaces every
1175 // payment radio with a fresh node, orphaning the direct .on('change') 1176 // listeners that eventClickPaymentContainer() bound to the originals. 1177 // Those listeners manage the .selected CSS class on .payment-option 1178 // and .module_payment_container. Without rebinding, switching away from 1179 // Stripe leaves the Stripe container permanently marked as selected. 1180 // Fix: rebind a change handler to the new clone nodes and immediately 1181 // sync the .selected state to whichever radio is currently checked. 1182 $('input[name="payment-option"]').on('change.stripe_opc_selected', function () { 1183 $('.payment-option, .module_payment_container').removeClass('selected'); 1184 $(this).parents('.payment-option, .module_payment_container').addClass('selected'); 1185 }); 1186 // Sync current state now so the Stripe container shows as selected 1187 // (it was just selected to trigger this block) and everything else is clear. 1188 (function () { 1189 const $checked = $('input[name="payment-option"]:checked'); 1190 $('.payment-option, .module_payment_container').removeClass('selected'); 1191 $checked.parents('.payment-option, .module_payment_container').addClass('selected'); 1192 }()); 1193 1194 // paypal - cross-compat: el clone(true,true)+replaceWith de arriba 1195 // reemplaza TODOS los radios de pago, incluido el de PayPal. PaypalTools 1196 // (shortcut.js) guarda referencias directas a esos nodos <input> en 1197 // window.paypalTools{Show,Hidden}ElemenList y, en su listener 'input', 1198 // hace display = trigger.checked ? 'block' : 'none' sobre 1199 // [paypal-button-container]. Tras el reemplazo esas referencias apuntan a 1200 // nodos desconectados del DOM (checked siempre false) -> al re-seleccionar 1201 // PayPal el boton queda en display:none. A diferencia de la rama de reload 1202 // AJAX (que re-inyecta shortcut.js y re-registra los nodos), aqui no se 1203 // recarga el listado, asi que remapeamos cada referencia muerta a su 1204 // reemplazo vivo (mismo id / data-module-name) y re-sincronizamos. 1205 if (typeof paymentList['paypal'] !== typeof undefined) { 1206 const remapPaypalRefs = function (listName) { 1207 if (typeof window[listName] === typeof undefined || !window[listName]) { 1208 return; 1209 } 1210 Object.keys(window[listName]).forEach(function (key) { 1211 window[listName][key] = window[listName][key] 1212 .map(function (el) { 1213 if (!el || document.body.contains(el)) { 1214 return el; 1215 } 1216 let replacement = el.id ? document.getElementById(el.id) : null; 1217 if (!replacement && typeof el.getAttribute === 'function') { 1218 const moduleName = el.getAttribute('data-module-name'); 1219 if (moduleName) { 1220 replacement = document.querySelector( 1221 'input[name="payment-option"][data-module-name="' + moduleName + '"]' 1222 ); 1223 } 1224 } 1225 return replacement || el; 1226 }) 1227 .filter(function (el) { 1228 return el && document.body.contains(el); 1229 }); 1230 }); 1231 }; 1232
1233 remapPaypalRefs('paypalToolsShowElemenList'); 1234 remapPaypalRefs('paypalToolsHiddenElemenList'); 1235 1236 if (typeof window.PaypalTools !== typeof undefined && window.PaypalTools) { 1237 if (typeof window.PaypalTools.showElementsIfChecked === 'function') { 1238 window.PaypalTools.showElementsIfChecked(); 1239 } 1240 if (typeof window.PaypalTools.hideElementTillChecked === 'function') { 1241 window.PaypalTools.hideElementTillChecked(); 1242 } 1243 } 1244 } 1245 1246 let _script = document.createElement('script'); 1247 _script.type = 'text/javascript'; 1248 _script.src = prestashop.urls.base_url + '/modules/stripe_official/views/js/checkout.js'; 1249 JqueryElement.append(_script); 1250 1251 const stripePaymentEl = document.getElementById('js-stripe-payment-element'); 1252 if (stripePaymentEl && !document.getElementById('opc-stripe-loading')) { 1253 if (!document.getElementById('opc-stripe-loading-style')) { 1254 const styleEl = document.createElement('style'); 1255 styleEl.id = 'opc-stripe-loading-style'; 1256 styleEl.textContent = '@keyframes opcStripeSpin{to{transform:rotate(360deg)}}' 1257 + '#opc-stripe-loading{display:flex;align-items:center;justify-content:center;' 1258 + 'gap:0.6rem;padding:1.5rem;color:#6c757d;font-size:0.9em;}' 1259 + '#opc-stripe-loading .opc-stripe-spinner{display:inline-block;width:1.1em;' 1260 + 'height:1.1em;border:2px solid #d1d5db;border-top-color:#4f46e5;' 1261 + 'border-radius:50%;animation:opcStripeSpin 0.8s linear infinite;}'; 1262 document.head.appendChild(styleEl); 1263 } 1264 1265 const loadingText = (typeof OPC !== 'undefined' && OPC.Message && OPC.Message.loadingPaymentForm) 1266 || 'Loading payment form...'; 1267 const loader = document.createElement('div'); 1268 loader.id = 'opc-stripe-loading'; 1269 loader.innerHTML = '<span class="opc-stripe-spinner"></span><span></span>'; 1270 loader.lastElementChild.textContent = loadingText; 1271 stripePaymentEl.parentNode.insertBefore(loader, stripePaymentEl); 1272 1273 const removeLoader = () => { 1274 if (loader.parentNode) { 1275 loader.parentNode.removeChild(loader); 1276 } 1277 }; 1278 1279 const stripeLoadingObserver = new MutationObserver(() => { 1280 if (stripePaymentEl.querySelector('iframe')) { 1281 removeLoader(); 1282 stripeLoadingObserver.disconnect(); 1283 } 1284 }); 1285 stripeLoadingObserver.observe(stripePaymentEl, { childList: true, subtree: true }); 1286 1287 setTimeout(() => { 1288 removeLoader(); 1289 stripeLoadingObserver.disconnect(); 1290 }, 15000); 1291 } 1292 }); 1293 } 1294 1295 if (typeof window.StripeOfficialInitExpressCheckout === 'function') { 1296 window.StripeOfficialInitExpressCheckout(JqueryElement); 1297 } 1298 1299 //nexixpay - v7.0.5 - Nexi Payments SpA 1300 if (typeof paymentList['nexixpay'] !== typeof undefined) { 1301 let _script = document.createElement('script'); 1302 _script.type = 'text/javascript'; 1303 _script.src = prestashop.urls.base_url + '/modules/nexixpay/views/js/front_xpay.js'; 1304 JqueryElement.append(_script); 1305 } 1306 1307 // paypal - v6.6.0 - 202 ecommerce 1308 // Duplicación de [paypal-mark-container] (logos) y .paypal-buttons (smart button 1309 // iframes) en AJAX reload del listado de pagos. `Shortcut.addMarkTo` y 1310 // `Shortcut.initButton` añaden sin comprobar instancias previas; el HTML del 1311 // additional-information re-inyecta `<script src="shortcut.js" data-key="shortcut">` 1312 // en cada reload, lo que provoca que el navegador vuelva a cargar shortcut.js y 1313 // re-asigne `window.Shortcut` (perdiendo cualquier patch previo
1313). Mismo patrón 1314 // con `bnpl.js`/`window.BNPL`. 1315 // 1316 // "Detected container element removed from DOM": the PayPal SDK v5 fires this 1317 // entirely inside its own render() call - the full error stack is PayPal-internal 1318 // with no OPC frames. It cannot be caught by window.onerror, unhandledrejection, 1319 // or initButton() guards. The only interception point is window.paypal.Buttons 1320 // itself: wrap its render() to bail silently when the container is already detached. 1321 if (!window._opcPaypalButtonsPatch) { 1322 window._opcPaypalButtonsPatch = true; 1323 var _opcPatchPaypalButtons = function () { 1324 if (!window.paypal || !window.paypal.Buttons) return false; 1325 if (window.paypal.Buttons._opcPatched) return true; 1326 var origButtons = window.paypal.Buttons.bind(window.paypal); 1327 window.paypal.Buttons = function () { 1328 var component = origButtons.apply(this, arguments); 1329 if (component && typeof component.render === 'function' && !component.render._opcPatched) { 1330 var origRender = component.render.bind(component); 1331 component.render = function (container) { 1332 var el = typeof container === 'string' ? document.querySelector(container) : container; 1333 if (el instanceof Element && !document.body.contains(el)) { 1334 return Promise.resolve(); 1335 } 1336 return origRender(container); 1337 }; 1338 component.render._opcPatched = true; 1339 } 1340 return component; 1341 }; 1342 // Copy over SDK properties (Marks, Messages, eligibility, etc.) 1343 Object.keys(origButtons).forEach(function (key) { 1344 try { window.paypal.Buttons[key] = origButtons[key]; } catch (e) {} 1345 }); 1346 window.paypal.Buttons._opcPatched = true; 1347 return true; 1348 }; 1349 if (!_opcPatchPaypalButtons()) { 1350 // window.paypal loads async - poll until SDK is ready 1351 var _opcPaypalPollAttempts = 40; 1352 var _opcPaypalPollInterval = setInterval(function () { 1353 _opcPaypalPollAttempts -= 1; 1354 if (_opcPatchPaypalButtons() || _opcPaypalPollAttempts <= 0) { 1355 clearInterval(_opcPaypalPollInterval); 1356 } 1357 }, 250); 1358 } 1359 } 1360 1361 if (typeof paymentList['paypal'] !== typeof undefined) { 1362 if (JqueryElement.find('#ppplus-mb').length > 0) { 1363 let _script = document.createElement('script'); 1364 _script.type = 'text/javascript'; 1365 _script.src = prestashop.urls.base_url + '/modules/paypal/views/js/payment_mb.js?v=6.5.1'; 1366 JqueryElement.append(_script); 1367 } 1368 // 1. Limpieza reactiva: eliminar [paypal-mark-container] duplicados ya en DOM 1369 JqueryElement.find('[paypal-mark-container]').parent().each(function () { 1370 const $marks = $(this).children('[paypal-mark-container]'); 1371 if ($marks.length > 1) { 1372 $marks.not(':first').remove(); 1373 } 1374 }); 1375 1376 // 2. Misma limpieza para los smart-button iframes (zoid-paypal-buttons-*). 1377 // v6.6.0: cada llamada a Shortcut.initButton() / BNPL.initButton() llama a 1378 // SDK.Buttons({...}).render(this.button) que apendea otro .paypal-buttons. 1379 JqueryElement.find('[paypal-button-container], [paypal-bnpl-button-container]').each(function () { 1380 const $buttons = $(this).children('.paypal-buttons'); 1381 if ($buttons.length > 1) { 1382 $buttons.not(':first').remove(); 1383 } 1384 }); 1385 1386 // 3. Patch proactivo: wrap addMarkTo/initButton sobre Shortcut y BNPL para 1387 // limpiar antes de renderizar. La guard `_opcPatched` la ponemos en la 1388 // función wrapper (no en el objeto Shortcut/BNPL): cuando shortcut.js se 1389 // re-carga y reasigna obj.addMarkTo al original, nuestra guard ya no aparece 1390 // y volvemos a parchear. Si el objeto aún no existe (script async), reintento 1391 // con polling hasta 5s. 1392 const patchPaypalHelper = function (objName) { 1393 const obj = window[objName]; 1394 if (!obj) { 1395 return false;
1396 } 1397 1398 if (typeof obj.addMarkTo === 'function' && !obj.addMarkTo._opcPatched) { 1399 const origAddMarkTo = obj.addMarkTo.bind(obj); 1400 const wrapped = function (container, styles) { 1401 if (container instanceof Element) { 1402 container.querySelectorAll('[paypal-mark-container]').forEach(function (el) { 1403 el.remove(); 1404 }); 1405 } 1406 return origAddMarkTo(container, styles); 1407 }; 1408 wrapped._opcPatched = true; 1409 obj.addMarkTo = wrapped; 1410 } 1411 1412 if (typeof obj.initButton === 'function' && !obj.initButton._opcPatched) { 1413 const origInitButton = obj.initButton.bind(obj); 1414 const wrapped = function () { 1415 // Solo bloqueamos si el contenedor ya no esta en el DOM. 1416 // NO eliminamos hijos .paypal-buttons aqui: el SDK observa sus 1417 // propios iframes via MutationObserver y lanza internamente 1418 // "Detected container element removed from DOM" cuando detecta 1419 // que un hijo .paypal-buttons es removido - ese el error que 1420 // queremos suprimir. La limpieza de duplicados se hace en el 1421 // paso reactivo (seccion 2) antes de que el SDK este montado. 1422 if (obj.button instanceof Element && !document.body.contains(obj.button)) { 1423 return undefined; 1424 } 1425 try { 1426 return origInitButton.apply(this, arguments); 1427 } catch (e) { 1428 return undefined; 1429 } 1430 }; 1431 wrapped._opcPatched = true; 1432 obj.initButton = wrapped; 1433 } 1434 1435 return true; 1436 }; 1437 1438 ['Shortcut', 'BNPL'].forEach(function (objName) { 1439 if (patchPaypalHelper(objName)) { 1440 return; 1441 } 1442 let attempts = 25; 1443 const interval = setInterval(function () { 1444 attempts -= 1; 1445 if (patchPaypalHelper(objName) || attempts <= 0) { 1446 clearInterval(interval); 1447 } 1448 }, 200); 1449 }); 1450 1451 // 3. Restaurar listeners de visibilidad de PayPal en el nuevo contenedor de pagos 1452 // Problema: PaypalTools registra el listener 'input' en el contenedor de pagos original. 1453 // Tras el reload AJAX ese contenedor es reemplazado, el listener queda en el nodo 1454 // antiguo (desconectado del DOM), y la visibilidad de #payment-confirmation deja de 1455 // actualizarse al seleccionar/deseleccionar PayPal. 1456 if (typeof window.PaypalTools !== typeof undefined) { 1457 const $newPaymentContainer = JqueryElement.find('.payment-options, .payment__list'); 1458 const newContainer = $newPaymentContainer[0]; 1459 1460 if (typeof window.paypalToolsHiddenElemenList !== typeof undefined) { 1461 // Eliminar referencias a nodos ya removidos del DOM 1462 Object.keys(window.paypalToolsHiddenElemenList).forEach(function (key) { 1463 window.paypalToolsHiddenElemenList[key] = window.paypalToolsHiddenElemenList[key].filter( 1464 function (el) { return document.body.contains(el); } 1465 ); 1466 }); 1467 // Sincronizar estado inicial de visibilidad 1468 window.PaypalTools.hideElementTillChecked(); 1469 // Re-vincular listener al nuevo contenedor de pagos 1470 if (newContainer instanceof Element) { 1471 newContainer.addEventListener('input', window.PaypalTools.hideElementTillChecked); 1472 } 1473 } 1474
1475 if (typeof window.paypalToolsShowElemenList !== typeof undefined) { 1476 // Eliminar referencias a nodos ya removidos del DOM 1477 Object.keys(window.paypalToolsShowElemenList).forEach(function (key) { 1478 window.paypalToolsShowElemenList[key] = window.paypalToolsShowElemenList[key].filter( 1479 function (el) { return document.body.contains(el); } 1480 ); 1481 }); 1482 // Sincronizar estado inicial de visibilidad 1483 window.PaypalTools.showElementsIfChecked(); 1484 // Re-vincular listener al nuevo contenedor de pagos 1485 if (newContainer instanceof Element) { 1486 newContainer.addEventListener('input', window.PaypalTools.showElementsIfChecked); 1487 } 1488 } 1489 } 1490 } 1491 1492 // tfauthorizedotnet - v7.2.0 - presta_world 1493 if (typeof paymentList['tfauthorizedotnet'] !== typeof undefined) { 1494 let _script = document.createElement('script'); 1495 _script.type = 'text/javascript'; 1496 _script.src = prestashop.urls.base_url + 'modules/tfauthorizedotnet/views/js/front/DatPayment.js'; 1497 JqueryElement.append(_script); 1498 } 1499 1500 // mercadopago - v4.18.5 - mercadopago 1501 // Cada sub-checkout (custom/ticket/pse/standard-modal) inyecta vÃa AJAX un 1502 // <script src="https://sdk.mercadopago.com/js/v2"> + scripts internos + 1503 // un inline `window.onload = loadXxx();` que asume las dependencias YA 1504 // cargadas. En contexto AJAX los <script src> son asÃncronos: el inline 1505 // corre antes que el SDK v2/initializeXxx â "ReferenceError: MercadoPago 1506 // is not defined". Reintentamos cada loadXxx con polling hasta que las 1507 // dependencias estén disponibles (máx 5s). 1508 if (typeof paymentList['mercadopago'] !== typeof undefined) { 1509 const mpInits = [ 1510 { fn: 'loadCustom', needs: ['MercadoPago', 'initializeCustom'] }, 1511 { fn: 'loadTicket', needs: ['mpValidateSellerInfo', 'validateDocumentInputs', 'mercadoPagoFormHandlerTicket'] } 1512 ]; 1513 1514 mpInits.forEach(({ fn, needs }) => { 1515 if (typeof window[fn] !== 'function') { 1516 return; 1517 } 1518 1519 const tryRun = (attemptsLeft) => { 1520 const ready = needs.every((dep) => typeof window[dep] !== 'undefined'); 1521 if (ready) { 1522 try { 1523 window[fn](); 1524 } catch (e) { 1525 // No bloqueamos otros patches si una pasarela rompe. 1526 // eslint-disable-next-line no-console 1527 console.warn('[OPC][mercadopago] ' + fn + ' falló', e); 1528 } 1529 return; 1530 } 1531 if (attemptsLeft > 0) { 1532 setTimeout(() => tryRun(attemptsLeft - 1), 100); 1533 } 1534 }; 1535 1536 tryRun(50); 1537 }); 1538 } 1539 1540 // bridge - v1.0.13 - 202 ecommerce 1541 if (typeof paymentList['bridge'] !== typeof undefined) { 1542 let _script = document.createElement('script'); 1543 _script.type = 'text/javascript'; 1544 _script.src = prestashop.urls.base_url + '/modules/bridge/views/js/front/front.js'; 1545 JqueryElement.append(_script); 1546 } 1547 1548 // wkcodextrafee - v7.0.1 - wkcodextrafee 1549 if (typeof paymentList['wkcodextrafee'] !== typeof undefined) { 1550 let _script = document.createElement('script'); 1551 _script.type = 'text/javascript'; 1552 _script.src = prestashop.urls.base_url + '/modules/wkcodextrafee/views/js/wkcodpayment.js'; 1553 JqueryElement.append(_script); 1554 } 1555 1556 // everypay - v8.0.1 - everypay 1557 if (typeof paymentList['everypay'] !== typeof undefined && typeof EVERYPAY !== typeof undefined && typeof EVERYPAY.init !== typeof undefined) { 1558 EVERYPAY.init(); 1559 } 1560 1561 //btgooglepay - v1.1.2 - Business Tech 1562 //El módulo intenta renderizar el botón en DOMContentLoaded, antes de que OPC 1563 //termine de cargar las opciones de pago vÃa AJAX. El container #google-pay-button 1564 //no existe todavÃa y btgooglepay aborta con "Container #google-pay-button
1564not found". 1565 //Re-inicializamos cuando OPC completa el render y el container ya está en el DOM. 1566 if (typeof paymentList['btgooglepay'] !== typeof undefined 1567 && typeof window.initGooglePay === 'function' 1568 && typeof window.waitForGooglePay === 'function' 1569 && JqueryElement.find('#google-pay-button').length > 0 1570 ) { 1571 window.waitForGooglePay(window.initGooglePay); 1572 } 1573 1574 if (Object.keys(OPC.Compatibilities).length > 0) { 1575 if (typeof OPC.Compatibilities.orderfees_payment !== typeof undefined) { 1576 JqueryElement.find('input[name="payment-option"]').on('change', () => { 1577 prestashop.emit('opc-cart-getCartSummary', {loadPayment: false, loadShipping: false}) 1578 }); 1579 } 1580 } 1581 }); 1582 1583 prestashop.on('opc-payment-toggleOrderButton-after', (params) => { 1584 const { JqueryElement, selectedOption, confirmationSelector, showConfirmationSelector } = params; 1585 1586 //wkpaymentfee - v6.1.1 - Webkul 1587 if (typeof managePaymentFeeForMethod !== typeof undefined) { 1588 managePaymentFeeForMethod(JqueryElement.find('input[name="payment-option"]:checked')); 1589 } 1590 1591 // systempay - v1.23.1 - Lyra Network 1592 if (typeof window.KR !== typeof undefined && typeof selectedOption !== typeof undefined) { 1593 const $additionalInfo = JqueryElement.find('#' + selectedOption + '-additional-information'); 1594 if ($additionalInfo.find('[kr-form-token]').length > 0) { 1595 window.KR.renderElements('[kr-form-token]').catch(() => {}); 1596 } 1597 } 1598 1599 //ps_checkout - v8.5.2.0 - PrestaShop 1600 //Mostrar el botón de submit del SDK (#opc-checkout-finalize) dentro de la sección binary. 1601 //Para el funding source 'card' con Hosted Fields habilitados (cardFields.tpl), el SDK 1602 //renderiza los iframes de tarjeta dentro de #pay-with-{option.id}-form. OPC oculta ese 1603 //form (clase ps-hidden) cuando el radio es binary; lo forzamos visible sólo para card. 1604 //Nota: option.id lo autogenera PrestaShop ('payment-option-N'), no coincide con 1605 //module_name, por eso identificamos la selección vÃa data-module-name. 1606 if (typeof window.ps_checkout !== typeof undefined && typeof JqueryElement !== typeof undefined) { 1607 JqueryElement.find('.js-payment-binary #opc-checkout-finalize').show(); 1608 1609 const $selectedRadio = JqueryElement.find('input[name="payment-option"]:checked'); 1610 if ($selectedRadio.data('module-name') === 'ps_checkout-card') { 1611 JqueryElement.find('#pay-with-' + selectedOption + '-form') 1612 .removeClass('ps-hidden') 1613 .css('display', 'block'); 1614 } 1615 } 1616 1617 // paypal - v6.2.1 - 202 ecommerce 1618 const paypalEcWrongButtonMessage = JqueryElement.find('[paypal-ec-wrong-button-message]'); 1619 if (paypalEcWrongButtonMessage.length > 0) { 1620 if (JqueryElement.find('input[name="payment-option"]:checked').attr("data-module-name") === 'paypal') { 1621 JqueryElement.find(confirmationSelector + ' button').on('click', function(){ 1622 event.preventDefault(); 1623 event.stopPropagation(); 1624 paypalEcWrongButtonMessage.show(); 1625 }); 1626 } else { 1627 JqueryElement.find(confirmationSelector + ' button').off('click'); 1628 } 1629 } 1630 1631 //ets_payment_with_fee - v2.2.9 - ETS-Soft. 1632 if (typeof ets_cookie_module_name !== typeof undefined) { 1633 ets_cookie_payment_option = false; 1634 ajaxChangePaymentMethod = function (){}; 1635 1636 if (typeof ajaxChangePaymentMethodOPC !== typeof undefined) { 1637 ajaxChangePaymentMethodOPC(params); 1638 } else { 1639 ajaxChangePaymentMethodOPC = function(params) { 1640 const { JqueryElement, selectedOption } = params; 1641 1642 if (typeof selectedOption !== typeof undefined) { 1643 const $paymentSelected = JqueryElement.find('input[name="payment-option"]:checked'); 1644 const moduleName = $paymentSelected.data('module-name'); 1645 let paymentMethodId = 0; 1646 1647 if (JqueryElement.find('#pay-with-' + selectedOption + '-form input[name="id_payment_method"]').length > 0) { 1648 paymentMethodId = JqueryElement.find('#pay-with-' + selectedOption + '-form input[name="id_payment_method"]').val(); 1649 } 1650 1651 $.ajax({ 1652 url: '', 1653 data: 'ets_set_payment_option=1&module_name=' + moduleName + '&id_payment_method=' + paymentMethodId + '&payment_option=' + selectedOption, 1654 type: 'post', 1655 dataType: 'json', 1656 beforeSend: function () { 1657 JqueryElement.append($('<div class="loading-opc"/>')); 1658 }, 1659 complete: function () { 1660 JqueryElement.find('.loading-opc').remove(); 1661 } 1662 }); 1663 } 1664 }; 1665 1666 ajaxChangePaymentMethodOPC(params); 1667 } 1668 } 1669 1670 //hipay_enterprise - v2.17.0 - HiPay 1671 if (typeof setSelectedPaymentMethod !== typeof undefined) { 1672 setSelectedPaymentMethod(); 1673 } 1674 1675 //revolutpayment - v2.2.4 - Revolut 1676 if (typeof JqueryElement.find('input#' + selectedOption) !== typeof undefined) { 1677 if (JqueryElement.find('input#' + selectedOption).data('module-name') === 'Revolut Pay - Get Cashback' 1678 || (JqueryElement.find('input#' + selectedOption).val() === 'revolutpayment' 1679 && JqueryElement.find('input#' + selectedOption).closest('.module_payment_container').find('#revolutPayForm button').length > 0 1680 ) 1681 ) { 1682 JqueryElement.find(confirmationSelector + ' button').addClass('revolut_hide'); 1683 } else if (showConfirmationSelector) { 1684 JqueryElement.find(confirmationSelector + ' button').removeClass('revolut_hide'); 1685 } 1686 } 1687 1688 // paypalfeeplus - v1.3.2 - idnovate 1689 if (typeof movePPFPElements !== typeof undefined) { 1690 setInterval(() => { 1691 movePPFPElements(); 1692 }, 1000); 1693 } 1694 }); 1695 1696 prestashop.on('opc-payment-toggleOrderButton-validation', (params) => { 1697 const { JqueryElementMain, JqueryElement, JqueryElementShipping, confirmationAlertSelector } = params; 1698 1699 const extraContentCarrierSelected = JqueryElementShipping.find('.delivery_option_radio:checked').parents('.delivery-option').next(); 1700 1701 //Reset 1702 if (intervalPaymentToggleOrderButtonValidation !== null) { 1703 clearInterval(intervalPaymentToggleOrderButtonValidation); 1704 } 1705 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1706 1707 if (JqueryElementMain.find('#notifications > .alert').length > 0) { 1708 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1709 $(confirmationAlertSelector).text(JqueryElementMain.find('#notifications > .alert').text()); 1710 } 1711 1712 // correosexpress - v5.5.2 - IT Department 1713 const $elementCEX = $('#CEX'); 1714 if ($elementCEX.length > 0) { 1715 const hasOfficeProduct = $('input[name*=delivery_option]:checked').attr('producto'); 1716 if (hasOfficeProduct !== undefined) { 1717 if ($elementCEX.find('#texto_producto').length > 0) { 1718 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1719 } else { 1720 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1721 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1722 } 1723 } 1724 } 1725 1726 // gelproximity - v1.2.9 - iModuli.it 1727 if (typeof gelprx_conf !== typeof undefined && $('#gelproximity-app-no-pick-point-selected').length > 0) { 1728 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1729 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1730 } 1731 1732 //slmshipping - v1.0.3 - de Skroutz 1733 if (typeof _skroutzPointsMapWidgetOptions !== typeof undefined) { 1734 if ($('[data-target="#SkroutzMapModal"]').is(':visible')) { 1735 const skroutzPointId = jQuery('[name="skroutz_point_id"]').val(); 1736 1737 if ($.trim(skroutzPointId) == '' || $.trim(skroutzPointId) == null) { 1738 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1739 1740 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1741 } 1742 } 1743 } 1744 1745 /* Smartpost Shipping - v1.2.13 - Mijora.lt */ 1746 if (typeof ItellaModule !== typeof undefined) { 1747 const deliveryIdSelected = JqueryElementShipping.find('.delivery_option_radio:checked').val(); 1748 if (deliveryIdSelected == itella_carrier_pickup_id + ',' && $('#itella_pickup_point_id').val() === '') { 1749 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1750 1751 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1752 } 1753 } 1754 1755 /* Venipak Shipping - v1.1.5 - mijora.lt */ 1756 if (document.getElementById("mjvp-selected-terminal") !== null) { 1757 const selected_terminal = document.getElementById("mjvp-selected-terminal").value; 1758 1759 if ($('.mjvp-pp-container').is(':visible') && (selected_terminal == '' || selected_terminal == null || selected_terminal == 0)) { 1760 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1761 1762 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1763 } 1764 } 1765 1766 /* Omniva Shipping - v2.0.20 - Mijora */ 1767 if (typeof omnivalt_params !== typeof undefined) { 1768 const deliveryIdSelected = JqueryElementShipping.find('.delivery_option_radio:checked').val(); 1769 1770 if (deliveryIdSelected == omnivalt_params.methods.omniva_terminal + ',' && $('select[name="omnivalt_parcel_terminal"]').val() == "") { 1771 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1772 1773 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1774 } 1775 } 1776 1777 //glsshipping - v3.7.0 - GLS 1778 // selectPickup.js rellena #parcel_codigo vÃa postMessage del iframe y hace 1779 // un AJAX `?method=saveParcel` fire-and-forget sin emitir eventos PS. El 1780 // trigger('click') sobre payment-option no basta: si el cliente eligió el 1781 // carrier GLS Parcel Shop antes de seleccionar método de pago, no hay
1782 // :checked y la re-evaluación nunca corre. Forzamos getPaymentList para 1783 // re-rendear el bloque payment y disparar toggleOrderButton de nuevo. 1784 if (JqueryElementShipping.find('#parcelshopid').length > 0) { 1785 const parcelShopId = parseInt(JqueryElementShipping.find('#parcelshopid').val()); 1786 const deliveryIdSelected = parseInt(JqueryElementShipping.find('.delivery_option_radio:checked').val()); 1787 1788 if (parcelShopId === deliveryIdSelected && JqueryElementShipping.find('#parcel_codigo').val() === '') { 1789 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1790 1791 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1792 1793 intervalPaymentToggleOrderButtonValidation = setInterval(function () { 1794 if (JqueryElementShipping.find('#parcel_codigo').val() !== '') { 1795 clearInterval(intervalPaymentToggleOrderButtonValidation); 1796 1797 prestashop.emit('opc-payment-getPaymentList'); 1798 } 1799 }, 1000); 1800 } else { 1801 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1802 } 1803 } 1804 1805 //gmparcellocker - v1.4.1 - GreenMouseStudio.com 1806 if (typeof window.gmparcellockerStoreSelected !== typeof undefined) { 1807 if (!window.gmparcellockerStoreSelected) { 1808 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1809 1810 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1811 1812 intervalPaymentToggleOrderButtonValidation = setInterval(function () { 1813 if (window.gmparcellockerStoreSelected) { 1814 JqueryElement.find('input[name="payment-option"]:checked').trigger('click'); 1815 1816 clearInterval(intervalPaymentToggleOrderButtonValidation); 1817 } 1818 }, 1000); 1819 } else { 1820 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1821 } 1822 } 1823 1824 //samedaycourier - v1.8.5 - Sameday Courier 1825 // Map mode renders #locker_name; selector mode (lockers map off) renders #lockerIdSelector. 1826 var samedayLockerField = extraContentCarrierSelected.find('#locker_name').length 1827 ? extraContentCarrierSelected.find('#locker_name') 1828 : extraContentCarrierSelected.find('#lockerIdSelector'); 1829 1830 if (samedayLockerField.length) { 1831 if (samedayLockerField.val() === '' || typeof samedayLockerField.val() === typeof undefined) { 1832 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1833 1834 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1835 1836 intervalPaymentToggleOrderButtonValidation = setInterval(function () { 1837 if (samedayLockerField.val() !== '' && typeof samedayLockerField.val() !== typeof undefined) { 1838 JqueryElement.find('input[name="payment-option"]:checked').trigger('click'); 1839 1840 clearInterval(intervalPaymentToggleOrderButtonValidation); 1841 } 1842 }, 1000); 1843 } else { 1844 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1845 } 1846 } 1847 1848 //boxnow - v2.x - v3.1.0 - BoxNow 1849 if (extraContentCarrierSelected.find('#boxnow-map-container').length > 0) { 1850 const boxnowZipValue = extraContentCarrierSelected.find('.boxnow-zip').val(); 1851 if (typeof boxnowZipValue !== typeof undefined && boxnowZipValue === '') { 1852 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1853 1854 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1855 1856 intervalPaymentToggleOrderButtonValidation = setInterval(function () { 1857 const updatedZipValue = extraContentCarrierSelected.find('.boxnow-zip').val(); 1858 if (typeof updatedZipValue !== typeof undefined && updatedZipValue !== '') { 1859 JqueryElement.find('input[name="payment-option"]:checked').trigger('click'); 1860 1861 clearInterval(intervalPaymentToggleOrderButtonValidation); 1862 } 1863 }, 1000); 1864 } else { 1865 JqueryElement.data('opc-payment-toggleOrderButton-validation', true); 1866 } 1867 } 1868 1869 /* chronopost - v7.5.6 - Chronopost Official */ 1870 if (typeof CHRONORELAIS_ID !== 'undefined') { 1871 const deliveryIdSelected = JqueryElementShipping.find('.delivery_option_radio:checked').val(); 1872 const selectedCarrierId = deliveryIdSelected ? deliveryIdSelected.substr(0, deliveryIdSelected.indexOf(',')) : ''; 1873 const chronoRelaisIds = [CHRONORELAIS_ID, CHRONORELAIS_AMBIENT_ID, RELAISEUROPE_ID, RELAISDOM_ID, TOSHOPDIRECT_ID, TOSHOPDIRECT_EUROPE_ID].filter(id => id !== '-1'); 1874 if (chronoRelaisIds.includes(selectedCarrierId) && !$('input[name="chronorelaisSelect"]:checked').val()) { 1875 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1876 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1877 } 1878 } 1879 1880 // dpdfrance - v6.5.1 - DPD France S.A.S 1881 if (typeof dpdfranceRelaisCarrierId !== 'undefined') { 1882 const deliveryIdSelected = JqueryElementShipping.find('.delivery_option_radio:checked').val(); 1883 const selectedCarrierId = deliveryIdSelected ? deliveryIdSelected.substr(0, deliveryIdSelected.indexOf(',')) : ''; 1884 // Cubre los tres tipos de relay: global, locker (taquillas) y retail 1885 const dpdRelaisIds = [ 1886 dpdfranceRelaisCarrierId, 1887 typeof dpdfranceRelaisLockerCarrierId !== 'undefined' ? dpdfranceRelaisLockerCarrierId : null, 1888 typeof dpdfranceRelaisRetailCarrierId !== 'undefined' ? dpdfranceRelaisRetailCarrierId : null,
1889 ].filter(Boolean); 1890 if (dpdRelaisIds.includes(selectedCarrierId)) { 1891 // Determinar qué tabla relay está visible (retail > locker > relais) 1892 let dpdActiveTable = 'table#dpdfrance_relais_point_table'; 1893 if ($('table#dpdfrance_retail_point_table').is(':visible')) { 1894 dpdActiveTable = 'table#dpdfrance_retail_point_table'; 1895 } else if ($('table#dpdfrance_locker_point_table').is(':visible')) { 1896 dpdActiveTable = 'table#dpdfrance_locker_point_table'; 1897 } 1898 // Soporta versión antigua (:checked) y nueva (data-relay-select=true) 1899 const selectedPUDO = $(dpdActiveTable + ' [name=dpdfrance_relay_id]:checked, ' + dpdActiveTable + ' [name=dpdfrance_relay_id][data-relay-select="true"]'); 1900 if (!selectedPUDO.length || $(dpdActiveTable + ' .dpdfrance_relais_error').length > 0) { 1901 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1902 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1903 } 1904 } 1905 } 1906 1907 // econtdelivery - v2.0.3 - ITPartner 1908 // Bloquea el botón de pago si Econt está seleccionado pero no se ha elegido punto de entrega. 1909 if (JqueryElementShipping.find('#econt_delivery_customer_info_frame').length > 0) { 1910 const $econtFrame = JqueryElementShipping.find('#econt_delivery_customer_info_frame'); 1911 const $econtCarrierOption = $econtFrame.closest('.carrier-extra-content').prev('.delivery-option'); 1912 1913 if ($econtCarrierOption.find('.delivery_option_radio').is(':checked') && !window._opcEcontDeliverySelected) { 1914 JqueryElement.data('opc-payment-toggleOrderButton-validation', false); 1915 $(confirmationAlertSelector).text(OPC.Message.pickupStoreRequired); 1916 } 1917 } 1918 }); 1919 1920 // Cart - Events 1921 prestashop.on('opc-cart-getCartSummary-complete', (params) => { 1922 const { JqueryElement } = params; 1923 1924 // Actualiza el total del carrito del header si existe. 1925 if ($('.blockcart').length > 0) { 1926 const refreshURL = $('.blockcart').data('refresh-url'); 1927 1928 if (refreshURL !== '') { 1929 $.post(refreshURL, {}).then(function (response) { 1930 $('.blockcart').replaceWith($(response.preview).find('.blockcart')); 1931 }); 1932 } 1933 } 1934 1935 // darique - v3.0.10 - Silbersaiten 1936 if (typeof dariqueModule !== typeof undefined) { 1937 dariqueModule.initLoaderBlock(); 1938 dariqueModule.loadPresentsBlock(0, []); 1939 } 1940 1941 const carousels = document.querySelectorAll('.swiper-default-carousel'); 1942 if (carousels.length > 0 && typeof Swiper !== typeof undefined) { 1943 const defaultOptions = { 1944 pagination: { 1945 el: '.swiper-pagination-product', 1946 clickable: true, 1947 }, 1948 loop: false, 1949 speed: 600, 1950 lazy: { 1951 loadedClass: 'loaded', 1952 checkInView: true, 1953 }, 1954 watchOverflow: true, 1955 watchSlidesProgress: true, 1956 watchSlidesVisibility: true, 1957 navigation: { 1958 nextEl: '.swiper-button-next', 1959 prevEl: '.swiper-button-prev', 1960 }, 1961 slidesPerView: iqitTheme.pl_slider_p, 1962 slidesPerGroup: iqitTheme.pl_slider_p, 1963 breakpoints: { 1964 768: { 1965 slidesPerView: iqitTheme.pl_slider_t, 1966 slidesPerGroup: iqitTheme.pl_slider_t, 1967 }, 1968 992: { 1969 slidesPerView: iqitTheme.pl_slider_d, 1970 slidesPerGroup: iqitTheme.pl_slider_d, 1971 }, 1972 1200: { 1973 slidesPerView: iqitTheme.pl_slider_ld, 1974 slidesPerGroup: iqitTheme.pl_slider_ld, 1975 }, 1976 }, 1977 }; 1978 1979 if (iqitTheme.pl_crsl_autoplay) { 1980 defaultOptions.autoplay = { 1981 delay: 4500, 1982 disableOnInteraction: true, 1983 }; 1984 } 1985
1986 carousels.forEach(function(carousel) { 1987 let customOptions = {}; 1988 1989 const dataOptions = carousel.getAttribute('data-swiper_options'); 1990 1991 if (dataOptions) { 1992 try { 1993 customOptions = JSON.parse(dataOptions); 1994 } catch (err) { 1995 console.error('data swiper parse error', carousel, err); 1996 } 1997 } 1998 1999 const swiperOptions = Object.assign({}, defaultOptions, customOptions); 2000 new Swiper(carousel, swiperOptions); 2001 }); 2002 } 2003 }); 2004} 2005 2006window.addEventListener("load", function(){ 2007 // idxlopd - v3.12.2 - innovadeluxe 2008 // Insert necesario para el funcionamiento de la compatibilidad. 2009 // INSERT INTO `ps_idxlopd_compatible` (`id_button`, `name`, `text`, `controller`, `details`, `id_shop`) VALUES 2010 // ('btn-register', NULL, 'Crear cuenta', 'order', NULL, NULL), 2011 // ('btn-register-guest', NULL, 'Continuar como invitado', 'order', NULL, NULL); 2012 if (typeof makeButtonCompatible !== typeof undefined) { 2013 makeButtonCompatible(); 2014 } 2015});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.