1/** 2 * build a client configuration object if not yet available. 3 * The real values will be taken from first spirit. 4 */ 5function queryOrBuildPegasosObject() { 6 if (typeof window.pegasos === 'undefined') { 7 window.pegasos = new Object(); 8 } 9 10 return window.pegasos; 11} 12 13const webservicePrefix = pegasos.webservicePrefix + "/api/internal/cache-manager"; 14 15//die pegasos-variable wird bereits in der Seitenvorlage gesetzt und steht somit IMMER zur Verfügung 16pegasos = queryOrBuildPegasosObject(); 17 18pegasos.main_min_height = 550; //Minimale Höhe, die nicht unterschritten werden soll 19pegasos.utils = utils; 20 21(function () { 22 var MSGS = new Translation(); 23 24 var listener = { 25 notLoggedIn: function (id, data, response) { 26 logoutDueToInactivity(); 27 } 28 }; 29 HS.addListener("*", listener); 30 31 // always set the <code>pegasos.$mainObj</code> for basket binding 32 setMainObjectToPegasosForBasketBinding(); 33}()); 34 35/** 36 * Sets <code>pegasos.$mainObj</code> by evaluating the existing elements by their id's. 37 * <p> 38 * The value of <code>pegasos.$mainObj</code> is used in <code>basket-binding</code>! 39 * <p> 40 * The following order of id's is processed and used if found: 41 * <ul> 42 * <li><code>center</code> 43 * <li><code>content</code> 44 * <li><code>main</code> 45 * </ul> 46 */ 47function setMainObjectToPegasosForBasketBinding() { 48 var $mainObj = $("#center"); 49 if ($mainObj.length <= 0) { 50 $mainObj = $("#content"); 51 if ($mainObj.length <= 0) { 52 $mainObj = $("#main"); 53 } 54 } 55 pegasos.$mainObj = $mainObj; 56} 57 58// Global helper function used by hermes listener above and ASN. 59function logoutDueToInactivity() { 60 window.location.href = pegasos.actionPrefix + '/Logout.do?inactivity=true'; 61} 62 63 64/* 65 Rückgabe des Webtrekk-Context 66 Zunächst Prüfen, ob ein individueller Context existiert. 67 Wenn nicht wird der Allgemeine der Seite genommen 68*/ 69$.fn.getContext = function () { 70 var context = $(this).arg("context"); 71 72 if (context == "") { 73 context = pegasos.context; 74 } 75 76 return context; 77}; 78 79/* 80 Rückgabe des Webtrekk-AddedFromPage 81 Zunächst Prüfen, ob AddedFromPage existiert. 82 Wenn nicht wird der Context genommen 83*/ 84$.fn.getAddedFromPage = function () { 85 var addedFromPage = $(this).arg("addedFromPage"); 86 87 if (addedFromPage == "") { 88 if (typeof(pegasos.addedFromPage) === "undefined") { 89 addedFromPage = pegasos.context; 90 } else { 91 addedFromPage = pegasos.addedFromPage; 92 } 93 } 94 95 return addedFromPage; 96}; 97 98 99//Nachladen einer Datei 100 101function getPage(filePath) { 102 var fileContent = $.ajax({ 103 type: "GET", 104 async: false, 105 url: filePath, 106 cache: false 107 }); 108 109 110 if (fileContent.status == 200) { 111 return fileContent.responseText; 112 } 113 114 return ("File not found: '" + filePath + "'"); 115 116} 117 118/** 119 Texte verwalten 120 Verwendung: 121 Texte setzen z.B. 122 TEXT.asn = { 123 cancel: "Abbrechen" 124 ,... 125 } 126 Text holen z.B. 127 TEXT.get("asn.cancel"); => "Abbrechen" 128 TEXT.get("gibts.nicht"); => "TEXT.gibts.nicht" 129 130 */ 131if (window.TEXT === undefined) { 132 window.TEXT = { 133 get: function (key) { 134 var keys = key.split('.'); 135 var obj = this; 136 for (var kx = 0; kx < keys.length; ++kx) { 137 obj = obj[keys[kx]]; 138 if (obj === undefined) { 139 obj = "TEXT." + key; 140 LOG.warn("Not defined: " + obj); 141 break; 142 } 143 } 144 return obj; 145 } 146 }; 147} 148 149/** 150 From: http://ejohn.org/blog/simple-javascript-inheritance/ 151 Minor mod's by 152 @author UT, Actebis 153 */ 154(function () { 155 var initializing = false, fnTest = /xyz/.test(function () { 156 xyz; 157 }) ? /\b_super\b/ : /.*/; 158 // The base Class implementation (does nothing) 159 this.Class = function () { 160 }; 161 162 // Create a new Class that inherits from this class 163 Class.extend = function (prop) { 164 var _super = this.prototype; 165 166 // Instantiate a base class (but only create the instance, 167 // don't run the init constructor) 168 initializing = true; 169 var prototype = new this(); 170 initializing = false;
171 172 // Copy the properties over onto the new prototype 173 for (var name in prop) { 174 // Check if we're overwriting an existing function 175 prototype[name] = typeof prop[name] == "function" && 176 typeof _super[name] == "function" && fnTest.test(prop[name]) ? 177 (function (name, fn) { 178 return function () { 179 var tmp = this._super; 180 181 try { 182 // Add a new ._super() method that is the same method 183 // but on the super-class 184 this._super = _super[name]; 185 186 // The method only need to be bound temporarily, so we 187 // remove it when we're done executing 188 var ret = fn.apply(this, arguments); 189 } 190 finally { 191 this._super = tmp; 192 } 193 194 return ret; 195 }; 196 })(name, prop[name]) : 197 prop[name]; 198 } 199 200 // The dummy class constructor 201 function Class() { 202 var self = this; 203 // All construction is actually done in the init method 204 if (!initializing && this.init) 205 this.init.apply(this, arguments); 206 } 207 208 // Populate our constructed prototype object 209 Class.prototype = prototype; 210 211 // Enforce the constructor to be what we expect 212 Class.constructor = Class; 213 // And make this class extendable 214 Class.extend = arguments.callee; 215 return Class; 216 }; 217})(); 218 219/* Setzt für eine Gruppe von Objekten eine Einheitliche Höhe, ausgehend von dem Objekt mit der GröÃten Höhe */ 220function equalHeight(group) { 221 var tallest = 0; 222 223 if (group != undefined && group.length > 1) { 224 for (var i = 0; i < group.length; i++) { 225 var thisHeight = $(group[i]).height(); 226 if (thisHeight > tallest) { 227 tallest = thisHeight; 228 } 229 } 230 } 231 232 if (tallest > 0) { 233 group.height(tallest); 234 } 235} 236 237 238/** 239 Base64 Codec 240 http://www.webtoolkit.info/javascript-base64.html 241 UT, Actebis modified 242 */ 243 244window.Base64Codec = { 245 246 _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 247 248 // public method for encoding 249 encode: function (input) { 250 var out = ""; 251 var c1, c2, c3, e1, e2, e3, e4; 252 var i = 0; 253 254 input = this._utf8_encode(input); 255 256 while (i < input.length) { 257 258 c1 = input.charCodeAt(i++); 259 c2 = input.charCodeAt(i++); 260 c3 = input.charCodeAt(i++); 261 262 e1 = c1 >> 2; 263 e2 = ((c1 & 3) << 4) | (c2 >> 4); 264 e3 = ((c2 & 15) << 2) | (c3 >> 6); 265 e4 = c3 & 63; 266 267 if (isNaN(c2)) { 268 e3 = e4 = 64; 269 } else if (isNaN(c3)) { 270 e4 = 64; 271 } 272 273 var k = this._keyStr; 274 out = out + k.charAt(e1) + k.charAt(e2) + k.charAt(e3) + k.charAt(e4); 275 } 276 277 return out; 278 }, 279 280 // public method for decoding 281 decode: function (inp) { 282 var out = ""; 283 var k1, k2, k3; 284 var e1, e2, e3, e4; 285 var i = 0; 286 var k = this._keyStr; 287 288 inp = inp.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 289 290 while (i < inp.length) { 291 e1 = k.indexOf(inp.charAt(i++)); 292 e2 = k.indexOf(inp.charAt(i++)); 293 e3 = k.indexOf(inp.charAt(i++)); 294 e4 = k.indexOf(inp.charAt(i++)); 295 296 k1 = (e1 << 2) | (e2 >> 4); 297 k2 = ((e2 & 15) << 4) | (e3 >> 2); 298 k3 = ((e3 & 3) << 6) | e4; 299 300 out += String.fromCharCode(k1); 301 302 if (e3 != 64) out += String.fromCharCode(k2); 303 if (e4 != 64) out += String.fromCharCode(k3); 304 } 305 306 out = this._utf8_decode(out); 307 return out; 308 }, 309 310 // private method for UTF-8 encoding 311 _utf8_encode: function (string) { 312 string = string.replace(/\r\n/g, "\n"); 313 var utf = ""; 314 315 for (var n = 0; n < string.length; n++) { 316 317 var c = string.charCodeAt(n); 318 319 if (c < 128) { 320 utf += String.fromCharCode(c); 321 } 322 else if ((c > 127) && (c < 2048)) { 323 utf += String.fromCharCode((c >> 6) | 192); 324 utf += String.fromCharCode((c & 63) | 128); 325 } 326 else { 327 utf += String.fromCharCode((c >> 12) | 224); 328 utf += String.fromCharCode(((c >> 6) & 63) | 128); 329 utf += String.fromCharCode((c & 63) | 128); 330 } 331 } 332 return utf; 333 }, 334 335 // private method for UTF-8 decoding 336 _utf8_decode: function (utf) { 337 var s = ""; 338 var i = 0; 339 var c = c1 = c2 = 0; 340 341 while (i < utf.length) { 342 343 c = utf.charCodeAt(i); 344 345 if (c < 128) { 346 s += String.fromCharCode(c); 347 i++; 348 } 349 else if ((c > 191) && (c < 224)) { 350 c2 = utf.charCodeAt(i + 1); 351 s += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 352 i += 2; 353 } 354 else { 355 c2 = utf.charCodeAt(i + 1); 356 c3 = utf.charCodeAt(i + 2); 357 s += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 358 i += 3; 359 } 360 } 361 return s; 362 } 363}; // /Base64Codec 364 365function openSupportInformation() { 366 367 var MSGS = new Translation(); 368 369 var buttonClose = MSGS.get('label.core.close'); 370 var buttonCopyToClipboard = MSGS.get('label.core.support.information.to.clipboard'); 371 372 var buttonArray = [ 373 { 374 text: buttonClose, click: function () { 375 $(this).dialog("close"); 376 } 377 }, 378 {text: buttonCopyToClipboard, "class": 'js-supportInfoToClipboard'} 379 ]; 380 381 Dialog.createAndShow($(".supportInformation"), "form", { 382 title: MSGS.get('label.systemError.supportInfo'), 383 minHeight: 320, 384 height: 480, 385 minWidth: 800, 386 width: 1024, 387 modal: false, 388 resizable: true, 389 draggable: true, 390 classes: { 391 "ui-dialog": "debuginfo" 392 }, 393 buttons: buttonArray 394 }); 395 396 PegasosBlock.unblockPage(); 397} 398 399//Handelt es sich um eine Shop-Seite 400function isShopPage() { 401 if ($("#shop_logo").length > 0) { 402 return true; 403 } 404 405 return false;
406} 407 408/** 409 * Selects a range of characters. If 'start' and 'end' parameters are equal, or 'end' is left out, positions the caret before 410 * 'start'. 411 * 412 * @param start 413 * index of the first selected character, inclusive 414 * @param end 415 * index of the last selected character, exclusive. optional. 416 */ 417$.fn.selectRange = function (start, end) { 418 return this.each(function () { 419 if (!end) end = start; 420 if (this.setSelectionRange) { 421 this.focus(); 422 this.setSelectionRange(start, end); 423 } else if (this.createTextRange) { 424 var range = this.createTextRange(); 425 range.collapse(true); 426 range.moveEnd('character', end); 427 range.moveStart('character', start); 428 range.select(); 429 } 430 }); 431}; 432 433/** Helper to get the topmost parent window */ 434function getRootWindow() { 435 var win = window; 436 var root = win; 437 if (isALightboxOpen()) { // If lightbox is open, we can go directly to it's parent 438 root = win.parent; 439 } 440 return root; 441} 442 443/** 444 * @param dateInput, either a string like 2014-04-18T00:00:00+02:00, a JS date object 445 * or a unix timestamp returned by a java webservice 446 * @param type date, time, datetime 447 */ 448function formatDateByType(dateInput, type) { 449 450 var formatedDate = ""; 451 452 if (dateInput) { 453 var date; 454 if (dateInput instanceof Date) { 455 date = dateInput; 456 } else { 457 date = new Date(dateInput) 458 } 459 460 var format = pegasos.datepickerDateFormat; 461 switch (type) { 462 case "date": 463 formatedDate = formatDateByPattern(format, date); 464 break; 465 case "datetime": 466 formatedDate = formatDateByPattern(format, date); 467 hours = ('' + date.getHours()).padStart(2); 468 minutes = ('' + date.getMinutes()).padStart(2); 469 formatedDate = formatedDate + " " + hours + ":" + minutes; 470 break; 471 case "time": 472 hours = ('' + date.getHours()).padStart(2); 473 minutes = ('' + date.getMinutes()).padStart(2); 474 formatedDate = formatedDate + " " + hours + ":" + minutes; 475 break; 476 default: 477 console.log('This case is not implemented.') 478 break; 479 } 480 } 481 return formatedDate; 482} 483 484function PegasosCore() { 485 486 var self = this; 487 488 self.init = function () { 489 $("#showMessageKeysOn").click(function () { 490 var link = document.location.href; 491 document.location = self._buildLink(link, 'on', 'off', 'showMessageKeys'); 492 return false; 493 }); 494 $("#showMessageKeysOff").click(function () { 495 var link = document.location.href; 496 document.location = self._buildLink(link, 'off', 'on', 'showMessageKeys'); 497 return false; 498 }); 499 $("#showECDEVMessagesOn").click(function () { 500 var link = document.location.href; 501 document.location = self._buildLink(link, 'on', 'off', 'showECDEVMessages'); 502 return false; 503 }); 504 $("#showECDEVMessagesOff").click(function () { 505 var link = document.location.href; 506 document.location = self._buildLink(link, 'off', 'on', 'showECDEVMessages'); 507 return false; 508 }); 509 510 $(document).on("click",".js_actionTracking",function(event){ 511 if(typeof(pegasos.tracking) != "undefined" && typeof(webtrekkV3) != "undefined") { 512 var trackingLinkId = undefined; 513 if (event.currentTarget){ 514 trackingLinkId = $(event.currentTarget).data("trackingLinkId"); 515 } 516 if ((trackingLinkId === undefined || trackingLinkId.length === 0) && event.target){ 517 trackingLinkId = $(event.target).data("trackingLinkId"); 518 } 519 if (trackingLinkId !== undefined && trackingLinkId.length > 0) { 520 pegasosUtils.wtSendinfo_safe({linkId: trackingLinkId}); 521 } 522 } 523 }); 524 525 window.addEventListener("UsercentricsDataEvent", function (e) { 526 if (e.detail != null && (e.detail.event === "consent_status")) { 527 // Usercentrics V2 528 self.checkDeviceId(); 529 } else if (e.data != null && (e.data.event === "consents_initialized" || e.data.event === "consents_changed_finished" || e.data.event === "all_consents_given_status")) { 530 // Usercentrics V1 531 self.checkDeviceId(); 532 } 533 }); 534 }; 535 536 self._buildLink = function (link, newMode, oldMode, paramName) { 537 538 var queryStringPos = link.indexOf('?'); 539 if (queryStringPos > -1) { 540 541 var queryString = link.substring(queryStringPos + 1, link.length); 542 link = link.substring(0, queryStringPos); 543 544 var namedAnchorIdx = link.indexOf("#"); 545 if (namedAnchorIdx !== -1){ 546 link = link.substring(0, namedAnchorIdx); 547 } 548 549 var temp = ""; 550 var params = queryString.split('&'); 551 for (var i = 0; i < params.length; i++) { 552 var param = params[i]; 553 if (param.indexOf(paramName) === 0) { 554 // ignore this parameter 555 continue; 556 } 557 if (i > 0) { 558 temp += "&"; 559 } 560 561 temp += param; 562 } 563 queryString = temp; 564 565 if (queryString.length > 0) { 566 link += '?' + queryString + '&'; 567 } else { 568 link += '?'; 569 } 570 } else { 571 var namedAnchorIdx = link.indexOf("#"); 572 if (namedAnchorIdx !== -1){ 573 link = link.substring(0, namedAnchorIdx); 574 } 575 576 link += '?'; 577 } 578 579 link += paramName + '=' + newMode; 580 return link; 581 }; 582 583 self.checkDeviceId = function() { 584 585 if (!this.isLoggedIn()) { 586 return; 587 } 588 589 const ucDataLayerMissing = !(typeof(ucDataLayer) !== 'undefined' && Array.isArray(ucDataLayer) && ucDataLayer.length > 0); 590 if (ucDataLayerMissing) { 591 console.debug("Usercentrics not yet initialized"); 592 return; 593 } 594
595 const cookieConsentUsercentrics = ucDataLayer[ucDataLayer.length - 1]["ALSO Trust"]; 596 597 const key = "cookieConsent.deviceId." + pegasos.userId; 598 let cookieConsentSessionStorage = undefined; 599 const temp = sessionStorage.getItem(key); 600 if (typeof (temp) !== "undefined" && temp !== null && temp !== "undefined") { 601 try { 602 cookieConsentSessionStorage = JSON.parse(temp); 603 } catch (e) { 604 console.log(e); 605 } 606 } 607 608 if (cookieConsentSessionStorage == null || cookieConsentSessionStorage !== cookieConsentUsercentrics) { 609 if (cookieConsentUsercentrics) { 610 $.ajax({ 611 url: pegasos.webservicePrefix + "/security/device-id", 612 method: "POST", 613 contentType: "application/json", 614 data: "{}" 615 }).done(function (data, textStatus, jqXHR) { 616 console.debug("Device ID set"); 617 }).fail(function(jqXHR, textStatus, errorThrown){ 618 console.error("Error: " + textStatus); 619 }); 620 } else { 621 $.ajax({ 622 url: pegasos.webservicePrefix + "/security/device-id", 623 method: "DELETE", 624 data: "{}" 625 }).done(function (data, textStatus, jqXHR) { 626 console.debug("Device ID deleted"); 627 }).fail(function(jqXHR, textStatus, errorThrown){ 628 console.error("Error: " + textStatus); 629 }); 630 } 631 632 sessionStorage.setItem(key, JSON.stringify(cookieConsentUsercentrics)); 633 } 634 } 635 636 self.isLoggedIn = function () { 637 return window.pegasos 638 // userId present? 639 && window.pegasos.userId 640 // userId is numeric? 641 && /^\d+$/.test(window.pegasos.userId.trim()); 642 } 643 644 self.init(); 645} 646 647var pegasosCore = null; 648var $passwordExpire; 649 650$(document).ready(function () { 651 pegasosCore = new PegasosCore(); 652 $passwordExpire = $('#daysPasswordExpireIn'); 653 654 $passwordExpire 655 .off("click") 656 .on("click.passwordUpdate", "#gotoPasswordUpdate", gotoPasswordUpdate) 657 .on("click.dismissHint", "#dismissExpiryHint", dismissExpiryHint) 658 ; 659 660 var dismissDate = localStorage.getItem('pwdUpdateDismissed'); 661 662 if(!equalDates(dismissDate, getToday())) { 663 $passwordExpire.show(); 664 } else { 665 $passwordExpire.hide(); 666 } 667 668 pegasosCore.checkDeviceId(); 669}); 670 671function gotoPasswordUpdate(){ 672 $.ajax({ 673 url: webservicePrefix + "/pwdnotify/one", 674 method: "GET" 675 }).done(function (data, textStatus, jqXHR) {
676 var env = "weblogin.also.com/auth"; 677 var client = "pegasos-shop-" + window.pegasos.salesorgNo; 678 679 if(window.location.href.indexOf("pegdev.also.com") > -1 || window.location.href.indexOf("localhost") > -1){ 680 env = "weblogin.also.com/auth-t3"; 681 client = "pegasos-shop-q-" + window.pegasos.salesorgNo; 682 } 683 var locale = window.pegasos.locale; 684 locale = locale.substring(0, 2); 685 686 var redirecturl = window.location.href; 687 if(redirecturl.indexOf("?") !== -1){ 688 redirecturl = redirecturl.substring(0, redirecturl.indexOf("?")); 689 } 690 691 var redirectUrl = "https://" + env + "/realms/also-customers/protocol/openid-connect/auth?client_id=" + client + "&kc_locale=" + locale + "&response_type=code&kc_action=UPDATE_PASSWORD&redirect_uri=" + redirecturl; 692 693 window.location.href = redirectUrl; 694 }).fail(defaultErrorFunction); 695} 696 697function dismissExpiryHint() { 698 localStorage.setItem('pwdUpdateDismissed', getToday()); 699 $passwordExpire.hide(); 700} 701 702function defaultErrorFunction(e) { 703 console.log("Error has occurred. Error message: " + e); 704} 705 706function getToday() { 707 var d = new Date(); 708 return d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate(); 709} 710 711function equalDates(date1, date2) { 712 return date1 == date2; 713} 714 715//Using tracekit to parse stacktraces and send errors to server 716TraceKit.remoteFetching = false;//don't try to load JS on your own 717TraceKit.collectWindowErrors = false;//we're registering our own onError handler for window 718 719TraceKit.report.subscribe(function myLogger(errorReport, arg1, error){ 720 721 if (typeof pegasosConfiguration === "undefined" || pegasosConfiguration == null || !pegasosConfiguration.enabled("pegasos.sentry.enabled.javascript")){ 722 return; 723 } 724 725 let stacktrace = {"frames": []}; 726 727 let allALSOCode = true; 728 let allTraceKitCode = true; 729 730 if (errorReport.stack) { 731 for (const element of errorReport.stack) { 732 var newFrame = { 733 "filename": element.url, 734 "lineno": element.line, 735 "colno": element.column, 736 "function": element.func 737 }; 738 stacktrace.frames.push(newFrame); 739 740 if (newFrame.filename) { 741 if (newFrame.filename.toLowerCase().indexOf('also') === -1 && newFrame.filename.toLowerCase().indexOf('localhost') === -1) { 742 allALSOCode = false; 743 } 744 if (newFrame.filename.toLowerCase().indexOf('tracekit') === -1) { 745 allTraceKitCode = false; 746 } 747 } 748 } 749 } 750 751 //Don't report errors when they happen outside of also hosted code 752 //or if they happen only inside of TraceKit 753 if (!allALSOCode || allTraceKitCode){ 754 return; 755 } 756 757 const sentryData = { 758 "logger":"javascript", 759 "platform":"javascript", 760 "request":{ 761 "headers":{ 762 "User-Agent": navigator.userAgent, 763 "Referer": document.referrer 764 }, 765 "url": window.location.href 766 }, 767 "exception":{ 768 "type": error.type, 769 "value": error.message 770 }, 771 "stacktrace": stacktrace, 772 "culprit": error.filename, 773 "message": error.message, 774 "tags":{ 775 "UserId": window.pegasos ? window.pegasos.userId : "n/a", 776 "CustomerNo": window.pegasos ? window.pegasos.customerNo : "n/a", 777 "SalesorgNo": window.pegasos ? window.pegasos.salesorgNo : "n/a", 778 "Hostname": window.location.hostname 779 }, 780 "extra":{ 781 //This would be the place to add some extra data if required 782 //"someArbitraryKey": "someArbitraryValue" 783 }, 784 "user":{ 785 "id": window.pegasos ? window.pegasos.userId : "n/a" 786 } 787 }; 788 789 $.ajax({ 790 type: "POST", 791 url: pegasos.webservicePrefix + "/api/sentry", 792 dataType: "json", 793 contentType: "application/json", 794 data: JSON.stringify(sentryData) 795 }); 796}); 797 798 799 800window.addEventListener("error", function (event){ 801 802 if (event.error && (typeof pegasosUtils != "undefined" && pegasosUtils != null)){ 803 pegasosUtils.logToSentry(event.error, event.message, event.filename, event.type); 804 } 805 806 return false;
807}); 808 809/* not dependent on documentReady like pegasosCore */ 810var pegasosUtils = { 811 812 isDateHtml5Supported: function () { 813 var input = document.createElement('input'); 814 var value = 'a'; 815 input.setAttribute('type', 'date'); 816 input.setAttribute('value', value); 817 return (input.value !== value); 818 }, 819 820 getTimeString: function () { 821 var date = new Date(); 822 var datestring = ("0" + date.getHours()).slice(-2) + ":" + ("0" + date.getMinutes()).slice(-2) + ":" + ("0" + date.getSeconds()).slice(-2) + "." + ("00" + date.getMilliseconds()).slice(-3); 823 return datestring; 824 }, 825 826 debug: function (message) { 827 if (pegasos && pegasos.showDebugInformation) { 828 console.debug(this.getTimeString() + " - " + message); 829 } 830 }, 831 832 info: function (message) { 833 console.info(this.getTimeString() + " - " + message); 834 }, 835 836 warn: function (message) { 837 console.warn(this.getTimeString() + " - " + message); 838 }, 839 840 error: function (message) { 841 console.warn(this.getTimeString() + " - " + message); 842 }, 843 844 logToSentry: function(error, message, filename, type){ 845 if (typeof TraceKit !== "undefined" && TraceKit != null){ 846 var myError = { 847 message: message, 848 stack: error.stack, 849 filename: filename, 850 type: type 851 }; 852 853 TraceKit.report(myError); 854 } 855 }, 856 /** 857 * Checks if a default Pegasos webservice response is OK. 858 * @param data The default Pegasos webservice response object. 859 */ 860 isResponseOk: function (data) { 861 if (data.response) { 862 return data.response.sys && data.response.sys.returnCode && data.response.sys.returnCode === "OK"; 863 } 864 return data.sys && data.sys.returnCode && data.sys.returnCode === "OK"; 865 }, 866 867 isInViewport: function (element) { 868 var $element = $(element); 869 if ($element.length === 0) { 870 this.warn("Element '" + element + "' not found."); 871 return false; 872 } 873 var $window = $(window); 874 var elementTop = $element.offset().top; 875 var elementBottom = elementTop + $element.outerHeight(); 876 var viewportTop = $window.scrollTop(); 877 var viewportBottom = viewportTop + $window.height(); 878 return elementBottom > viewportTop && elementTop < viewportBottom; 879 }, 880 881 keysAsArrayfromMap: function (map) { 882 var keys = []; 883 if (!!map && map instanceof Map) { 884 if (!!map.keys) { 885 keys = Array.from(map.keys()); 886 } else { 887 for (var key in map) { 888 keys.push(key); 889 } 890 } 891 } 892 return keys; 893 }, 894 895 arrayFromSet: function (set) { 896 var arr = []; 897 if (!!set && set instanceof Set) { 898 if (!!Array.from) { 899 arr = Array.from(set); 900 } else { 901 set.forEach(function (v1, v2, s) { 902 arr.push(v2); 903 }); 904 } 905 } 906 return arr; 907 }, 908 909 /** 910 * Checks whether the rightpanel facade is present and the rightpanel is available and visible. 911 * This is the guard consumers should use before calling any window.RightpanelFacade method. 912 * Safe to call on any page: returns false if the rightpanel module is not loaded 913 * (unlike RightpanelFacade.isAvailable(), which assumes the module is present). 914 */ 915 isRightpanelUsable: function () { 916 return !!window.RightpanelFacade && window.RightpanelFacade.isAvailable(); 917 }, 918 919 wtSendinfo_safe: function(wt_object){ 920 if (typeof(webtrekkV3) !== "undefined" && !!pegasos.tracking && !!pegasos.tracking.wt) { 921 //if this hasn't been initialized properly it does not provide the required function. 922 //try to fix this by creating a new instance 923 if ((typeof pegasos.tracking.wt.sendinfo !== 'function') && (typeof pageConfig !== 'undefined')) { 924 pegasos.tracking.wt = new webtrekkV3(pageConfig); 925 } 926 927 if (typeof pegasos.tracking.wt.sendinfo === 'function') { 928 pegasos.tracking.wt.sendinfo(wt_object); 929 } 930 } 931 }, 932 933 /** 934 * returns the user currency iso code<br> 935 * respects also the customer currency if user currency should be ignored. 936 * as fallback "DEFAULT" is returned 937 * this is just the iso code (or "DEFAULT"), not the translation. The translation can be retrieved with getCurrencyTranslation() which uses this method internally. 938 * 939 * @param ignoreUserCurrency Ignore user currency and use customer currency or default currency instead. 940 * Needed when working with SAP prices as SAP does not know the user currency. 941 * @returns {*} 942 */ 943 getUserCurrency(ignoreUserCurrency = false){ 944 if (!ignoreUserCurrency){ 945 //value is stored in pegasos variable which is set on page load, so we can directly return it without calling the server 946 return pegasos.userCurrency; 947 } 948 949 //if userCurrency should be ignored, we want to have the currency used for SAP-prices. SAP does not know anything about userCurrency
950 if (typeof (pegasos.currencyForSapPrices) !== "undefined" && pegasos.currencyForSapPrices != null){ 951 return pegasos.currencyForSapPrices; 952 } 953 954 var userCurrency = "DEFAULT"; 955 956 $.ajax({ 957 type: "GET", 958 url: pegasos.webservicePrefix + "/currency/userCurrency?ignoreUserCurrency=" + ignoreUserCurrency, 959 async: false, 960 success: function (userCurrencyResponse) { 961 userCurrency = userCurrencyResponse; 962 }, 963 error: function () { 964 console.warn("userCurrency could not be retrieved from server, using 'DEFAULT' as fallback"); 965 } 966 }); 967 968 pegasos.currencyForSapPrices = userCurrency; 969 970 return pegasos.currencyForSapPrices; 971 }, 972 973 /** 974 * get userCurrency as iso code. Either take it from pegasos variable or retrieve it from server if not available.<br> 975 * If the currency is "DEFAULT" return the translation for "DEFAULT" which should be the correct iso code as well. 976 * @param ignoreUserCurrency 977 * @returns {*} 978 */ 979 getUserCurrencyIsoCode(ignoreUserCurrency = false){ 980 var userCurrency = this.getUserCurrency(ignoreUserCurrency); 981 if (userCurrency === "DEFAULT") { 982 //if currency is DEFAULT ask translation which should return the correct iso code 983 userCurrency = MSGS.get("label.core.currency." + userCurrency); 984 if (userCurrency === "Ft") { 985 //Workaround: we cannot work with Ft as it is no ISO-Code. so we need to switch to HUF. 986 //TODO: change value for "label.core.currency.DEFAULT" for 5850 to HUF 987 userCurrency = "HUF"; 988 } 989 } 990 return userCurrency; 991 }, 992 993 /** 994 * formats given price value according to the users currency 995 * If the currency translation is not valid (e.g. due to a missing or invalid translation) no currency will be shown but the number will still be formatted 996 * 997 * @param value 998 * @param ignoreUserCurrency Ignore user currency and use default currency instead. Needed when working with SAP prices as SAP does not know the user currency. Defaults to false. 999 * @param hideSymbol If true, format the value without showing currency code or symbol. 1000 * @returns {string} 1001 */ 1002 formatPrice(value, ignoreUserCurrency = false, hideSymbol = false) { 1003 if (value === null || value === undefined || value === '') { 1004 return ''; 1005 } 1006 1007 var numericValue = Number(value); 1008 if (Number.isNaN(numericValue)) { 1009 return ''; 1010 } 1011 1012 var currencyIsoCode = this.getUserCurrencyIsoCode(ignoreUserCurrency); 1013 1014 var numberFormatOptions = this.getNumberFormatOptionsForCurrency(currencyIsoCode); 1015 var locale = pegasos.locale.replace("_", "-"); 1016 1017 if (hideSymbol && numberFormatOptions.style === 'currency') { 1018 // Keep locale-specific rounding/fraction digits, but render as plain decimal without currency. 1019 var resolvedCurrencyOptions = new Intl.NumberFormat(locale, numberFormatOptions).resolvedOptions(); 1020 1021 numberFormatOptions = { 1022 style: 'decimal', 1023 minimumFractionDigits: resolvedCurrencyOptions.minimumFractionDigits, 1024 maximumFractionDigits: resolvedCurrencyOptions.maximumFractionDigits 1025 }; 1026 } 1027 1028 return new Intl.NumberFormat(locale, 1029 numberFormatOptions).format(value) 1030 }, 1031 1032 /** 1033 * returns number format options for the given currency translation.<br> 1034 * If the translation is not a valid currency iso code, it falls back to standard number formatting options. 1035 * 1036 * @param currencyIsoCode 1037 * @returns 1038 */ 1039 getNumberFormatOptionsForCurrency: function(currencyIsoCode){ 1040 1041 if (typeof (pegasos.currencyFormatOptions) === "undefined" || pegasos.currencyFormatOptions == null){ 1042 //store format options in pegasos variable to avoid calling this method multiple times for the same currency translation
1043 // which would cause multiple regex checks and console warnings in case of invalid translations 1044 pegasos.currencyFormatOptions = {}; 1045 } else if (pegasos.currencyFormatOptions[currencyIsoCode] !== undefined){ 1046 return pegasos.currencyFormatOptions[currencyIsoCode]; 1047 } 1048 1049 var formatOptions; 1050 1051 if(currencyIsoCode === "HUF"){ 1052 formatOptions = { 1053 style: 'currency', 1054 currency: currencyIsoCode,//use isoCode instead of translation 1055 currencyDisplay: 'symbol', 1056 maximumFractionDigits : 0 1057 }; 1058 } 1059 else { 1060 1061 const isCurrencyCodeValid = new RegExp(/^[A-Z]{3}$/); 1062 var currencyCodeTranslated = MSGS.get("label.core.currency." + currencyIsoCode); 1063 1064 if (!isCurrencyCodeValid.test(currencyCodeTranslated)) { 1065 //No valid currency code 1066 //Fallback to standard number formatting 1067 1068 console.warn("Currency '" + currencyCodeTranslated + "' is not a valid currency iso code. " + 1069 "Falling back to standard number formatting without currency."); 1070 1071 formatOptions = { 1072 style: 'decimal', 1073 maximumFractionDigits: 2, 1074 minimumFractionDigits: 2 1075 } 1076 } else { 1077 1078 //standard currency formatting 1079 formatOptions = { 1080 style: 'currency', 1081 currency: currencyCodeTranslated, 1082 currencyDisplay: 'code' 1083 }; 1084 } 1085 } 1086 1087 pegasos.currencyFormatOptions[currencyIsoCode] = formatOptions; 1088 1089 return formatOptions; 1090 }, 1091 1092 /** 1093 * formats a percentage value like 12.5 to a localized string like 12,50 % 1094 * or, if hideSymbol is true, like 12,50 1095 * 1096 * @param percentagePoints value in percent points, e.g. 12.5 for 12.5% 1097 * @param hideSymbol if true only the localized numeric value is returned without percent sign 1098 * @param minimumFractionDigits 1099 * @param maximumFractionDigits 1100 * @returns {string} 1101 */ 1102 formatPercentage(percentagePoints, hideSymbol=false, minimumFractionDigits = 0, maximumFractionDigits = 2) { 1103 if (percentagePoints === null || percentagePoints === undefined || percentagePoints === '') { 1104 return ''; 1105 } 1106 1107 var numericValue = Number(percentagePoints); 1108 if (Number.isNaN(numericValue)) { 1109 return ''; 1110 } 1111 1112 var locale = pegasos.locale.replace("_", "-"); 1113 1114 var numberFormatOptions = { 1115 minimumFractionDigits: minimumFractionDigits, 1116 maximumFractionDigits: maximumFractionDigits 1117 }; 1118 1119 if (hideSymbol) { 1120 return new Intl.NumberFormat(locale, numberFormatOptions).format(numericValue); 1121 } 1122 1123 return new Intl.NumberFormat(locale, { 1124 ...numberFormatOptions, 1125 style: 'percent' 1126 }).format(numericValue / 100); 1127 }, 1128 1129 /** 1130 * normalizes a localized number string to a number value. E.g. it converts "12,50" to 12.5 for German locale. 1131 * Needed to send normalized value to webservice 1132 * @param value 1133 * @returns {number} 1134 */ 1135 normalizeLocaleNumber(value){ 1136 1137 if (value === null || value === undefined || value.trim() === '') { 1138 return null; 1139 } 1140 1141 const parts = new Intl.NumberFormat(pegasos.locale.replace("_", "-")).formatToParts(12345.6); 1142 1143 const group = parts.find(part => part.type === 'group')?.value ?? ','; 1144 const decimal = parts.find(part => part.type === 'decimal')?.value ?? '.'; 1145 1146 const normalized = value 1147 .replaceAll(group, '') 1148 .replace(decimal, '.'); 1149 1150 return Number(normalized); 1151 }, 1152 1153 /** 1154 * returns the currency for display as it is defined by locale (either as symbol or code) 1155 * @param ignoreUserCurrency 1156 * @returns {string|*} 1157 */ 1158 getCurrencyDisplay(ignoreUserCurrency = false) { 1159 var currencyIsoCode = this.getUserCurrencyIsoCode(ignoreUserCurrency); 1160 var numberFormatOptions = this.getNumberFormatOptionsForCurrency(currencyIsoCode); 1161 1162 const formatter = new Intl.NumberFormat(pegasos.locale.replace("_", "-"), 1163 numberFormatOptions) 1164 1165 const parts = formatter.formatToParts(0); 1166 const currencyPart = parts.find(part => part.type === 'currency'); 1167 return currencyPart ? currencyPart.value : currencyIsoCode; 1168 }, 1169 formatDate: function (datemilis, includeTime = false) { 1170 if (!datemilis || datemilis <= 0 || isNaN(datemilis)) { 1171 return ""; 1172 } 1173 var dateobj = new Date(datemilis); 1174 const formatter = Intl.DateTimeFormat(pegasos.locale.replace("_", "-"),{ 1175 dateStyle: 'short', 1176 timeStyle: includeTime ? 'short' : undefined 1177 }); 1178 return formatter.format(dateobj); 1179 }, 1180 /** 1181 * Loads the notepad list from the server asynchronously and converts it to the legacy struts-result object if requested. 1182 * 1183 * callable like this: 1184 * pegasosUtils.fetchNotepadList(true).then(function (savedBaskets) { 1185 * self.savedBaskets = savedBaskets; 1186 * }); 1187 * 1188 * @param convertToLegacyStrutsResult 1189 * @returns {*} 1190 */ 1191 fetchNotepadList: function (convertToLegacyStrutsResult = false) { 1192 1193 return $.ajax({ 1194 type: "GET", 1195 url: pegasos.webservicePrefix + "/notepad/notepadList", 1196 dataType: "json" 1197 }).then(function (data) { 1198 1199 if (!convertToLegacyStrutsResult){ 1200 return data; 1201 } 1202 1203 //convert to old struts-result object so that the existing js-logic can be reused without changes 1204 var convertNotepad = function (notepad) { 1205 return { 1206 "notepadName": notepad.notepadName, 1207 "createdDate": notepad.createdDate, 1208 "createdDateFormatted": pegasosUtils.formatDate(notepad.createdDate), 1209 "createdBy": notepad.createdBy,
1210 "lastChangedDate": notepad.lastUpdatedDate, 1211 "lastChangedDateFormatted": pegasosUtils.formatDate(notepad.lastUpdatedDate), 1212 "lastChangedBy": notepad.lastUpdatedBy, 1213 "notepadId": String(notepad.cartId), 1214 }; 1215 }; 1216 1217 const publicNotepads = data.filter(function (notepad) { 1218 return notepad.notepadType === "PUN"; 1219 }).map(convertNotepad); 1220 const privateNotepads = data.filter(function (notepad) { 1221 return notepad.notepadType === "PRN"; 1222 }).map(convertNotepad); 1223 1224 const defaultNotepad = data.find(function (notepad) { 1225 return notepad.markAsDefault === true; 1226 }); 1227 1228 const savedBaskets = { 1229 "publicNotepadList":publicNotepads, 1230 "privateNotepadList":privateNotepads 1231 } 1232 if (defaultNotepad) { 1233 savedBaskets.notepadIdMarkedAsDefault = String(defaultNotepad.cartId); 1234 } 1235 return savedBaskets; 1236 }, function () { 1237 if (!convertToLegacyStrutsResult){ 1238 return []; 1239 } 1240 return { 1241 "publicNotepadList":[], 1242 "privateNotepadList":[], 1243 //marker so callers can distinguish a failed load from an empty-but-successful result 1244 "loadFailed":true 1245 } 1246 }); 1247 } 1248};
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.