1var APP = APP || {}; 2// Utilities 3APP.utils = (function () { 4 "use strict"; 5 6 let popoverEventListenerAdded = false; 7 8 return { 9 10 checkUsername: function (msisdn, type) { 11 switch (type) { 12 case 'tel': 13 return this.checkNorwegianMsisdn(msisdn) 14 case 'email': 15 return this.checkEmail(msisdn) 16 } 17 }, 18 19 checkNorwegianMsisdn: function (msisdn) { 20 return msisdn.replace(/^[^49]|[^0-9]/g, ''); 21 }, 22 23 checkEmail: function (email) { 24 return email.replace(/[^a-zA-Z0-9@._+-]/g, ''); 25 }, 26 27 validateUsername: function (msisdn, type) { 28 switch (type) { 29 case 'tel': 30 return this.validateNorwegianMsisdn(msisdn) 31 case 'email': 32 return this.validateEmail(msisdn) 33 } 34 }, 35 36 37 validateNorwegianMsisdn: function (msisdn) { 38 msisdn = this.stripSpaces(msisdn); 39 40 var regex = new RegExp('^([4]|[9])[0-9]{7}$'); 41 return regex.test(msisdn); 42 }, 43 44 validateEmail: function (email) { 45 var that = this, 46 validate = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 47 48 return validate.test(that.stripSpaces(email)); 49 }, 50 51 stripSpaces: function (str) { 52 if (str && str.length > 2 && str.indexOf(' ') !== -1) { 53 str = str.split(' ').join(''); 54 } 55 return str; 56 }, 57 pushGoogle: function (eventCategory, eventAction, eventLabel, nonInteraction) { 58 nonInteraction = nonInteraction || false; 59 if (typeof gtag !== 'undefined' && eventCategory && eventAction && eventLabel) { 60 gtag('event', eventAction, { 61 'event_category' : eventCategory, 62 'event_label' : eventLabel, 63 'non_interaction': nonInteraction 64 }); 65 } 66 }, 67 pushGoogleSelectItem: function () { 68 if (!this.canSubmitEcommerceEvent()) { 69 return; 70 } 71 72 gtag("event", "select_item", { 73 items: [this.getEcommerceItem()], 74 }); 75 }, 76 pushGoogleViewItem: function () { 77 if (!this.canSubmitEcommerceEvent()) { 78 return; 79 } 80 81 gtag("event", "view_item", { 82 currency: window.currency, 83 value: parseInt(APP.ecommerce.price), 84 items: [this.getEcommerceItem()], 85 }); 86 }, 87 pushGoogleViewItemListOnConsentEvent: function (items) { 88 window.addEventListener('consent-updated', () => { 89 if (typeof gtag === 'undefined') { 90 return; 91 } 92 93 gtag('event', 'view_item_list', { 94 "currency": window.currency, 95 "items": items 96 }); 97 }, { once: true }); 98 }, 99 pushGoogleBeginCheckout: function () { 100 if (!this.canSubmitEcommerceEvent()) { 101 return; 102 } 103 104 gtag("event", "begin_checkout", { 105 currency: window.currency, 106 value: parseInt(APP.ecommerce.price), 107 items: [this.getEcommerceItem()], 108 }); 109 }, 110 pushGooglePurchase: function () { 111 if (!this.canSubmitEcommerceEvent()) { 112 return; 113 } 114 115 gtag("event", "purchase", { 116 transaction_id: APP.transaction.token_id, 117 value: APP.transaction.total_price, 118 currency: window.currency, 119 items: [this.getEcommerceItem()], 120 }); 121 122 if (typeof fbq !== "undefined") { 123 fbq("track", "Purchase", { currency: window.currency, value: 1 }); 124 } 125 }, 126 canSubmitEcommerceEvent: function () { 127 return typeof gtag !== "undefined" && typeof APP.ecommerce !== "undefined"; 128 }, 129 getEcommerceItem: function () { 130 return { 131 item_id: APP.ecommerce.item_id, 132 item_name: APP.ecommerce.item_name, 133 item_brand: APP.ecommerce.item_brand, 134 item_category: APP.ecommerce.item_category, 135 item_variant: APP.ecommerce.item_variant, 136 index: APP.ecommerce.index, 137 item_list_name: APP.ecommerce.item_list_name, 138 quantity: APP.ecommerce.quantity, 139 price: APP.ecommerce.price, 140 }; 141 }, 142 ajaxCall: function (prop, callback, failCallback) { 143 if (typeof prop === 'object') { 144 145 if (APP.core.getLanguageUri() !== "") { 146 prop.url = APP.core.getLanguageUri() + prop.url; 147 } 148 149 $.ajax(prop).done(function(resp) { 150 151 if (callback !== undefined) { 152 if (resp !== undefined) { 153 callback(resp); 154 } else { 155 callback(); 156 } 157 } else { 158 return resp; 159 } 160 161 }).fail(function(resp) { 162 if (failCallback !== undefined) { 163 failCallback(resp); 164 } 165 }); 166 } else { 167 APP.utils.ajaxLogger({ 168 type: 'ajax', 169 message: '[ERROR] ajaxCall: prop is not an object, type: ' + typeof(prop) 170 }); 171 } 172 }, 173 174 175 /** LOG MESSAGE TO SERVER. 176 * 177 * @param data [object] 178 * data.type [optional] logfile, default: ajax. 179 * data.message [required] message to be logged. Example: "[WARN] var g undefined, should be one of bla bla." 180 */ 181 ajaxLogger: function (data) { 182 183 if (!data.type) { 184 data.type = 'ajax'; 185 } 186 187 if (data.message) { 188 189 $.ajax({ 190 type: 'POST', 191 url: '/ajax/log', 192 data: { 193 type: data.type, 194 message: data.message 195 }, 196 dataType: 'json' 197 }).done(function() { 198 199 }); 200 } 201 }, 202 203 204 redirect: function (uri) { 205 if (uri === undefined) { 206 return; 207 } 208 209 if (uri === location.pathname) { 210 window.location = uri; 211 return; 212 } 213 214 let langPrefix = APP.core.getLanguageUri(); 215 let destination = uri; 216 217 if (langPrefix === '/en' && uri.indexOf('/en/') < 0) { 218 destination = (uri === '/') ? langPrefix : langPrefix + uri; 219 } 220 221 window.location = destination; 222 }, 223 224 225 showLoading: function (obj) { 226 if (!$.trim($(obj).find('.loading').html()).length) { 227 $(obj).find('.loading').removeClass('hidden').append('<span class="loading-spinner"></span> ' + $t('Vennligst vent')); 228 } else { 229 $(obj).find('.loading').removeClass('hidden').slideDown(100); 230 } 231 }, 232 233 234 // bootstrap 4 loading spinner 235 loadingButton: function(button, disabled) { 236 if (disabled !== undefined) { 237 $(button).prop({disabled: disabled}); 238 } 239 240 if ($(button).find('.spinner-border').length > 0) { 241 $(button).find('.spinner-border').remove(); 242 243 return; 244 } 245 246 $(button).prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="false"></span> '); 247 }, 248 249 250 getCookie: function (check_name) { 251 // first we'll split this cookie up into name/value pairs 252 // note: document.cookie only returns name=value, not the other components 253 var a_all_cookies = document.cookie.split( ';' ), 254 a_temp_cookie = '', 255 cookie_name = '', 256 cookie_value = '', 257 b_cookie_found = false;
258 259 for (var i = 0; i < a_all_cookies.length; i++ ) { 260 // now we'll split apart each name=value pair 261 a_temp_cookie = a_all_cookies[i].split( '=' ); 262 263 // and trim left/right whitespace while we're at it 264 cookie_name = a_temp_cookie[0].replace(/^\s+|\s+$/g, ''); 265 266 if ( cookie_name === check_name ) { 267 b_cookie_found = true; 268 if ( a_temp_cookie.length > 1 ) { 269 cookie_value = unescape( a_temp_cookie[1].replace(/^\s+|\s+$/g, '') ); 270 } 271 // note that in cases where cookie is initialized but no value, null is returned 272 return cookie_value; 273 } 274 a_temp_cookie = null; 275 cookie_name = ''; 276 } 277 if (!b_cookie_found) { 278 return null; 279 } 280 }, 281 282 283 setCookie: function (cookie) { 284 285 var now = new Date(), 286 expires; 287 288 now.setTime( now.getTime() ); 289 290 if (cookie.name !== undefined) { 291 292 if (cookie.expires) { 293 expires = cookie.expires * 1000 * 60 * 60 * 24; // Days 294 } else { 295 expires = 3600000; // Default 1 hour 296 } 297 298 expires = new Date(now.getTime() + expires); 299 300 document.cookie = 301 cookie.name + '=' + cookie.value + 302 '; expires=' + expires.toUTCString() + 303 '; path=/' + 304 '; domain=.' + this.getBaseDomain(window.location.hostname); 305 306 } 307 }, 308 309 getBaseDomain: function(hostname) { 310 const parts = hostname.split('.'); 311 const length = parts.length; 312 313 if (length > 2) { 314 return parts.slice(-2).join('.'); 315 } 316 317 return hostname; 318 }, 319 320 validateForm: function (obj, validateObj) { 321 322 for (var key in obj) { 323 if (obj.hasOwnProperty(key)) { 324 // Fields without validation functions, validate length only. 325 if ($.inArray(key, validateObj.inputs) > -1) { 326 if ($(obj[key]).val().trim().length > 0) { 327 $(obj[key]).closest('.form-group').removeClass('has-error'); 328 } else { 329 $(obj[key]).closest('.form-group').addClass('has-error'); 330 } 331 } 332 333 if ($.inArray(key, validateObj.textarea) > -1) { 334 if ($(obj[key]).val().trim().length < 10) { 335 $(obj[key]).closest('.form-group').addClass('has-error'); 336 } else { 337 $(obj[key]).closest('.form-group').removeClass('has-error'); 338 } 339 } 340 341 if ($.inArray(key, validateObj.zipcode) > -1) { 342 343 if ($(obj[key]).val().trim().length > 3 && $(obj[key]).val().length < 5) { 344 $(obj[key]).closest('.form-group').removeClass('has-error'); 345 } else { 346 $(obj[key]).closest('.form-group').addClass('has-error'); 347 } 348 } 349 350 // If first option is selected, show error. 351 if ($.inArray(key, validateObj.selects) > -1) { 352 353 if ($(obj[key]).prop('selectedIndex') < 1) { 354 $(obj[key]).closest('.form-group').addClass('has-error'); 355 } else { 356 $(obj[key]).closest('.form-group').removeClass('has-error'); 357 } 358 } 359 360 // Match two fields 361 if ($.inArray(key, validateObj.match) > -1) { 362 363 if ($(obj[key][0]).val() !== $(obj[key][1]).val()) { 364 $(obj[key][1]).closest('.form-group').addClass('has-error'); 365 } else { 366 $(obj[key][1]).closest('.form-group').removeClass('has-error'); 367 } 368 } 369 370 // Fields with email validation 371 if ($.inArray(key, validateObj.validateEmail) > -1) { 372 373 if (!this.validateEmail($(obj[key]).val())) { 374 $(obj[key]).closest('.form-group').addClass('has-error'); 375 } else { 376 $(obj[key]).closest('.form-group').removeClass('has-error'); 377 } 378 } 379 380 // Fields with msisdn validation 381 if ($.inArray(key, validateObj.validateNorwegianMsisdn) > -1) { 382 383 if (!this.validateNorwegianMsisdn($(obj[key]).val())) { 384 $(obj[key]).closest('.form-group').addClass('has-error'); 385 } else { 386 $(obj[key]).closest('.form-group').removeClass('has-error'); 387 } 388 } 389 } 390 } 391 }, 392 393 394 395 /** CLOCK COUNTDOWN FOR TOKENS 396 * 397 * When time is up we trigger a event called '/clock_stop'. Listen on that if you want to do something fancy. 398 */ 399 startClock: function (timestamp) { 400 if (!timestamp) { 401 return; 402 } 403 404 var self = this; 405 var timer; 406 407 window.start = parseFloat(timestamp); 408 409 if (!isNaN(window.start) && typeof window.start === "number") { 410 window.start = window.start - 1; 411 if (window.start >= 0) { 412 timer = setTimeout(function () { 413 self.updateClock() 414 }, 1000); 415 } else { 416 clearTimeout(timer); 417 $(document).trigger('/clock_stop'); 418 } 419 } else { 420 window.start = 0; 421 } 422 }, 423 424 updateClock: function () { 425 this.startClock(window.start); 426 }, 427 428 debounce: function(func, wait, immediate) { 429 var timeout; 430 return function() { 431 var context = this, args = arguments; 432 var later = function() { 433 timeout = null;
434 if (!immediate) func.apply(context, args); 435 }; 436 var callNow = immediate && !timeout; 437 clearTimeout(timeout); 438 timeout = setTimeout(later, wait); 439 if (callNow) func.apply(context, args); 440 }; 441 }, 442 443 initVimeoPlayerEmbed: function ($playButton, options, callback) { 444 if (typeof Vimeo !== 'object') { 445 return; 446 } 447 448 var elementId = $playButton.data('player-id'); 449 var $playerContainer = $('#' + elementId); 450 451 if ($playerContainer.hasClass('vimeo-loading') || $playerContainer.hasClass('vimeo-loaded')) { 452 return; 453 } 454 455 $playerContainer.addClass('video-loading'); 456 $playerContainer.parent().find('.video-error').remove(); 457 458 var player = new Vimeo.Player(elementId, { 459 url: $playButton.attr('href'), 460 width: 640, 461 autoplay: true 462 }); 463 464 player.ready().catch(function(resp) { 465 APP.utils.ajaxLogger({ 466 type: 'error', 467 message: '[ERROR] vimeo ('+window.location.href+'): ' + resp.message, 468 }) 469 470 $playerContainer.parent().append('<div class="video-error slide-in">' + $t('En feil oppsto under avspilling, prøv igjen.') + '</div>'); 471 $playButton.removeClass('d-none'); 472 $playerContainer.removeClass('video-loading'); 473 474 // Cleanup the player and remove from dom 475 player.destroy(); 476 }) 477 478 // some mobile browsers won't allow autoplay without muting the video 479 if (APP.core.isMobile()) { 480 player.setMuted(true); 481 } 482 483 484 if ($playerContainer.data('seekable') === false) { 485 var watched15Sec = false; 486 var watched5Sec = false; 487 var timeWatched = 0; 488 489 player.on("timeupdate", function(data) { 490 if (data.seconds - 1 < timeWatched && data.seconds > timeWatched) { 491 timeWatched = data.seconds; 492 } 493 494 if (data.seconds <= timeWatched && options.triggerCallbackNearEnd) { 495 if (!watched15Sec && (data.duration - timeWatched) < 15) { 496 watched15Sec = true; 497 if (callback && typeof callback === "function") { 498 callback(elementId); 499 } 500 } 501 502 if (!watched5Sec && (data.duration - timeWatched) < 5) { 503 watched15Sec = true; 504 if (callback && typeof callback === "function") { 505 callback(elementId); 506 } 507 } 508 } 509 }); 510 511 player.on("seeked", function(data) { 512 if (timeWatched < data.seconds) { 513 player.setCurrentTime(timeWatched); 514 } 515 }); 516 } 517 518 player.on('play', function() { 519 if (!$playerContainer.data('play')) { 520 $playerContainer.data('play', true); 521 522 if (options && options.origin) { 523 APP.utils.pushGoogle('user-action', options.origin, 'play-video'); 524 } 525 526 if (options && options.playCallback && typeof options.playCallback === "function") { 527 options.playCallback($playerContainer); 528 } 529 } 530 }); 531 532 player.on('ended', function () { 533 if (!$playerContainer.data('ended') && options.origin) { 534 $playerContainer.data('ended', true); 535 APP.utils.pushGoogle('user-action', options.origin, 'finished-video'); 536 } 537 538 if (callback && typeof callback === "function") { 539 callback(elementId); 540 } 541 }) 542 543 player.on('loaded', function() { 544 $playButton.addClass('d-none'); 545 $playerContainer.removeClass('video-loading').addClass('video-loaded'); 546 }); 547 548 return player; 549 }, 550 551 /** 552 * Using new vimeo player sdk 553 * @param elementId 554 * @param origin 555 * @param callback 556 * @param options 557 */ 558 initVimeoPlayerEvents: function (elementId, origin, callback, options) { 559 if (typeof Vimeo !== 'object') { 560 return; 561 } 562 563 var $playerIframe = $('#' + elementId); 564 565 var player = new Vimeo.Player(elementId); 566 player.ready().then(function() { 567 568 if ($playerIframe.data('seekable') === false) { 569 var isWatched15Sec = false;
570 var isWatched5Sec = false; 571 var timeWatched = 0; 572 573 player.on("timeupdate", function(data) { 574 if (data.seconds - 1 < timeWatched && data.seconds > timeWatched) { 575 timeWatched = data.seconds; 576 } 577 578 if (data.seconds <= timeWatched && options.triggerCallbackNearEnd) { 579 if (!isWatched15Sec && (data.duration - timeWatched) < 15) { 580 isWatched15Sec = true; 581 if (callback && typeof callback === "function") { 582 callback(elementId); 583 } 584 } 585 586 if (!isWatched5Sec && (data.duration - timeWatched) < 5) { 587 isWatched5Sec = true; 588 if (callback && typeof callback === "function") { 589 callback(elementId); 590 } 591 } 592 } 593 }); 594 595 player.on("seeked", function(data) { 596 if (timeWatched < data.seconds) { 597 player.setCurrentTime(timeWatched); 598 } 599 }); 600 } 601 602 player.on('play', function() { 603 if (!$playerIframe.data('play')) { 604 $playerIframe.data('play', true) 605 APP.utils.pushGoogle('user-action', origin, 'play-video'); 606 } 607 }); 608 609 player.on('ended', function () { 610 if (!$playerIframe.data('ended')) { 611 $playerIframe.data('ended', true) 612 APP.utils.pushGoogle('user-action', origin, 'finished-video'); 613 } 614 615 if (callback && typeof callback === "function") { 616 callback(elementId); 617 } 618 }) 619 }); 620 }, 621 622 siteNotification: function (obj) { 623 624 var self = this; 625 if (obj.message) { 626 627 APP.modal.show({ 628 modalHeader: { 629 content: obj.title || '', 630 classes: [obj.modalHeaderClasses] 631 }, 632 modalBody: { 633 content: obj.message 634 }, 635 modalFooter: { 636 content: '<a href="#" class="btn ' + (obj.modalFooterClass ? obj.modalFooterClass : 'btn-default') + '" data-bs-dismiss="modal">' + $t('Lukk') + '</a>' 637 } 638 }); 639 640 if (obj.redirect !== undefined) { 641 $('.modal').on('hidden.bs.modal', function() { 642 self.redirect(obj.redirect); 643 }); 644 } 645 } 646 }, 647 648 isElementInViewport: function (el, offset) { 649 if (!offset) { 650 offset = 0; 651 } 652 var rect = el.getBoundingClientRect(); 653 var windowHeight = (window.innerHeight || document.documentElement.clientHeight); 654 655 return ((rect.top + offset) <= windowHeight) && (((rect.top + offset) + rect.height) >= 0); 656 }, 657 658 createIntersectionObserver: function(elem, callback, options) { 659 if (!elem) { 660 return; 661 } 662 663 664 var observer = new IntersectionObserver(callback, options || {}); 665 // observer.USE_MUTATION_OBSERVER = false; // don't check for intersections when DOM changes 666 observer.observe(elem); 667 668 return observer 669 }, 670 671 copyToClipboard: function(element, value) { 672 var $temp = $("<input>"); 673 $("body").append($temp); 674 $temp.val(value).select(); 675 document.execCommand("copy"); 676 $temp.remove(); 677 $(element).find('svg').addClass('single-pulse').delay(300).queue(function(){ 678 $(this).removeClass("single-pulse").dequeue(); 679 }); 680 }, 681 682 waitForImagesToLoad: async function (element) { 683 if (!element) { 684 return Promise.resolve(); 685 } 686 const elementPosition = element.getBoundingClientRect()?.top; 687 const images = document.querySelectorAll('img'); 688 689 const promises = Array.from(images).map(img => { 690 if (img.complete || img.getBoundingClientRect()?.top > elementPosition) { 691 return Promise.resolve(); 692 } 693 img.loading = 'eager'; 694 695 return new Promise(resolve => img.addEventListener('load', resolve, { once: true })); 696 }); 697 698 return Promise.all(promises); 699 }, 700 701 702 scrollToElement: function (selector){ 703 const element = document.querySelector(selector); 704 if(!element){ 705 return; 706 } 707 708 window.scrollTo({ 709 top: element.getBoundingClientRect().top + window.scrollY - 25, 710 behavior: 'smooth' 711 }) 712 }, 713 714 switchTheme: function (element) { 715 const activeTheme = document.documentElement.getAttribute('data-bs-theme'); 716 const currentTheme = activeTheme === 'light' ? 'dark' : 'light'; 717 718 document.documentElement.setAttribute('data-bs-theme', currentTheme) 719 720 this.setCookie({ 721 name: 'theme', 722 value: currentTheme, 723 expires: 30 724 }); 725 }, 726 727 initTooltips: function() { 728 const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); 729 if (tooltipTriggerList && tooltipTriggerList.length) { 730 tooltipTriggerList.forEach(function(tooltipTriggerEl) { 731 new bootstrap.Tooltip(tooltipTriggerEl) 732 }); 733 } 734 }, 735 736 initPopovers: function (selector, options) { 737 const popoverTriggerList = document.querySelectorAll(selector); 738 if (popoverTriggerList && popoverTriggerList.length) { 739 740 popoverTriggerList.forEach(function(popoverTriggerEl) { 741 const popoverTarget = popoverTriggerEl.getAttribute('data-bs-target'); 742 const popoverTargetEl = document.querySelector(popoverTarget) 743 744 let popover = bootstrap.Popover.getInstance(popoverTriggerEl) 745 746 if (!popover) { 747 popover = new bootstrap.Popover(popoverTriggerEl, { 748 html: true, 749 sanitize: false, 750 container: '.root-container', 751 content: popoverTargetEl ? popoverTargetEl.innerHTML : '', 752 trigger: options.trigger 753 }); 754 755 if (options.trigger === 'manual') { 756 popoverTriggerEl.addEventListener('click', function(event) { 757 event.preventDefault(); 758 event.stopPropagation();
759 popover.toggle(); 760 }); 761 } 762 } 763 764 }); 765 766 if (options.trigger === 'manual' && !this.popoverEventListenerAdded) { 767 document.addEventListener('click', function(event) { 768 if (event.target.closest('.popover')) { 769 return; 770 } 771 772 document.querySelectorAll('[data-bs-toggle="popover"]').forEach(function (popover) { 773 const popoverInstance = bootstrap.Popover.getInstance(popover); 774 if (popoverInstance) { 775 popoverInstance.hide(); 776 } 777 }) 778 }); 779 this.popoverEventListenerAdded = true; 780 } 781 } 782 }, 783 784 /** 785 * Prevents rapid repeated clicks on buttons and links. 786 * First click behaves normally, subsequent clicks are blocked if they happen within the duration window. 787 */ 788 preventRapidClicks: function (el, duration = 2000) { 789 let clicked = false; 790 el.addEventListener('click', function (event) { 791 if (!clicked) { 792 clicked = true; 793 setTimeout(function() { 794 clicked = false; 795 }, duration); 796 } else { 797 event.stopImmediatePropagation(); 798 event.preventDefault(); 799 } 800 }, true); 801 }, 802 803 bindRapidClickPreventionToElements: function(selectors, duration = 2000) { 804 const links = document.querySelectorAll(selectors); 805 links.forEach(function (link) { 806 APP.utils.preventRapidClicks(link, duration) 807 }); 808 } 809 }; 810 811})(); 812 813var __local = __local || {}; 814__local = (function () { 815 "use strict"; 816 817 return { 818 getUser:function(){ 819 var __user; 820 try{ 821 if(store && store.enabled ) { 822 __user = store.get('user'); 823 if(!__user){ 824 __user = store.set('user', { history: [], jsError: [], userAgent: navigator.userAgent }); 825 }else{ 826 if(!__user.history){ 827 __user.history = []; 828 } 829 if(!__user.jsError){ 830 __user.jsError = []; 831 } 832 __user = store.set('user', { history: __user.history, jsError: __user.jsError, userAgent: navigator.userAgent }); 833 } 834 } 835 }catch(err){/*Ignore error*/} 836 return __user; 837 }, 838 pushHistory:function(action){ 839 try{ 840 var __user = this.getUser(); 841 if(__user && __user.history){ 842 if(__user.history.length >= 100){ 843 __user.history.pop(); 844 } 845 __user.history.unshift(action); 846 store.set('user', { history: __user.history, jsError: __user.jsError, userAgent: navigator.userAgent }); 847 } 848 }catch(err){/*Ignore error*/} 849 }, 850 resetStore:function(){ 851 try{ 852 var __user = this.getUser(); 853 if(__user && __user.history){ 854 store.set('user', { history: [], jsError: [], userAgent: navigator.userAgent }); 855 } 856 }catch(err){/*Ignore error*/} 857 }, 858 pushError:function(error){ 859 try{ 860 var __user = this.getUser(); 861 if(__user && __user.jsError){ 862 if(__user.jsError.length >= 10){ 863 __user.jsError.pop(); 864 } 865 var payload = {error: error, url: window.location.href, timestamp: Date('now')}; 866 __user.jsError.unshift(payload); 867 store.set('user', { history: __user.history, jsError: __user.jsError, userAgent: navigator.userAgent }); 868 } 869 }catch(err){/*Ignore error*/} 870 } 871 } 872})(); 873 874 875// Polyfills 876if ( !Object.prototype.hasOwnProperty ) { 877 Object.prototype.hasOwnProperty = function(prop) { 878 var proto = this.__proto__ || this.constructor.prototype; 879 return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]); 880 }; 881} 882 883 884// handler for client side browser errors 885window.onerror = function (msg, file, line, col, err) { 886 "use strict"; 887 var dontLog = [ 888 'Error loading script', 889 'Script error.', 890 'Object required', 891 'postMessage', // cause: plusone apis.google.com 892 'getElementById(a).src', // cause: plusone apis.google.com
893 'NPObject', // cause: symantec norton 894 '__gCrWeb', // cause: google chrome for iOS 895 'checkSessionState', // cause: plusone apis.google.com 896 'Kan ikke hente egenskapen src', // cause: plusone apis.google.com 897 'DealPly' 898 ]; 899 900 var log = true; 901 var message = msg; 902 903 if (msg) { 904 for (var i = 0; i < dontLog.length; i++) { 905 if (msg.indexOf(dontLog[i]) > -1) { 906 log = false; 907 break; 908 } 909 } 910 } 911 912 if (log && (!file || file.startsWith(window.location.protocol + '//' + window.location.hostname))) { 913 if (onerror.num++ < onerror.max) { 914 var errorObj = { 915 "message": msg, 916 "url": document.location.href, 917 "handled_by": "window.onerror", 918 "file": file, 919 "line": line + (col ? ":" + col : "") 920 }; 921 922 if(__local){ 923 __local.pushError(errorObj); 924 } 925 926 message += "\n" + JSON.stringify(errorObj, null, 4); 927 928 if (err !== undefined) { 929 message += "\nstack: " + err.stack; 930 } 931 932 $.ajax({ 933 type: 'POST', 934 url: '/ajax/log', 935 data: {"type": "client", "message": message} 936 }); 937 938 //return true; 939 } 940 } 941}; 942onerror.max = 5; 943onerror.num = 0;
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.