1<?xml version="1.0" encoding="UTF-8"?> 2<!DOCTYPE html> 3<html xmlns="http://www.w3.org/1999/xhtml"><head id="j_idt5"> 4 <link rel="SHORTCUT ICON" href="https://f.nordiskemedier.dk/favicon/metal.ico" /> 5 <link rel="ICON" href="https://f.nordiskemedier.dk/favicon/metal.ico" type="image/x-icon" /> 6 <title>Välj prenumeration</title> 7 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> 8 <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> 9 <meta id="viewport" name="viewport" content="width=device-width, initial-scale=1" /> 10
10<script> 11 //<![CDATA[ 12 (function () { 13 var width = window.innerWidth || 14 document.documentElement.clientWidth || 15 document.body.clientWidth; 16 if (width >= 768) { 17 document.getElementById("viewport").setAttribute("content", "width=1344"); 18 } else { 19 document.getElementById("viewport").setAttribute("content", "width=device-width, initial-scale=1"); 20 } 21 })(); 22 //]]> 23 </script>
23 24 <meta http-equiv="content-script-type" content="text/javascript" /> 25 <meta http-equiv="cache-control" content="no-cache" /> 26 <meta http-equiv="expires" content="0" /> 27 <meta http-equiv="pragma" content="no-cache" /> 28
28<script src="/js/vars.js?t=1790858078229" type="text/javascript"></script>
28 29
29<script src="/js/access.js?t=1790858078229" type="text/javascript"></script>
29 30
30<script> 31 window.startupTimestamp = "1790858078229"; 32 window.contextPath = ""; 33 window.domain = "https://www.metal-supply.se"; 34 window.fileDomain = "https://f.nordiskemedier.dk"; 35 window.logoName = "10"; 36 window.hostId = 10; 37 window.visitor = {}; 38 window.cvrRestApiAddress = "https://cvr-api.nordiskemedier.dk/resources"; 39 window.metaObject = {"industryName":"","authorInitials":"","pageType":"","contentType":""}; 40 window.testMode = false; 41 42 window.googleAnalytics = false; 43 window.pendingScriptExecutions = []; 44 window.enableCookieBot = new function () {}; 45 46 window.trackerEnabled = true; // TODO: replace with JsVarsServlet after merge 47 </script>
47 48 49
49<script type="text/javascript" async="true"> 50 //<![CDATA[ 51 (function() { 52 var host = window.location.hostname; 53 var element = document.createElement('script'); 54 var firstScript = document.getElementsByTagName('script')[0]; 55 var url = 'https://cmp.inmobi.com' 56 .concat('/choice/', 'h8eTMmqFZF-_K', '/', host, '/choice.js?tag_version=V3'); 57 var uspTries = 0; 58 var uspTriesLimit = 3; 59 element.async = true; 60 element.type = 'text/javascript'; 61 element.src = url; 62 63 // Not default InMobi code - make sure there is a script present 64 // before inserting anything before it. 65 if (firstScript) { 66 firstScript.parentNode.insertBefore(element, firstScript); 67 } 68 69 function makeStub() { 70 var TCF_LOCATOR_NAME = '__tcfapiLocator'; 71 var queue = []; 72 var win = window; 73 var cmpFrame; 74 75 function addFrame() { 76 var doc = win.document; 77 var otherCMP = !!(win.frames[TCF_LOCATOR_NAME]); 78 79 if (!otherCMP) { 80 if (doc.body) { 81 var iframe = doc.createElement('iframe'); 82 83 iframe.style.cssText = 'display:none'; 84 iframe.name = TCF_LOCATOR_NAME; 85 doc.body.appendChild(iframe); 86 } else { 87 setTimeout(addFrame, 5); 88 } 89 } 90 return !otherCMP; 91 } 92 93 function tcfAPIHandler() { 94 var gdprApplies; 95 var args = arguments; 96 97 if (!args.length) { 98 return queue; 99 } else if (args[0] === 'setGdprApplies') { 100 if ( 101 args.length > 3 && 102 args[2] === 2 && 103 typeof args[3] === 'boolean' 104 ) { 105 gdprApplies = args[3]; 106 if (typeof args[2] === 'function') { 107 args[2]('set', true); 108 } 109 } 110 } else if (args[0] === 'ping') { 111 var retr = { 112 gdprApplies: gdprApplies, 113 cmpLoaded: false, 114 cmpStatus: 'stub' 115 }; 116 117 if (typeof args[2] === 'function') { 118 args[2](retr); 119 } 120 } else { 121 if(args[0] === 'init' && typeof args[3] === 'object') { 122 args[3] = Object.assign(args[3], { tag_version: 'V3' }); 123 } 124 queue.push(args); 125 } 126 } 127 128 function postMessageEventHandler(event) { 129 var msgIsString = typeof event.data === 'string'; 130 var json = {}; 131 132 try { 133 if (msgIsString) { 134 json = JSON.parse(event.data); 135 } else { 136 json = event.data; 137 } 138 } catch (ignore) {} 139 140 var payload = json.__tcfapiCall; 141 142 if (payload) { 143 window.__tcfapi( 144 payload.command, 145 payload.version, 146 function(retValue, success) { 147 var returnMsg = { 148 __tcfapiReturn: { 149 returnValue: retValue, 150 success: success, 151 callId: payload.callId 152 } 153 }; 154 if (msgIsString) { 155 returnMsg = JSON.stringify(returnMsg); 156 } 157 if (event && event.source && event.source.postMessage) { 158 event.source.postMessage(returnMsg, '*'); 159 } 160 }, 161 payload.parameter 162 ); 163 } 164 } 165 166 while (win) { 167 try { 168 if (win.frames[TCF_LOCATOR_NAME]) { 169 cmpFrame = win; 170 break; 171 } 172 } catch (ignore) {} 173 174 if (win === window.top) { 175 break; 176 } 177 win = win.parent; 178 } 179 if (!cmpFrame) { 180 addFrame(); 181 win.__tcfapi = tcfAPIHandler; 182 win.addEventListener('message', postMessageEventHandler, false); 183 } 184 }; 185 186 makeStub(); 187 188 function makeGppStub() { 189 const CMP_ID = 10; 190 const SUPPORTED_APIS = [ 191 '2:tcfeuv2', 192 '6:uspv1', 193 '7:usnatv1', 194 '8:usca', 195 '9:usvav1', 196 '10:uscov1', 197 '11:usutv1', 198 '12:usctv1' 199 ]; 200 201 window.__gpp_addFrame = function (n) { 202 if (!window.frames[n]) { 203 if (document.body) { 204 var i = document.createElement("iframe"); 205 i.style.cssText = "display:none"; 206 i.name = n; 207 document.body.appendChild(i); 208 } else { 209 window.setTimeout(window.__gpp_addFrame, 10, n); 210 } 211 } 212 }; 213 window.__gpp_stub = function () { 214 var b = arguments; 215 __gpp.queue = __gpp.queue || []; 216 __gpp.events = __gpp.events || []; 217 218 if (!b.length || (b.length == 1 && b[0] == "queue")) { 219 return __gpp.queue; 220 } 221 222 if (b.length == 1 && b[0] == "events") { 223 return __gpp.events; 224 } 225 226 var cmd = b[0]; 227 var clb = b.length > 1 ? b[1] : null; 228 var par = b.length > 2 ? b[2] : null; 229 if (cmd === "ping") { 230 clb( 231 { 232 gppVersion: "1.1", // must be âVersion.Subversionâ, current: â1.1â 233 cmpStatus: "stub", // possible values: stub, loading, loaded, error 234 cmpDisplayStatus: "hidden", // possible values: hidden, visible, disabled 235 signalStatus: "not ready", // possible values: not ready, ready 236 supportedAPIs: SUPPORTED_APIS, // list of supported APIs 237 cmpId: CMP_ID, // IAB assigned CMP ID, may be 0 during stub/loading 238 sectionList: [], 239 applicableSections: [-1], 240 gppString: "", 241 parsedSections: {}, 242 }, 243 true 244 ); 245 } else if (cmd === "addEventListener") { 246 if (!("lastId" in __gpp)) { 247 __gpp.lastId = 0; 248 } 249 __gpp.lastId++; 250 var lnr = __gpp.lastId; 251 __gpp.events.push({ 252 id: lnr, 253 callback: clb, 254 parameter: par, 255 }); 256 clb( 257 { 258 eventName: "listenerRegistered", 259 listenerId: lnr, // Registered ID of the listener 260 data: true, // positive signal 261 pingData: { 262 gppVersion: "1.1", // must be âVersion.Subversionâ, current: â1.1â 263 cmpStatus: "stub", // possible values: stub, loading, loaded, error 264 cmpDisplayStatus: "hidden", // possible values: hidden, visible, disabled 265 signalStatus: "not ready", // possible values: not ready, ready 266 supportedAPIs: SUPPORTED_APIS, // list of supported APIs 267 cmpId: CMP_ID, // list of supported APIs 268 sectionList: [], 269 applicableSections: [-1], 270 gppString: "", 271 parsedSections: {}, 272 }, 273 }, 274 true 275 ); 276 } else if (cmd === "removeEventListener") { 277 var success = false;
278 for (var i = 0; i < __gpp.events.length; i++) { 279 if (__gpp.events[i].id == par) { 280 __gpp.events.splice(i, 1); 281 success = true; 282 break; 283 } 284 } 285 clb( 286 { 287 eventName: "listenerRemoved", 288 listenerId: par, // Registered ID of the listener 289 data: success, // status info 290 pingData: { 291 gppVersion: "1.1", // must be âVersion.Subversionâ, current: â1.1â 292 cmpStatus: "stub", // possible values: stub, loading, loaded, error 293 cmpDisplayStatus: "hidden", // possible values: hidden, visible, disabled 294 signalStatus: "not ready", // possible values: not ready, ready 295 supportedAPIs: SUPPORTED_APIS, // list of supported APIs 296 cmpId: CMP_ID, // CMP ID 297 sectionList: [], 298 applicableSections: [-1], 299 gppString: "", 300 parsedSections: {}, 301 }, 302 }, 303 true 304 ); 305 } else if (cmd === "hasSection") { 306 clb(false, true); 307 } else if (cmd === "getSection" || cmd === "getField") { 308 clb(null, true); 309 } 310 //queue all other commands 311 else { 312 __gpp.queue.push([].slice.apply(b)); 313 } 314 }; 315 window.__gpp_msghandler = function (event) { 316 var msgIsString = typeof event.data === "string"; 317 try { 318 var json = msgIsString ? JSON.parse(event.data) : event.data; 319 } catch (e) { 320 var json = null; 321 } 322 if (typeof json === "object" && json !== null && "__gppCall" in json) { 323 var i = json.__gppCall; 324 window.__gpp( 325 i.command, 326 function (retValue, success) { 327 var returnMsg = { 328 __gppReturn: { 329 returnValue: retValue, 330 success: success, 331 callId: i.callId, 332 }, 333 }; 334 event.source.postMessage(msgIsString ? JSON.stringify(returnMsg) : returnMsg, "*"); 335 }, 336 "parameter" in i ? i.parameter : null, 337 "version" in i ? i.version : "1.1" 338 ); 339 } 340 }; 341 if (!("__gpp" in window) || typeof window.__gpp !== "function") { 342 window.__gpp = window.__gpp_stub; 343 window.addEventListener("message", window.__gpp_msghandler, false); 344 window.__gpp_addFrame("__gppLocator"); 345 } 346 }; 347 348 makeGppStub(); 349 350 var uspStubFunction = function() { 351 var arg = arguments; 352 if (typeof window.__uspapi !== uspStubFunction) { 353 setTimeout(function() { 354 if (typeof window.__uspapi !== 'undefined') { 355 window.__uspapi.apply(window.__uspapi, arg); 356 } 357 }, 500); 358 } 359 }; 360 361 var checkIfUspIsReady = function() { 362 uspTries++; 363 if (window.__uspapi === uspStubFunction && uspTries < uspTriesLimit) { 364 console.warn('USP is not accessible'); 365 } else { 366 clearInterval(uspInterval); 367 } 368 }; 369 370 if (typeof window.__uspapi === 'undefined') { 371 window.__uspapi = uspStubFunction; 372 var uspInterval = setInterval(checkIfUspIsReady, 6000); 373 } 374 })(); 375 //]]> 376 </script>
376 377
377<script type="application/ld+json"> 378 { 379 "@context":"http://schema.org", 380 "@type":"NewsMediaOrganization", 381 "name":"Metal Supply", 382 "logo":{ 383 "@type":"ImageObject", 384 "url":"https://f.nordiskemedier.dk/logo/portal/10.png" 385 }, 386 "url":"https://www.metal-supply.se", 387 "ethicsPolicy":"https://www.metal-supply.se/privacy", 388 "sameAs":["https://www.linkedin.com/company/metal-supply-se/"] 389 } 390 </script>
390 391
391<script src="/js/notbot.js?t=1790858078229" type="text/javascript"></script>
391 392 393 <link href="https://f.nordiskemedier.dk/font/media.css" rel="stylesheet" /> 394 <link rel="stylesheet" href="https://f.nordiskemedier.dk/font-awesome/pro/6.6.0/css/all.min.css" /> 395 <link rel="stylesheet" href="https://f.nordiskemedier.dk/bootstrap/5.3.6/css/bootstrap.min.css" /> 396 <link rel="stylesheet" href="/css/default.css?t=1790858078229" type="text/css" /> 397
397<script src="https://f.nordiskemedier.dk/jquery/jquery-3.6.0.min.js"></script>
397 398 399
399<script src="/js/jquery-ui.min.js"></script>
399<script type="text/javascript" src="/javax.faces.resource/js/jquery.cookies.2.2.0.min.js.html?ln=portal-jsf&t=17908580"></script>
399 400
400<script src="https://f.nordiskemedier.dk/bootstrap/5.3.6/js/bootstrap.bundle.min.js"></script>
400 401
401<script src="/js/default.js?t=1790858078229" type="text/javascript"></script>
vendor: 138 bytes, lines 401-403
401 402 <script src="https://www.google.com/recaptcha/api.js?render=explicit&hl=sv" type="text/javascript"></script> 403
403<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
403 404
404<script src="/dist/vue-components.umd.min.js?t=1790858078229" type="text/javascript"></script>
404 405 406
406<script src="https://f.nordiskemedier.dk/turnjs4/lib/turn.min.js" type="text/javascript"></script>
406<!--[if lt IE 9]> 407
407<script src="https://f.nordiskemedier.dk/turnjs4/lib/turn.html4.min.js" type="text/javascript"></script>
407<![endif]--><!--[if lt IE 9]> 408
408<script type="text/javascript"> 409 window.location = "/browser-support.html"; 410 </script>
410<![endif]--> 411
411<script src="https://cdn.jsdelivr.net/npm/ua-parser-js@0/dist/ua-parser.min.js"></script>
411 412
412<script type="text/javascript"> 413 window.enableGTM = function() { 414 try {
vendor: 647 bytes, lines 414-427
414 415 (function (w, d, s, l, i) { 416 w[l] = w[l] || []; 417 w[l].push({ 418 'gtm.start': 419 new Date().getTime(), event: 'gtm.js' 420 }); 421 var f = d.getElementsByTagName(s)[0], 422 j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; 423 j.async = true; 424 j.src = 425 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; 426 f.parentNode.insertBefore(j, f); 427 })(window, document, 'script', 'dataLayer', '
427GTM-PPHWGFF
vendor: 16 bytes, lines 427-428
427'); 428
428} catch (err) { 429 console.log(err); 430 } 431 }; 432 </script>
432 433 434 435 436
436<script src="/js/cookie-utilities.js"></script>
436 437
437<script type="text/javascript"> 438 var gaPageType = 'misc'; 439 var gaContentType = 'misc'; 440 var gaPageAge = null; 441 var gaPageSize = null; 442 var gaIndustryName = 'null'; 443 444 </script>
444 445
445<script type="text/javascript"> 446 window.enableBanners = function () { 447 try { 448 window['banner-consent-is-ready'] = true; 449 EventBus.broadcast("banner-consent-is-ready"); 450 } catch (err) { 451 console.log(err); 452 } 453 }; 454 </script>
454 455
455<script type="text/javascript"> 456 //<![CDATA[ 457 (function () { 458 var salespanelModuleEnabled = false; 459 var quantCastModuleEnabled = true; 460 var salespanelResolve, salespanelReject; 461 window.salespanelPromise = new Promise((resolve, reject) => { 462 // Check if Salespanel and QuantCast module is enabled for this host and check if the tag has loaded. 463 if (!salespanelModuleEnabled || !quantCastModuleEnabled) { 464 resolve(false); 465 } 466 salespanelResolve = resolve; 467 salespanelReject = reject; 468 }); 469 470 // Reject just in case QuantCast module is enabled but variable is not initialized. 471 if (typeof window.__tcfapi == "undefined") { 472 salespanelReject("QuantCast error: __tcfapi is undefined"); 473 return; 474 } 475 476 // Listen for Quantcast events for changes to Salespanel consent. 477 window.__tcfapi('addEventListener', 2, async function (tcData, success) { 478 if (!success) { 479 salespanelReject("Salespanel error: Could not create event listener."); 480 return; 481 } 482 483 // the rest of this function is called when the page is loaded OR 484 // - the user accepts / rejects / updates the consent terms 485 // - the user dismisses the consent box by clicking the 'x' 486 if (tcData.eventStatus !== 'tcloaded' && tcData.eventStatus !== 'useractioncomplete') { 487 return; 488 } 489 490 // Salespanel is managed by Google Tag Manager and is not inserted if the relevant consent is not given. 491 // If consent is given, it returns true, otherwise false after 2,5 seconds. 492 var loops = 0; 493 while(!salespanelObjectLoaded() && loops < 5) { 494 await new Promise(resolve => setTimeout(resolve, 500)); 495 loops += 1; 496 } 497 498 if (salespanelObjectLoaded()) { 499 // Set cookie. 500 allowTracking(); 501 salespanelResolve(true); 502 } else { 503 // Remove cookie. 504 rejectTracking(); 505 // Salespanel did not load in time or was rejected by consent. 506 salespanelReject("Salespanel error: Not loaded."); 507 } 508 }); 509 510 function salespanelObjectLoaded() { 511 return typeof $salespanel !== "undefined" && $salespanel.hasOwnProperty("push"); 512 } 513 514 function allowTracking() { 515 // Check if Salespanel tracking has already been allowed. 516 if ($.cookies.get("track_uid") !== null) { 517 return; 518 } 519 520 try { 521 // Activate tracking when the visitor provides explicit consent. 522 // Place `track_uid` cookie. 523 $salespanel.push(["set", "tracking:allowTracking"]); 524 } catch (e) { 525 salespanelReject("Salespanel error: Allow tracking failed."); 526 } 527 } 528 529 function rejectTracking() { 530 // Remove cookie if user removed Salespanel consent. 531 // All created Salespanel events will now be un-identified. 532 if ($.cookies.get("track_uid")) { 533 $.cookies.del("track_uid", {domain: '.metal-supply.se'}); 534 } 535 } 536 })(); 537 //]]> 538 </script>
538 539
539<script type="text/javascript"> 540 //<![CDATA[ 541 window.paywallScripts = { 542 seenPaywall: async function (type) { 543 // Wait for salespanel promise to be fulfilled (either loaded or not). 544 const salespanelLoaded = await window.salespanelPromise.catch(() => { 545 // Salespanel didn't load if promise is rejected. 546 return false; 547 }); 548 if (!salespanelLoaded) return; 549 550 // Find previous amount of seen paywalls. 551 await this.seenPaywallPromise().then(seenPaywalls => { 552 // Increment amount of seen paywall on visitor attribute. 553 this.setPaywallsAttribute(seenPaywalls); 554 555 // Create Meta/Facebook Pixel event notifying of a seen paywall. 556 if (seenPaywalls > 3) { 557 fbq('trackCustom', "Set paywall +3 gange"); 558 } 559 }).catch((message) => { 560 console.error(message); 561 }); 562 563 // Create Salespanel event notifying of a seen paywall. 564 this.sendPaywallEvent(type); 565 }, 566 seenPaywallPromise: function () { 567 return new Promise((resolve, reject) => { 568 try { 569 $salespanel.push(["get", "qualify:visitor", function (data) { 570 data.visitor_attributes.paywalls !== null ? 571 resolve(data.visitor_attributes.paywalls) : 572 resolve(0); 573 }]); 574 } catch (e) { 575 reject("Salespanel error: Get details failed."); 576 } 577 }); 578 }, 579 setPaywallsAttribute: function (seenPaywalls) { 580 try { 581 $salespanel.push(["set", "attributes:visitor", { 582 "paywalls": seenPaywalls + 1 583 }]); 584 } catch (e) { 585 console.error("Salespanel error: Couldn't set attribute."); 586 } 587 }, 588 sendPaywallEvent: function (type) { 589 try { 590 $salespanel.push(["set", 591 "activity:customActivity", 592 "website", 593 "Saw paywall", 594 type 595 ]); 596 } catch (e) { 597 console.error("Salespanel error: Couldn't send custom activity."); 598 } 599 } 600 } 601 //]]> 602 </script>
602 603
603<script type="text/javascript"> 604 (function (){ 605 var is = []; 606 var impersonating = false; 607 if (!impersonating && window.visitor.person && window.visitor.person.id) { 608 is.push({ 609 name: "person", 610 ref: window.visitor.person.id 611 }); 612 } 613 if (!impersonating && window.visitor.recipient && window.visitor.recipient.id) { 614 is.push({ 615 name: "recipient", 616 ref: window.visitor.recipient.id 617 }); 618 } 619 620 var evtr; 621 var queue = []; 622 window.queueTrackerEvent = function (a, sn, sr, tn, tr) { 623 if (evtr) { 624 evtr(a, sn, sr, tn, tr, is); 625 } else { 626 queue.push({ 627 a: a, 628 sn: sn, 629 sr: sr, 630 tn: tn, 631 tr: tr 632 }) 633 } 634 }; 635 window.addEventListener("onTrackerLoaded", function () { 636 evtr = window.createTracker("https://api.evtr.nordiskemedier.dk", "evtr"); 637 while (queue.length) { 638 var ev = queue.splice(0, 1)[0]; 639 if (ev) { 640 evtr(ev.a, ev.sn, ev.sr, ev.tn, ev.tr, is); 641 } 642 } 643 }); 644 })(); 645 </script>
645 646
646<script async="async" src="https://js.evtr.nordiskemedier.dk/_.js?t=1790858078229" type="text/javascript"></script>
646 647 648
648<script type="text/javascript"> 649 $(function() { 650 $('.item').hover( 651 function() { $(this).css('background-color', '#efefef').css('cursor', 'pointer') }, 652 function() { $(this).css('background-color', 'transparent').css('cursor', 'normal') } 653 ); 654 $(".tt").tooltip(); 655 }); 656 function getTitle() { 657 return document.title; 658 } 659 function getLocation() { 660 return window.location; 661 } 662 function getUrl() { 663 return window.location.href; 664 } 665 function confirmDelete(p) { 666 var v = "\u00C4r du s\u00E4ker p\u00E5 att du vill radera \"{0}\"?"; 667 v = v.replace("{0}", p); 668 return confirm(v); 669 } 670 function getVisitorEmail() { 671 return ""; 672 } 673 function getVisitorRememberInfo() { 674 return true; 675 } 676 677 var ids = []; 678 function messageFade() { 679 $('.fade').fadeTo('slow', 0.3); 680 } 681 682 // Remove cookies that are no longer in use 683 $(document).ready(function () { 684 $.cookies.del("c1"); 685 $.cookies.del("c2"); 686 $.cookies.del("c3"); 687 }); 688 689 window.pushGTrackScript = function (f, cont) { 690 window.pendingScriptExecutions.push({ 691 callback: function () { 692 if (window.googleAnalytics) { 693 eval(f(cont)); 694 } 695 }, 696 context: cont 697 }); 698 699 } 700 $(window).on('load', function () { 701 // if subscription is suspended change paywall message. 702 if ($('#paywall-overlay') && window.visitor?.person?.suspended) { 703 $('#paywall-overlay .paywall-package').html("\u00C5tkomsten \u00E4r avst\u00E4ngd."); 704 $('#paywall-overlay .paywall-body-wrapper').html("Av s\u00E4kerhetssk\u00E4l \u00E4r ditt konto tillf\u00E4lligt avst\u00E4ngt.".replace('{0}', "[email protected]").replace('{1}', "[email protected]")); 705 $('.paywall-content-wrapper').addClass('warning'); 706 } 707 }); 708 </script>
708 709
709<script type="text/javascript"> 710 //<![CDATA[ 711 function center(element) { 712 var e=$(element); 713 var w=e.width(); 714 var h=e.height(); 715 e.css("position","fixed").css("z-index","50000").css("top","50%").css("left","50%").css("margin-left","-"+(w/2)+"px").css("margin-top","-"+(h/2)+"px"); 716 e.draggable({ 717 handle: $(element+"_handle") 718 }); 719 } 720 //]]> 721 </script>
721 722 723 724 <link rel="alternate" type="application/rss+xml" title="News RSS" href="https://www.metal-supply.se/xml/rss2/articles" /> 725 <link rel="alternate" type="application/rss+xml" title="Advertisements RSS" href="https://www.metal-supply.se/xml/rss2/advertisements?typeId=1&typeId=2" /> 726 <link rel="alternate" type="application/rss+xml" title="Competences RSS" href="https://www.metal-supply.se/xml/rss2/competences" /> 727 <link rel="alternate" type="application/rss+xml" title="Jobs RSS" href="https://www.metal-supply.se/xml/rss2/jobs" /> 728 <link rel="alternate" type="application/rss+xml" title="Courses RSS" href="https://www.metal-supply.se/xml/rss2/courses" /> 729 730
730<script> 731 window.adTracker = { 732 slots: {}, 733 adsMeta: {}, 734 hoverState: { 735 currentSlotId: null, 736 lastChangeTs: 0 737 }, 738 clickState: { 739 lastPointerDown: null, // { slotId, ts } 740 clickHandled: false 741 } 742 }; 743 </script>
743<script async="async" src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
743<script>window.googletag = window.googletag || {cmd: []};if (!window.__gptGlobalsInit) { window.__gptGlobalsInit = true; window.slotFallbackData = new Map(); window.__inViewPct = window.__inViewPct || {}; window.__lastRefreshAt = new Map(); window.__slotCooldownMs = new Map(); function __canRefresh(slot){ const id = slot.getSlotElementId(); const last = window.__lastRefreshAt.get(id) || 0; const cd = window.__slotCooldownMs.get(id) || 30000; return Date.now() - last >= cd; } function __markRefreshed(slot){ window.__lastRefreshAt.set(slot.getSlotElementId(), Date.now()); } window.__refreshState = window.__refreshState || {}; function __getRefreshState(id){ return (window.__refreshState[id] = window.__refreshState[id] || { pending:false, nextAllowedTs:0 }); } function __tryRefresh(slot){ const id = slot.getSlotElementId(); const st = __getRefreshState(id); if (!st.pending) return; if (st.refreshScheduled) return; const now = Date.now(); const pct = (window.__inViewPct && window.__inViewPct[id]) || 0; if (now < st.nextAllowedTs){ __scheduleTryRefresh(slot, st.nextAllowedTs - now); return; } if (pct < 50){ __scheduleTryRefresh(slot, 3000); return; } if (!__canRefresh(slot)){ __scheduleTryRefresh(slot, 3000); return; } st.refreshScheduled = true; const ranInt = Math.floor(Math.random() * (2000 - 100 + 1)) + 100; setTimeout(function(){ googletag.pubads().refresh([slot]); __markRefreshed(slot); st.pending = false; st.refreshScheduled = false; }, ranInt); }function handleSlotRender(slotElementId) { var labelId = 'label-' + slotElementId; var labelEl = document.getElementById(labelId); if (!labelEl) { return; } labelEl.style.display = 'block';}function registerAdSlot(slotId) { const slotEl = document.getElementById(slotId); if (!slotEl) return; const wrapper = slotEl; wrapper.dataset.adSlotId = slotId; if (!wrapper.style.position) { wrapper.style.position = 'relative'; } window.adTracker = window.adTracker || {}; window.adTracker.slots = window.adTracker.slots || {}; window.adTracker.slotListeners = window.adTracker.slotListeners || {}; const existing = window.adTracker.slotListeners[slotId]; if (existing && existing.wrapper) { existing.wrapper.removeEventListener('mouseenter', existing.mouseenter); existing.wrapper.removeEventListener('mouseleave', existing.mouseleave); } const mouseenter = function () { window.adTracker.hoverState.currentSlotId = slotId; window.adTracker.hoverState.lastChangeTs = Date.now(); }; const mouseleave = function () { if (window.adTracker.hoverState.currentSlotId === slotId) { window.adTracker.hoverState.currentSlotId = null; window.adTracker.hoverState.lastChangeTs = Date.now(); } }; wrapper.addEventListener('mouseenter', mouseenter); wrapper.addEventListener('mouseleave', mouseleave); window.adTracker.slotListeners[slotId] = { wrapper: wrapper, mouseenter: mouseenter, mouseleave: mouseleave }; window.adTracker.slots[slotId] = wrapper;} function __scheduleTryRefresh(slot, delayMs){ const id = slot.getSlotElementId(); const st = __getRefreshState(id); if (st.tryTimer) clearTimeout(st.tryTimer); st.tryTimer = setTimeout(function(){ st.tryTimer = null; __tryRefresh(slot); }, Math.max(0, delayMs || 0)); } googletag.cmd.push(function(){ googletag.pubads().addEventListener('slotVisibilityChanged', function(e){ const slot = e.slot; const id = slot.getSlotElementId(); window.__inViewPct[id] = e.inViewPercentage || 0; __scheduleTryRefresh(slot, 250); }); googletag.pubads().addEventListener('impressionViewable', function(e){ const slot = e.slot; const id = slot.getSlotElementId(); const cd = window.__slotCooldownMs.get(id) || 30000; const st = __getRefreshState(id); st.pending = true; st.nextAllowedTs = Date.now() + cd; __scheduleTryRefresh(slot, cd); }); }); window.fireWindowResizeEvent = window.fireWindowResizeEvent || function(){ try { window.dispatchEvent(new Event('resize')); } catch(e){} };}googletag.cmd.push(function(){ window.__fallbackHandled = window.__fallbackHandled || new Set(); googletag.pubads().addEventListener('slotRenderEnded', function(event){ const adslotId = event.slot.getSlotElementId(); if (!event.isEmpty) { fireWindowResizeEvent(); handleSlotRender(adslotId); window.adTracker.adsMeta[adslotId] = { creativeId: event.creativeId, lineItemId: event.lineItemId, campaignId: event.campaignId, size: event.size, adUnitPath: event.slot.getAdUnitPath(), }; registerAdSlot(adslotId); return; } if (!window.slotFallbackData || !window.slotFallbackData.has(adslotId)) { fireWindowResizeEvent(); return; } if (window.__fallbackHandled.has(adslotId)) { fireWindowResizeEvent(); return; } window.__fallbackHandled.add(adslotId); const cfg = window.slotFallbackData.get(adslotId); try { googletag.destroySlots([event.slot]); } catch(e) {} requestAnimationFrame(function(){ const toArr = s => s.split('],').map(x=>JSON.parse(x.replace(/\s/g,'') + (x.endsWith(']')?'':']'))); const sizeArrayLarge = cfg.sizesLarge ? toArr(cfg.sizesLarge) : []; const sizeArraySmall = cfg.sizesSmall ? toArr(cfg.sizesSmall) : []; const sizeArrayMobile = cfg.sizesMobile ? toArr(cfg.sizesMobile) : []; var mapping = googletag.sizeMapping() .addSize([1588,0], sizeArrayLarge) .addSize([768,0], sizeArraySmall) .addSize([0,0], sizeArrayMobile).build(); var already = googletag.pubads().getSlots().some(s => s.getSlotElementId() === adslotId); if (!already) { var fb = googletag.defineSlot(cfg.fallbackAdUnitPath, cfg.sizes, adslotId) .defineSizeMapping(mapping) .addService(googletag.pubads()); if (cfg.refreshAdSlot === 'true') { window.__slotCooldownMs && window.__slotCooldownMs.set(adslotId, Number(cfg.refreshInterval)||30000); } else { window.__slotCooldownMs && window.__slotCooldownMs.set(adslotId, 1440000); } } setTimeout(function(){ try { googletag.display(adslotId); } catch(e) { console.warn('fallback display failed:', e); } fireWindowResizeEvent(); }, 0); }); });});googletag.cmd.push(function(){googletag.setConfig({singleRequest: true});googletag.setConfig({ collapseDiv: "ON_NO_FILL" }); googletag.enableServices();});</script>
743 744 745 746
746<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
746</head><body class="minified"> 747
747<script src="/js/cookie-consent.js"></script>
747 748 749 <div style="display:none;"> 750 <a href="/void/index.html" rel="nofollow">23948sdkhjf</a> 751 </div> 752 <i id="media-width-detection" style="display: none"> 753 754 <i class="d-md-none xs"></i> 755 <i class="d-none d-md-inline lg"></i> 756 </i>
vendor: 93 bytes, lines 756-758
756 757 758 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
758GTM-PPHWGFF
vendor: 101 bytes, lines 758-761
758" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 759 760 761
761<div class="remodal-bg"> 762 <header id="main-header" v-cloak="" class="unprintable"> 763 <nav> 764 <div> 765 <global-search-icon></global-search-icon> 766 </div> 767 <div> 768 <a href="/" class="logo"> 769 <img src="https://f.nordiskemedier.dk/logo/portal/10.png" alt="Metal Supply" /> 770 </a> 771 </div> 772 <div> 773 <button v-if="window.visitor.person" v-on:click.prevent="open(personMenu)" class="btn btn-link btn-log-in px-0" style="position: relative;"> 774 <i class="fa-light fa-user d-sm-none"></i> 775 <span class="d-none d-sm-inline">Mitt konto</span> 776 <span class="badge bg-danger rounded-pill translate-middle notification-badge notification-badge--account" v-if="unreadNotifications > 0 && 10 > unreadNotifications">{{ unreadNotifications }}</span> 777 <span class="badge bg-danger rounded-pill translate-middle notification-badge notification-badge--account" v-else-if="unreadNotifications > 0 && unreadNotifications >= 10"> 9+ </span> 778 </button> 779 <button v-else="" v-on:click.prevent="open(topMenu)" class="btn btn-link btn-log-in px-0"> 780 <i class="fa-light fa-user d-sm-none"></i> 781 <span class="d-none d-sm-inline">Logga in</span> 782 </button> 783 <button v-if="(window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient) || (window.visitor.person && window.visitor.corporateClient)" v-on:click.prevent="open(ipAccessMenu)" class="btn btn-link btn-ip-access px-0"> 784 <span class="d-flex" v-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length == 1"> 785 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.corporateClient.name, 20) }}</span> 786 </span> 787 <span class="d-flex" v-else-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length > 1"> 788 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString("Företagslösning", 12) }}</span><span class="long-name">{{ truncateString("Företagslösning", 20) }}</span> 789 </span> 790 <span class="d-flex" v-else-if="window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient"> 791 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.ipAccess.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.ipAccess.corporateClient.name, 20) }}</span> 792 </span> 793 <span class="d-flex" v-else=""> 794 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.corporateClient.name, 20) }}</span> 795 </span> 796 </button> 797 <template v-else=""><a id="j_idt106:j_idt108" href="/products/subscription.html" style="" class="btn btn-primary" target=""> 798 <span class="d-inline d-sm-none">Köp</span> 799 <span class="d-none d-sm-inline">Köp åtkomst</span></a> 800 </template> 801 <button v-on:click.prevent="open(rightMenu)" class="btn btn-link btn-bars px-0 d-inline-flex"> 802 <i class="fa-light fa-bars-staggered"></i> 803 </button> 804 </div> 805 </nav> 806 <div v-on:click.self="close" class="menu-container menu-top" v-bind:class="getStyleClass(topMenu)"> 807 <div>
808 <menu-log-in v-bind:sign-up-link="signUpLink" v-on:close="close"></menu-log-in> 809 </div> 810 </div> 811 <div v-on:click.self="close" class="menu-container menu-right" v-bind:class="getStyleClass(rightMenu)"> 812 <div> 813 <div style="align-items: center;" class="d-flex justify-content-end"> 814 <button v-if="window.visitor.person" v-on:click.prevent="open(personMenu)" class="btn btn-link btn-log-in px-0"> 815 Mitt konto 816 </button> 817 <button v-else="" v-on:click.prevent="open(topMenu)" class="btn btn-link btn-log-in px-0"> 818 <i class="fa-light fa-user d-sm-none"></i> 819 <span class="d-none d-sm-inline">Logga in</span> 820 </button> 821 <button v-if="(window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient) || (window.visitor.person && window.visitor.corporateClient)" v-on:click.prevent="open(ipAccessMenu)" class="btn btn-link btn-ip-access px-0 mx-4"> 822 <span class="d-flex" v-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length == 1"> 823 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.corporateClient.name, 20) }}</span> 824 </span> 825 <span class="d-flex" v-else-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length > 1"> 826 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString("Företagslösning", 12) }}</span><span class="long-name">{{ truncateString("Företagslösning", 20) }}</span> 827 </span> 828 <span class="d-flex" v-else-if="window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient"> 829 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.ipAccess.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.ipAccess.corporateClient.name, 20) }}</span> 830 </span> 831 <span class="d-flex" v-else=""> 832 <i class="fa-light fa-lock-open me-1"></i><span class="short-name">{{ truncateString(window.visitor.corporateClient.name, 12) }}</span><span class="long-name">{{ truncateString(window.visitor.corporateClient.name, 20) }}</span> 833 </span> 834 </button> 835 <template v-else=""><a id="j_idt110:j_idt112" href="/products/subscription.html" style="" class="btn btn-primary mx-4" target=""> 836 <span class="d-inline d-sm-none">Köp</span> 837 <span class="d-none d-sm-inline">Köp åtkomst</span></a> 838 </template> 839 <button v-on:click.prevent="close" class="btn btn-link btn-x px-0 d-inline-flex"> 840 <i class="fa-light fa-xmark-large"></i> 841 </button> 842 </div> 843 <div class="mt-5 mb-4"> 844 <global-search-input></global-search-input> 845 </div> 846 <ul class="primary-menu"> 847 <li v-for="(option, i) in mainOptions" v-bind:key="i" v-bind:class="{'open': unfolded == i}"> 848 <a v-if="option.target" v-bind:href="option.target" v-bind:target="option.external ? '_blank' : ''">{{ option.name }}</a> 849 <template v-else-if="option.children"> 850 <a href="#" v-on:click.prevent="unfolded = unfolded != i ? i : null">{{ option.name }}<i class="fa-light" v-bind:class="{'fa-chevron-down': unfolded != i, 'fa-chevron-up': unfolded == i}"></i></a> 851 <ul> 852 <li v-for="(child, j) in option.children" v-bind:key="j"> 853 <a v-bind:href="child.target" v-bind:target="child.external ? '_blank' : ''">{{ child.name }}</a> 854 </li> 855 </ul> 856 </template> 857 </li> 858 </ul> 859 <ul class="secondary-menu"> 860 <li> 861 <a href="/epages/archive.html"> 862 <i class="fa-light fa-newspaper"></i>E-tidning 863 </a> 864 </li> 865 <li> 866 <a href="/newsletter/subscribe.html"><i class="fa-light fa-envelope"></i>Registrera nyhetsbrev</a> 867 </li> 868 <li> 869 <a href="/bookmark"><i class="fa-light fa-bookmark"></i>Min läslista</a> 870 </li> 871 </ul> 872 <ul v-if="mainSecondaryOptions && mainSecondaryOptions.length" class="secondary-menu"> 873 <li v-for="(option, i) in mainSecondaryOptions" v-bind:key="i"> 874 <a v-if="option.target" v-bind:href="option.target" v-bind:target="option.external ? '_blank' : ''">{{ option.name }}</a> 875 <template v-else-if="option.children">
876 <span>{{ option.name }}</span> 877 <ul> 878 <li v-for="(child, j) in option.children" v-bind:key="j"> 879 <a v-bind:href="child.target" v-bind:target="child.external ? '_blank' : ''">{{ child.name }}</a> 880 </li> 881 </ul> 882 </template> 883 </li> 884 </ul> 885 <div class="extra"> 886 <ip-access-toggle></ip-access-toggle> 887 </div> 888 </div> 889 </div> 890 <div v-if="window.visitor.person" v-on:click.self="close" class="menu-container menu-right" v-bind:class="getStyleClass(personMenu)"> 891 <div> 892 <div class="d-flex"> 893 <div class="d-flex w-50 align-items-center"> 894 </div> 895 896 <div style="align-items: center;" class="d-flex justify-content-end w-50"> 897 <button v-on:click.prevent="close" class="btn btn-link btn-x px-0 d-inline-flex"> 898 <i class="fa-light fa-xmark-large"></i> 899 </button> 900 </div> 901 </div> 902 <ul class="primary-menu mt-4"> 903 <li v-bind:class="{'open': unfolded == -1}"> 904 <a href="#" v-on:click.prevent="unfolded = unfolded != -1 ? -1 : null" class="border-top-0"> 905 <span class="d-block"> 906 <span class="d-block">{{ window.visitor.person.name }}</span> 907 <small v-if="window.visitor.member" class="d-block text-muted"> 908 <span class="me-2">{{ window.visitor.member.name }}</span> 909 <span v-if="window.visitor.person.members && window.visitor.person.members.length > 1" class="border rounded px-1">+{{ window.visitor.person.members.length - 1 }}</span> 910 </small> 911 </span> 912 <i class="fa-light" v-bind:class="{'fa-chevron-down': unfolded != -1, 'fa-chevron-up': unfolded == -1}"></i> 913 </a> 914 <ul> 915 <li v-if="window.visitor.person.members && window.visitor.person.members.length > 1" v-for="member in window.visitor.person.members"> 916 <a href="#" v-on:click="switchMember(member.id)"> 917 {{ member.name }} 918 <small class="d-block text-muted">Företagsprofil<i v-if="window.visitor.member && member.id === window.visitor.member.id" class="text-success ms-2 fa-solid fa-check-circle"></i></small> 919 </a> 920 </li> 921 <li> 922 <a href="/user/update.html">Inställningar</a> 923 </li> 924 <li> 925 <a href="/member/log-out.html">Logga ut</a> 926 </li> 927 </ul> 928 </li> 929 <li v-for="(option, i) in personOptions" v-bind:key="i" v-bind:class="{'open': unfolded == i}"> 930 <a v-if="option.target" v-bind:href="option.target" v-bind:target="option.external ? '_blank' : ''">{{ option.name }}</a> 931 <template v-else-if="option.children"> 932 <a href="#" v-on:click.prevent="unfolded = unfolded != i ? i : null">{{ option.name }}<i class="fa-light" v-bind:class="{'fa-chevron-down': unfolded != i, 'fa-chevron-up': unfolded == i}"></i></a> 933 <ul> 934 <li v-for="(child, j) in option.children" v-bind:key="j"> 935 <a v-bind:href="child.target" v-bind:target="child.external ? '_blank' : ''">{{ child.name }}</a> 936 </li> 937 </ul> 938 </template> 939 </li> 940 </ul> 941 <ul class="secondary-menu"> 942 <li> 943 <a href="/epages/archive.html"> 944 <i class="fa-light fa-newspaper"></i>E-tidning 945 </a> 946 </li> 947 <li> 948 <a href="/newsletter/subscribe.html"><i class="fa-light fa-envelope"></i>Registrera nyhetsbrev</a> 949 </li> 950 <li> 951 <a href="/bookmark"><i class="fa-light fa-bookmark"></i>Min läslista</a> 952 </li> 953 </ul> 954 <ul v-if="mainSecondaryOptions && mainSecondaryOptions.length" class="secondary-menu"> 955 <li v-for="(option, i) in mainSecondaryOptions" v-bind:key="i"> 956 <a v-if="option.target" v-bind:href="option.target" v-bind:target="option.external ? '_blank' : ''">{{ option.name }}</a> 957 <template v-else-if="option.children">
958 <span>{{ option.name }}</span> 959 <ul> 960 <li v-for="(child, j) in option.children" v-bind:key="j"> 961 <a v-bind:href="child.target" v-bind:target="child.external ? '_blank' : ''">{{ child.name }}</a> 962 </li> 963 </ul> 964 </template> 965 </li> 966 </ul> 967 <div class="extra"> 968 <ip-access-toggle></ip-access-toggle> 969 </div> 970 </div> 971 </div> 972 <div v-if="(window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient) || (window.visitor.person && window.visitor.corporateClient)" v-on:click.self="close" class="menu-container menu-right" v-bind:class="getStyleClass(ipAccessMenu)"> 973 <div> 974 <div style="align-items: center;" class="d-flex justify-content-end"> 975 <button v-on:click.prevent="close" class="btn btn-link btn-x px-0 d-inline-flex"> 976 <i class="fa-light fa-xmark-large"></i> 977 </button> 978 </div> 979 <ul class="ip-menu"> 980 <li> 981 <h5 v-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length == 1" class="m-0">{{ window.visitor.corporateClient.name }}</h5> 982 <h5 v-else-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length > 1" class="m-0">Företagslösning</h5> 983 <h5 v-else-if="window.visitor.ipAccess && !window.visitor.ipAccess.disable && window.visitor.ipAccess.corporateClient" class="m-0">{{ window.visitor.ipAccess.corporateClient.name }}</h5> 984 <h5 v-else="" class="m-0">{{ window.visitor.corporateClient.name }}</h5> 985 </li> 986 <li v-if="window.visitor.corporateClient && window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length >= 1"><p><b>Välkommen till vår företagslösning</b></p> 987<p>Här kan du administrera användarna i din företagslösning.</p> 988 </li> 989 <li v-else-if="window.visitor.corporateClient && window.visitor.person && window.visitor.corporateClient && !window.visitor.ipAccess">Välkommen till din prenumeration för Metal Supply. 990 </li> 991 <li v-else=""><p><b>Welcome to our Corporate client solution</b></p> 992<p>Your IP is registered with the company mentioned above and provides access to Metal Supply.</p> 993 </li> 994 <template v-if="window.visitor.person && window.visitor.person.companiesWhereAdmin && window.visitor.person.companiesWhereAdmin.length >= 1"> 995 <li v-for="company in window.visitor.person.companiesWhereAdmin" class="subscription-menu-company-link"> 996 <a v-bind:href="company.link"> 997 <span class="d-block"> 998 <span class="d-block"> 999 Hantera företagsprenumeration 1000 </span> 1001 <small v-if="window.visitor.person.companiesWhereAdmin.length >= 1" class="d-block text-muted">{{company.name}}</small> 1002 </span> 1003 </a> 1004 </li> 1005 </template> 1006 <li v-if="window.visitor.ipAccess && window.visitor.ipAccess.corporateClient && window.visitor.ipAccess.corporateClient.type != 3 && window.visitor.ipAccess.corporateClient.type != 2 && !window.visitor.person"> 1007 <a v-bind:href="'/corporate-client/index.html?token='+window.visitor.ipAccess.corporateClient.token" class="btn btn-link p-0"> 1008 Click here to go to sign-up<i class="fa-light fa-arrow-right ms-2"></i> 1009 </a> 1010 </li> 1011 </ul> 1012 <div class="extra"> 1013 <ip-access-toggle></ip-access-toggle> 1014 </div> 1015 </div> 1016 </div> 1017 </header> 1018
1018<script type="application/javascript"> 1019 //<![CDATA[ 1020 window.mainMenu = new Vue({ 1021 el: "#main-header", 1022 data: { 1023 topMenu: { 1024 transitioning: false, 1025 open: false 1026 }, 1027 rightMenu: { 1028 transitioning: false, 1029 open: false 1030 }, 1031 personMenu: { 1032 transitioning: false, 1033 open: false 1034 }, 1035 ipAccessMenu: { 1036 transitioning: false, 1037 open: false 1038 }, 1039 notificationsMenu: { 1040 transitioning: false, 1041 open: false 1042 }, 1043 personNotifications: [], 1044 unreadNotifications: 0, 1045 hasMore: true, 1046 stack: [], 1047 unfolded: null, 1048 mainOptions: [{"children":[{"external":false,"name":"Senaste nytt","target":"/article/latest.html"}],"name":"Nyheter"},{"children":[{"external":false,"name":"Medlemsnyheter","target":"/announcement"},{"external":false,"name":"Företag","target":"/company"}],"name":"Branschinsikt"},{"children":[{"external":false,"name":"Maskiner","target":"/machine"},{"external":false,"name":"Produkter","target":"/product"},{"external":false,"name":"Tjänster","target":"/competence"},{"external":false,"name":"Kurser","target":"/course"}],"name":"Köp & Sälj"},{"external":false,"name":"Jobb","target":"/job"},{"external":false,"name":"Kalendarium","target":"/event/search"}], 1049 mainSecondaryOptions: [{"children":[{"external":false,"name":"Bli prenumerant","target":"/products/subscription.html"}
1049,{"external":true,"name":"Bli medlem","target":"https://nordiskemediehus.dk/se/produkt/medlemskab/"},{"external":true,"name":"Annonsera","target":"https://nordiskemediehus.dk/se/se-metal-supply/"},{"external":true,"name":"Se alla produkter","target":"https://nordiskemediehus.dk/se/produkter/"}],"name":"Produkter"}], 1050 createSubscriptionLink: {"external":false,"name":"Bli prenumerant","target":"/products/subscription.html"}, 1051 createMembershipLink: {"external":true,"name":"Bli medlem","target":"https://nordiskemediehus.dk/se/produkt/medlemskab/"}, 1052 }, 1053 computed: { 1054 personOptions: function () { 1055 const options = []; 1056 1057 const add = (list, name, target) => { 1058 const option = { 1059 name 1060 }; 1061 if (target) { 1062 option.target = target; 1063 } else { 1064 option.children = []; 1065 } 1066 list.push(option); 1067 return option.children; 1068 } 1069 1070 if (!window.visitor.member) { 1071 add(options, "Market overview", "/watch"); 1072 } else { 1073 add(options, "Aktiviteter", "/member"); 1074 1075 if (window.visitor.member.state.type.announcements || window.visitor.member.state.type.posts) { 1076 const createItems = add(options, "Skapa ett inlägg"); 1077 if (window.visitor.member.state.type.announcements) { 1078 add(createItems, "Nyhet", "/post/announcement"); 1079 } 1080 if (window.visitor.member.state.type.posts) { 1081 if (true) { 1082 add(createItems, "Maskin eller produkt", "/post/machine-or-product"); 1083 } 1084 if (true) { 1085 add(createItems, "Tj\u00E4nst", "/post/competence"); 1086 } 1087 if (true) { 1088 add(createItems, "Jobb", "/post/job"); 1089 } 1090 if (true) { 1091 add(createItems, "Kurs", "/post/course"); 1092 } 1093 } 1094 1095 if (window.visitor.member.state.type.posts && true && (true)) { 1096 const crawlingItems = add(options, "Importlagring"); 1097 add(crawlingItems, "Import overview", "/post/overview?imported=true"); 1098 add(crawlingItems, "Crawling status", "/import/status.html"); 1099 } 1100 1101 add(options, "Sammanst\u00E4llning", "/post/overview"); 1102 } 1103 // if (window.visitor.member.misc.banners || window.visitor.member.misc.newsletterBanners) { 1104 // const bannerItems = add(options, "Annonsering"); 1105 // if (window.visitor.member.misc.banners) { 1106 // add(bannerItems, "\u00D6versikt", "/banner/index.html"); 1107 // } 1108 // if (window.visitor.member.misc.newsletterBanners) { 1109 // add(bannerItems, "Annonsering i nyhetsbrevet", "/newsletter/banner/index.html"); 1110 // } 1111 // // Member-specifik kode... virker ikke med cache 1112 // // const saleEmail = "N\/A"; 1113 // // if (saleEmail.indexOf("@") != -1) { 1114 // // add(bannerItems, "Kontakta oss f\u00F6r ett kostnadsf\u00F6rslag", "mailto:" + saleEmail + "?subject=N\u00E4r det g\u00E4ller annonsering"); 1115 // // } 1116 // } 1117 1118 if (false) { 1119 add(options, "Market overview", "/watch"); 1120 } 1121 1122 const profileItems = add(options, "F\u00F6retagsprofil"); 1123 add(profileItems, "Se profilen", "/company/view/" + window.visitor.member.id + "/" + window.visitor.member.slug); 1124 if (window.visitor.member.state.type.profile) { 1125 add(profileItems, "Redigera profil", "/company/profile.html"); 1126 } 1127 add(profileItems, "Kontakter", "/member-contact/overview"); 1128 if (window.visitor.member.state.type.supplements) { 1129 add(profileItems, "F\u00F6retagsportf\u00F6lj", "/supplement/management"); 1130 } 1131 add(profileItems, "Inst\u00E4llningar f\u00F6r administrat\u00F6rer", "/company/administrators.html"); 1132 1133 add(options, "Hj\u00E4lp", "/member/help.html"); 1134 } 1135 1136 return options; 1137 // var addContacts = function (items, text, contacts) { 1138 // if (contacts) { 1139 // var contactItems = []; 1140 // for (var i = 0; i < contacts.length; i++) { 1141 // var contact = contacts[i]; 1142 // contactItems.push({ 1143 // text: contact.name, 1144 // portrait: contact.portrait, 1145 // phonePrefix: "Tel: ", 1146 // phone: contact.phone, 1147 // email: contact.email 1148 // }); 1149 // } 1150 // items.push({ 1151 // text: text, 1152 // items: contactItems, 1153 // }); 1154 // } 1155 // }; 1156 // 1157 // var contactItems = []; 1158 // for (var contact in this.visitor.contacts) { 1159 // addContacts(contactItems, this.visitor.contacts[contact].title, this.visitor.contacts[contact].contacts); 1160 // } 1161 // 1162 // items.push({ 1163 // icon: "fa-light fa-phone-alt", 1164 // text: "Kontakt", 1165 // href: "#", 1166 // items: contactItems, 1167 // }); 1168 // 1169 // return { 1170 // items: items 1171 // }; 1172 // } 1173 }, 1174 signUpLink: function () { 1175 if (window.access.module.subscriptions && this.createSubscriptionLink) { 1176 this.createSubscriptionLink.name = "Prenumerera"; 1177 return this.createSubscriptionLink; 1178 } else if (this.createMembershipLink) { 1179 this.createMembershipLink.name = "Bli medlem"; 1180 return this.createMembershipLink; 1181 } else { 1182 return null; 1183 } 1184 } 1185 }, 1186 watch: { 1187 stack: function (newValue) { 1188 if (newValue && newValue.length > 0) { 1189 document.body.classList.add("menu-open"); 1190 } else { 1191 document.body.classList.remove("menu-open"); 1192 } 1193 } 1194 }, 1195 mounted: function () { 1196 document.addEventListener("keyup", this.keyupListener); 1197 }, 1198 unmounted: function () { 1199 window.removeEventListener("keyup", this.keyupListener); 1200 }, 1201 created: function () { 1202 this.loadUnreadNotificationCount(); 1203 }, 1204 updated: function () { 1205 if(this.notificationsMenu.open) { 1206 this.$nextTick(() => { 1207 this.markNotificationsSeen(); 1208 }); 1209 } 1210 }, 1211 methods: { 1212 truncateString(str, maxLength) { 1213 if (!str || typeof str !== 'string') return ''; 1214 const ellipsis = 'â¦'; 1215 1216 if (str.length > maxLength) { 1217 let trimmed = str.slice(0, maxLength); 1218 trimmed = trimmed.replace(/\s+$/, ''); 1219 if ((trimmed + ellipsis).length < str.length) { 1220 return trimmed + ellipsis; 1221 } 1222 } 1223 1224 return str; 1225 }, 1226 keyupListener: function (event) { 1227 if (event.keyCode == 27) { 1228 this.close(); 1229 } 1230 }, 1231 open: function (menu) { 1232 this.stack.push(menu); 1233 menu.transitioning = true; 1234 this.$nextTick(() => { 1235 menu.open = true; 1236 }) 1237 }, 1238 openNotificationsMenu: function () { 1239 if(false) { 1240 this.open(this.notificationsMenu); 1241 this.loadNotifications(); 1242 } 1243 }, 1244 close: function () { 1245 const menu = this.stack.length ? this.stack.splice(this.stack.length - 1, 1)[0] : null; 1246 if (menu) { 1247 menu.open = false;
1248 setTimeout(() => { 1249 menu.transitioning = false; 1250 }, 250) 1251 } 1252 }, 1253 closeNotificationsMenu: function () { 1254 this.$nextTick(() => { 1255 this.loadUnreadNotificationCount(); 1256 }); 1257 this.close(this.notificationsMenu); 1258 }, 1259 getStyleClass: function (menu) { 1260 return { 1261 "transitioning": menu.transitioning || menu.open, 1262 "open": menu.open 1263 }; 1264 }, 1265 switchMember: function (memberId) { 1266 this.$services.personSecureService.switchMember(memberId) 1267 .then(() => window.location.reload()) 1268 .catch(err => console.error(err)); 1269 }, 1270 openLogIn: function () { 1271 Vue.$hashUtil.remove("paywall"); 1272 this.open(this.topMenu); 1273 }, 1274 loadNotifications: function () { 1275 if (window.visitor.person && false) { 1276 this.$services.notificationSecureService.loadByPerson() 1277 .then(data => { 1278 this.personNotifications = data; 1279 }).catch(err => { 1280 console.error(err); 1281 }); 1282 } 1283 }, 1284 loadUnreadNotificationCount: function () { 1285 if (window.visitor.person && false) { 1286 this.$services.notificationSecureService.loadUnreadCount() 1287 .then(data => { 1288 this.unreadNotifications = data; 1289 }).catch(err => { 1290 console.error(err); 1291 }); 1292 } 1293 }, 1294 async markNotificationsSeen() { 1295 if (this.personNotifications.length > 0) { 1296 const personNotificationIds = this.personNotifications 1297 .filter(n => !n.seen) 1298 .map(n => n.id); 1299 if (personNotificationIds.length > 0) { 1300 this.$services.notificationSecureService.seen(personNotificationIds); 1301 } 1302 } 1303 }, 1304 async handleClick(personNotification) { 1305 this.$services.notificationSecureService.clicked(personNotification.id); 1306 window.location.href = personNotification.notification.link; 1307 }, 1308 async loadMore() { 1309 min = Math.max(this.personNotifications.length, 0); 1310 this.loadNotifications(min, 15); 1311 }, 1312 timeAgo(dateString) { 1313 const created = new Date(dateString); 1314 const now = new Date(); 1315 1316 const seconds = Math.floor((now - created) / 1000); 1317 1318 if (seconds < 60) return seconds + ' sekunder sedan'; 1319 1320 const minutes = Math.floor(seconds / 60); 1321 if (minutes < 60) return minutes + ' minuter sedan'; 1322 1323 const hours = Math.floor(minutes / 60); 1324 if (hours < 60) return hours + ' timmar sedan'; 1325 1326 const days = Math.floor(hours / 24); 1327 if (days < 7) return days + ' dagar sedan'; 1328 1329 const dd = ("0" + created.getDate()).slice(0-2); 1330 const mm = ("0" + (created.getMonth() + 1)).slice(0-2); 1331 const yyyy = created.getFullYear(); 1332 return dd + "-" + mm + "-" + yyyy; 1333 } 1334 } 1335 }); 1336 //]]> 1337 </script>
1337 1338 <header id="extra-header" class="unprintable"> 1339 <nav></nav> 1340 <nav> 1341 <featured-links v-bind:value="featuredOptions"></featured-links> 1342 </nav> 1343 </header> 1344
1344<script type="application/javascript"> 1345 //<![CDATA[ 1346 new Vue({ 1347 el: "#extra-header", 1348 data: { 1349 featuredOptions: [{"external":false,"name":"Nyheter","target":"/"},{"external":false,"name":"Medlemsnyheter","target":"/announcement"},{"external":false,"name":"Köp & Sälj","target":"/buy-sell"},{"external":false,"name":"Jobb","target":"/job"},{"external":false,"name":"Företag","target":"/company"}], 1350 articles: [] 1351 } 1352 }); 1353 //]]> 1354 </script>
1354 1355 <hash-modal id="paywall-modal" v-cloak="" v-on:opened="opened" hash="paywall" class="paywall paywall-modal "> 1356 <div class="paywall-message">Logga in eller skapa en prenumeration för att spara artiklar 1357 </div> 1358 <div class="paywall-body-wrapper"> 1359 <div class="paywall-gain"> 1360 FÃ¥ tillgÃ¥ng till allt innehÃ¥ll pÃ¥ Metal Supply 1361 </div> 1362 <div class="paywall-binding"> 1363 Ingen bindningstid eller kortinformation krävs 1364 </div> 1365 <div class="paywall-action"><a id="j_idt148:j_idt150:j_idt161:j_idt165" href="/products/subscription.html?product=1079&campaign_code=paywall" style="" class="btn btn-primary" target=""> 1366 Prova Metal Supply nu 1367 </a> 1368 </div> 1369 <div class="paywall-log-in"> 1370 Redan prenumerant? <a href="#" v-on:click.prevent="window.mainMenu.openLogIn()">Logga in</a> 1371 </div> 1372 <div class="paywall-conditions"> 1373 <div> 1374 Gäller endast personlig prenumeration. 1375 </div> 1376 <div>Kontakta oss för en <a id="j_idt148:j_idt150:j_idt169:0:j_idt171" href="/products/subscription.html?product=1103&campaign_code=paywall">företagslösning</a>. 1377 </div> 1378 </div> 1379 </div> 1380 </hash-modal> 1381
1381<script type="text/javascript"> 1382 //<![CDATA[ 1383 new Vue({ 1384 el: "#paywall-modal", 1385 methods: { 1386 opened: function () { 1387 window.paywallScripts.seenPaywall("unknown"); 1388 } 1389 } 1390 }); 1391 //]]> 1392 </script>
1392 1393 1394 <div class="super-container"> 1395 <div class="base-container"> 1396 1397 1398 <main> 1399
1399<script src="/js/vue-components.js"></script>
1399 1400 1401 <div id="supscriptions-app" v-cloak=""> 1402 <div v-if="loading" class="d-flex justify-content-center" style="margin-top: 10px;"> 1403 <div class="loading-spinner-subscription"></div> 1404 </div> 1405 <div v-else-if="!selectedProduct && hideProducts === false"> 1406 <div class="d-flex justify-content-center subscription-description-single"> 1407 Välj din åtkomst till Metal Supply 1408 </div> 1409 <div style="position: relative;"> 1410 <div class="row g-0" v-on:touchstart="onTouchStart" v-on:touchmove="onTouchMove" v-on:touchend="onTouchEnd"> 1411 <div class="col d-none d-md-flex align-items-center justify-content-center"> 1412 <div v-if="productIndex" v-on:click="prevProduct()" class="subscription-scroll-button left"> 1413 <i class="fas fa-chevron-circle-left"></i> 1414 </div> 1415 </div> 1416 <div class="col-12 col-md-11"> 1417 <transition-group id="products" tag="div" v-bind:style="touchQueue.length ? 'overflow-x: hidden' : ''" class="row flex-md-row flex-md-nowrap justify-content-center subscription-product-list subscription-zero-highlight-box-container" v-bind:class="{'justify-content-md-start': products.length > 3}" mode="out-in" v-bind:name="slideDirection"> 1418 <div v-for="(product, index) in visibleProducts" v-bind:key="'product' + product.id" class="col-12 col-md-4"> 1419 <div class="subscription-product align-self-start d-flex flex-column justify-content-between" v-bind:id="'product-'+product.id"> 1420 <div> 1421 <div v-if="product.free && product.price != null && !campaignProductId" class="subscription-zero-highlight-box"> 1422 Prova 1 månad för 0 kr. 1423 </div> 1424 <div class="product-name-price-combined"> 1425 <div class="product-name"> 1426 {{ formattedProductName(product.name) }} 1427 </div> 1428 <div v-if="product.price != null" class="product-price-text d-flex justify-content-center" v-html="formattedPrice(product.price)"> 1429 </div> 1430 <div v-else="" class="product-price-text d-flex justify-content-center">Kontakta oss</div> 1431 1432 <div v-if="product.subtitle" class="form-price-subtitle d-flex justify-content-center"> 1433 {{ product.subtitle }} 1434 </div> 1435 </div> 1436 <ul class="product-features"> 1437 <li v-for="(feature, index) in computedFeatures(product.id)" v-if="product.showFeatures || index == 0 || index == 1 || index == 2" v-bind:class="index == 2 && !product.showFeatures ? 'transparent-feature' : ''"> 1438 <span v-bind:id="'feature-name-' + index" v-bind:class="'subscription-feature-name'" v-html="formatFeatureTitle(feature.title)"> 1439 </span> 1440 <div v-if="containsDoubleBrackets(feature.title)" class="subscription-feature-hover"> 1441 {{ feature.text }} 1442 </div> 1443 </li> 1444 </ul> 1445 </div> 1446 <div> 1447 <div v-if="product.showFeatures"> 1448 <button type="button" class="btn btn-success btn-rounded w-100" v-on:click="selectProduct(product)"> 1449 <template v-if="product != null && !product.price"> 1450 Kontakta oss för erbjudanden 1451 </template> 1452 <template v-else-if="product != null && product.price && !product.free"> 1453 Köp en prenumeration 1454 </template> 1455 <template v-else-if="product != null && product.price && product.free"> 1456 Prova på 1457 </template> 1458 </button> 1459 <div class="product-description d-flex justify-content-center"> 1460 {{ product.priceDetail }} 1461 </div> 1462 </div>
1463 <div class="product-show-more" v-on:click="showFeatures(product, !product.showFeatures)" v-show="!product.showFeatures"> 1464 <div class="d-flex justify-content-center"> 1465 <i v-bind:class="['fa-light', product.showFeatures ? '' : 'fa-chevron-circle-down']"></i> 1466 </div> 1467 </div> 1468 <div class="d-flex justify-content-center" v-if="product.showFeatures & window.width < 768" v-on:click="showFeatures(product, false)"> 1469 <i class="fas fa-chevron-circle-up" style="font-size: 40px;"></i> 1470 </div> 1471 </div> 1472 </div> 1473 </div> 1474 </transition-group> 1475 </div> 1476 <div class="col d-none d-md-flex align-items-center justify-content-center"> 1477 <div v-if="productIndex + productCols < products.length" v-on:click="nextProduct()" class="subscription-scroll-button right"> 1478 <i class="fas fa-chevron-circle-right"></i> 1479 </div> 1480 </div> 1481 </div> 1482 </div> 1483 </div> 1484 1485 1486 1487 <div v-else-if="selectedProduct || hideProducts === true" id="contact-form" style="display: block;" v-bind:style="['display: block']"> 1488 <div class="d-flex justify-content-center subscription-description-single"> 1489 <ul id="progress-bar" class="subscription-progressbar"> 1490 <li class="active">Information</li> 1491 <li v-if="selectedProduct != null && selectedProduct.price != null || renewCard === true">Betalning</li> 1492 <li>Kvitto</li> 1493 </ul> 1494 </div> 1495 <div v-if="productPage"> 1496 1497 <div class="row"> 1498 <div class="col-12 col-md-8 order-2 order-md-1"> 1499 <form id="supscriptionsFormApp" class="subscription-form" v-on:submit.prevent="handleSubmit"> 1500 <div> 1501 <h3>Beställning av: {{ formattedProductName(selectedProduct.name) }}</h3> 1502 1503 <div role="tabpanel" class="tab-pane fade in active show" id="profile"> 1504 1505 <div id="contactType"> 1506 <div class="form-group form-check" style="text-align: start; width: 100%;"> 1507 <input id="company" class="form-check-input" type="checkbox" v-model="pendingOrder.company" v-bind:disabled="companyDisabled" /> 1508 <label for="company">Ãr du ett företag?</label> 1509 </div> 1510 </div> 1511 1512 <div id="infoContainer"> 1513 1514 <div class="row"> 1515 1516 <div class="col-12 col-sm-6"> 1517 <input-text-floating id="firstName" label="Förnamn *" v-bind:errors="formErrors.firstName" v-model="pendingOrder.firstName" required="true" maxlength="50" placeholder=""></input-text-floating> 1518 </div> 1519 1520 <div class="col-12 col-sm-6"> 1521 <input-text-floating id="lastName" label="Efternamn *" v-bind:errors="formErrors.lastName" v-model="pendingOrder.lastName" required="true" maxlength="50" placeholder=""></input-text-floating> 1522 </div> 1523 </div> 1524 1525 <input-text-floating id="email" label="E-post *" v-bind:errors="formErrors.email" v-model="pendingOrder.email" required="true" maxlength="255" placeholder="" input-type="email"></input-text-floating> 1526 1527 1528 <input-text-floating v-show="pendingOrder.company" id="companyName" v-bind:label="'F\u00F6retag' + (pendingOrder.company ? ' *' : '')" v-bind:errors="formErrors.companyName" v-model="pendingOrder.companyName" v-bind:required="pendingOrder.company ? true : false" maxlength="50" placeholder=""></input-text-floating> 1529 1530 <input-text-floating v-show="pendingOrder.company" id="cvr" v-bind:label="'VAT nummer' + (pendingOrder.company ? ' *' : '')" v-bind:errors="formErrors.cvr" v-model="pendingOrder.cvr" v-bind:required="pendingOrder.company ? true : false " maxlength="255" placeholder=""></input-text-floating> 1531 1532 <div class="autocomplete-container"> 1533 <div id="addressInput-container" class="form-floating"> 1534 <input-text-floating id="addressInput" label="Adress *" v-bind:errors="formErrors.addressInput" v-model="pendingOrder.address" maxlength="1000" required="true" placeholder="" onkeyup="makeAPICall()"></input-text-floating> 1535 1536 <div id="autocompleteDropdown" class="autocomplete-suggestions" style="display: none;"></div> 1537 </div> 1538 </div> 1539 <div class="checkbox form-group"> 1540 <div id="detailedAddressGroup"> 1541 <input id="showDetailedAddress" type="checkbox" v-model="showDetailedAddress" class="checkbox" /> 1542 <label for="showDetailedAddress">Visa detaljerad adressinmatning</label> 1543 </div> 1544 </div> 1545 <div v-show="showDetailedAddress" class="detailed-address form-group"> 1546 <div class="row"> 1547 1548 1549 <div v-if="bordas" class="col-12 col-sm-6"> 1550 <input-text-floating id="streetName" label="Gata/väg" v-bind:errors="formErrors.streetName" v-model="pendingOrder.streetName" maxlength="255" placeholder=""></input-text-floating> 1551 </div> 1552 1553 <div v-if="bordas" class="col-4 col-sm-2"> 1554 <input-text-floating id="streetNumber" label="Nr." v-bind:errors="formErrors.streetNumber" v-model="pendingOrder.streetNumber" placeholder=""></input-text-floating> 1555 </div> 1556 1557 <div v-if="bordas" class="col-4 col-sm-2"> 1558 <input-text-floating id="floor" label="VÃ¥ning" v-bind:errors="formErrors.floor" v-model="pendingOrder.floor" maxlength="2" placeholder=""></input-text-floating> 1559 </div> 1560 1561 <div v-if="bordas" class="col-4 col-sm-2"> 1562 <input-text-floating id="door" label="Dörr" v-bind:errors="formErrors.door" v-model="pendingOrder.door" maxlength="4" placeholder=""></input-text-floating> 1563 </div> 1564 1565 1566 1567 <div v-if="!bordas" class="col-12"> 1568 <input-text-floating id="addressFull" label="Adress" v-bind:errors="formErrors.addressFull" v-model="pendingOrder.addressFull" maxlength="1000" placeholder=""></input-text-floating> 1569 </div> 1570 1571 <div class="col-12 col-sm-4"> 1572 <input-text-floating id="postalCode" label="Postnummer" v-bind:errors="formErrors.postalCode" v-model="pendingOrder.postalCode" maxlength="10" placeholder=""></input-text-floating> 1573 </div> 1574 1575 <div class="col-12 col-sm-8"> 1576 <input-text-floating id="addressCity" label="Stad" v-bind:errors="formErrors.addressCity" v-model="pendingOrder.addressCity" maxlength="50" placeholder=""></input-text-floating> 1577 </div> 1578 </div> 1579 </div> 1580 1581 1582 <div class="row"> 1583 1584 <div class="col-12 col-md-6"> 1585 <div id="countryId-container" class="form-group"> 1586 <div class="form-floating"> 1587 <select id="countryId" class="form-select" v-model="pendingOrder.country" required="true"> 1588 <option value="" selected="" disabled="">Land</option> 1589 <option v-for="countryOption in countryOptions" v-bind:value="countryOption.id">{{ countryOption.name }} 1590 </option> 1591 </select> 1592 <label for="countryId">Land *</label> 1593 </div> 1594 </div> 1595 </div> 1596 1597 <div class="col-12 col-md-6"> 1598 <div id="phone-container"> 1599 <div class="row g-0"> 1600 <div class="col-auto"> 1601 <input-text-floating id="callingCode" label="Kod *" v-bind:errors="formErrors.callingCode" v-model="pendingOrder.callingCode" required="true" maxlength="5" placeholder="" style="max-width: 5rem;" input-type="number" input-class="border-end-0 rounded-0 rounded-start"></input-text-floating> 1602 </div> 1603 <div class="col"> 1604 <input-text-floating id="phoneNumber" label="Telefon *" v-bind:errors="formErrors.phoneNumber" v-model="pendingOrder.phoneNumber" required="true" maxlength="25" placeholder="" input-type="tel" input-class="rounded-0 rounded-end"></input-text-floating> 1605 </div> 1606 </div> 1607 </div> 1608 </div> 1609 </div> 1610 </div> 1611 1612 <div class="form-floating"> 1613 <textarea id="remarks" v-model="pendingOrder.remarks" v-bind:errors="formErrors.remarks" placeholder="" maxlength="5000" class="form-control custom-textarea"></textarea> 1614 <label for="remarks" class="input-label">Notering</label> 1615 </div> 1616 </div> 1617 </div> 1618 <div> 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 <div class="form-group checkbox"> 1631 <input id="marketing" type="checkbox" v-model="pendingOrder.marketing" /> 1632 <label for="marketing">Ja tack, jag vill gärna fÃ¥ marknadsföring, erbjudanden, undersökningar o.s.v. frÃ¥n Metal Supply. <a href="/privacy">Läs cookie- och datapolicy.</a></label> 1633 </div> 1634 1635 1636 <div v-show="selectedProduct.price" id="acceptTerms-container" class="form-group checkbox"> 1637 <input id="termsAccepted" type="checkbox" v-model="termsAccepted" required="true" /> 1638 <label for="termsAccepted">Jag har läst och godkänner prenumerationsavtalet. <a href="/page/v
1638iew.html?id=18342">Se villkoren</a></label> 1639 </div> 1640 </div> 1641 1642 <div class="payment-buttons justify-content-center d-flex"> 1643 1644 <template v-if="!selectedProduct.price"> 1645 <button type="submit" id="company-button" class="btn btn-success btn-rounded d-flex justify-content-center" value="company"> 1646 Skicka 1647 </button> 1648 </template> 1649 1650 <template v-else-if="selectedProduct.price && selectedProduct.free"> 1651 <button type="submit" id="trail-button" class="btn btn-success btn-rounded d-flex justify-content-center" value="trail"> 1652 Prova nu 1653 </button> 1654 </template> 1655 1656 <template v-else-if="selectedProduct.price && !selectedProduct.free"> 1657 <div class="row justify-content-center"> 1658 <div id="invoiceButton" class="form-group clearfix" style="margin-top: 10px; width: 280px"> 1659 <div class="d-sm-flex justify-content-center"> 1660 <div class="d-flex flex-column justify-content-center mx-sm-1"> 1661 <button type="submit" id="invoice-contact-button" class="btn btn-success btn-rounded d-flex justify-content-center" value="invoice"> 1662 Faktura 1663 </button> 1664 </div> 1665 </div> 1666 </div> 1667 <div v-if="onlinePaymentModule && bordasModule" id="cardButton" class="form-group clearfix" style="margin-top: 10px; width: 280px"> 1668 <div class="d-sm-flex justify-content-center"> 1669 <div class="d-flex flex-column justify-content-center mx-sm-1"> 1670 <button type="submit" id="card-button" class="btn btn-success btn-rounded d-flex justify-content-center" value="card"> 1671 GÃ¥ till kortbetalning 1672 </button> 1673 </div> 1674 </div> 1675 </div> 1676 </div> 1677 </template> 1678 </div> 1679 </form> 1680 </div> 1681 <div class="col-12 col-md-4 order-1 order-md-2"> 1682 <div class="d-flex flex-column subscription-product-list"> 1683 1684 <div class="subscription-product"> 1685 <div> 1686 <div class="product-name-price-combined"> 1687 <div class="product-choice-text"> 1688 Du har valt: 1689 </div> 1690 1691 <div class="product-name"> 1692 {{ formattedProductName(selectedProduct.name) }} 1693 </div> 1694 1695 <div v-if="selectedProduct.price" class="product-price-text d-flex justify-content-center" v-html="formattedPrice(selectedProduct.price)"> 1696 </div> 1697 1698 <div v-if="selectedProduct.subtitle" class="form-price-subtitle d-flex justify-content-center"> 1699 {{ selectedProduct.subtitle }} 1700 </div> 1701 </div> 1702 1703 <div v-if="selectedProduct.price" class="total-price">
1704 <span>Till betalning nu:</span> 1705 <span class="price-now"> {{ selectedProduct.simplePrice }} </span> 1706 </div> 1707 <div v-if="selectedProduct.priceDetail" class="form-price-detail d-flex justify-content-center"> 1708 {{ selectedProduct.priceDetail }} 1709 </div> 1710 1711 <ul class="product-features d-none d-md-block"> 1712 <li v-for="(feature, index) in computedFeatures(selectedProduct.id)" v-if="selectedProduct.showFeatures"> 1713 <span v-bind:id="'feature-name-' + index" v-bind:class="'subscription-feature-name'" v-html="formatFeatureTitle(feature.title)"> 1714 </span> 1715 <div v-if="containsDoubleBrackets(feature.title)" class="subscription-feature-hover"> 1716 {{ feature.text }} 1717 </div> 1718 </li> 1719 </ul> 1720 </div> 1721 <div> 1722 <span class="change-package" v-on:click="deselectProduct(selectedProduct)">Byt paket</span> 1723 </div> 1724 </div> 1725 </div> 1726 </div> 1727 </div> 1728 </div> 1729 <div v-else-if="paymentPage" id="paymentPage"> 1730 <div id="checkout-container-div"> 1731 1732 </div> 1733 </div> 1734 <div v-else-if="receiptPage" id="receiptPage" class="d-flex flex-column justify-content-center"> 1735 <div class="row"> 1736 <div class="col-12 receipt"> 1737 <div v-if="selectedProduct != null && !selectedProduct.price"> 1738 <div class="header"> 1739 <b>Tack för din information</b> 1740 </div> 1741 <div class="subtext mt-5 mt-md-4 mb-5"> 1742 <div class="subtext subheader"> 1743 Vi kommer snart att kontakta dig med mer information om våra produkter. 1744 </div> 1745 <div class="subtext disclaimer"> 1746 Vi ser fram emot att diskutera hur vi bäst kan möta dina behov. 1747 </div> 1748 </div> 1749 </div> 1750 <div v-else-if="hideProducts === true"> 1751 <div class="header"> 1752 <b>Tack för din information</b> 1753 </div> 1754 <div class="subtext mt-5 mt-md-4 mb-5"> 1755 <div class="subtext subheader"> 1756 Din prenumeration har uppdaterats med den nya betalningsinformationen. 1757 </div> 1758 </div> 1759 </div> 1760 <div v-else=""> 1761 <div class="header"> 1762 <b>Tack för registreringen</b> 1763 </div> 1764 <div class="subtext mt-5 mt-md-4 mb-5"> 1765 <div class="subtext subheader"> 1766 Du har fått ett e-postmeddelande för att aktivera din prenumeration. 1767 </div> 1768 <div class="subtext disclaimer"> 1769 Kan du inte hitta aktiveringsmailet? Kolla din skräppost. 1770 </div> 1771 </div> 1772 </div> 1773 1774 <a id="frontpage" class="btn btn-primary btn-rounded" href="/"> 1775 <div> 1776 Gå till startsidan 1777 </div> 1778 </a> 1779 </div> 1780 </div> 1781 </div> 1782 1783 <global-errors v-bind:value="globalErrors"></global-errors> 1784 </div> 1785 1786 <div v-if="!loading && !selectedProduct && hideProducts === false" class="subscription-bottom-text"> 1787 För alla prenumerationer tilldelas en personlig inloggning över flera enheter 1788 </div> 1789 </div> 1790 1791
1791<script type="text/javascript"> 1792 var vm; 1793 //<![CDATA[ 1794 (function () { 1795 vm = new Vue({ 1796 el: '#supscriptions-app', 1797 data: { 1798 productGroups: null, 1799 loading: false, 1800 selectedProduct: null, 1801 hideProducts: false, 1802 productPage: false, 1803 deeplinkProduct: null, 1804 paymentPage: false, 1805 receiptPage: false, 1806 renewCard: false, 1807 pendingOrder: { 1808 company: true, 1809 firstName: '', 1810 lastName: '', 1811 email: '', 1812 companyName: '', 1813 cvr: '', 1814 address: '', 1815 country: '2', 1816 phoneNumber: '', 1817 callingCode: '', 1818 streetName: '', 1819 streetNumber: '', 1820 floor: '', 1821 door: '', 1822 addressFull: '', 1823 postalCode: '', 1824 addressCity: '', 1825 remarks: '', 1826 product: '', 1827 campaignCode: 'paywall', 1828 marketing: false, 1829 submitType: '' 1830 }, 1831 showDetailedAddress: false, 1832 termsAccepted: false, 1833 formErrors: {}, 1834 globalErrors: [], 1835 countryOptions: [], 1836 bordasModule: true, 1837 bordas: false, 1838 productType: 'SUBSCRIPTION', 1839 campaignProductId: '', 1840 products: [], 1841 productClasses: [], 1842 bundle: { 1843 id_10626: '' 1844 }, 1845 paymentId: null, 1846 checkoutLanguage: null, 1847 subscriptionId: null, 1848 checkoutKeyValue: null, 1849 subscriptionSet: false, 1850 companyDisabled: false, 1851 onlinePaymentModule: false, 1852 productCols: 3, 1853 productIndex: 0, 1854 slideDirection: '', 1855 touchQueue: [], 1856 touchStartX: 0, 1857 touchEndX: 0, 1858 minSwipeDistance: 50, 1859 isSmallScreen: false, 1860 mediaQuery: null 1861 }, 1862 created: function () { 1863 Vue.$bundle("portal", this.bundle, (bundle) => { 1864 this.bundle = bundle; 1865 }); 1866 1867 this.mediaQuery = window.matchMedia('(min-width: 768px)'); 1868 this.updateScreenSize(this.mediaQuery); 1869 this.mediaQuery.addEventListener('change', this.updateScreenSize); 1870 1871 this.loading = true; 1872 const promises = []; 1873 1874 promises.push(this.getProductGroups(this.productType, this.campaignProductId).then((data) => { 1875 this.productGroups = data; 1876 1877 for (var i = 0; i < this.productGroups.length; i++) { 1878 this.products = this.products.concat(this.productGroups[i].products ? this.productGroups[i].products : []); 1879 } 1880 1881 window.addEventListener('resize', this.handleResize); 1882 this.handleResize(); 1883 1884 if (this.window.width < 768) { 1885 for (var i = 0; i < this.productGroups.length; i++) { 1886 if (this.productGroups[i].products) { 1887 for (var j = 0; j < this.productGroups[i].products.length; j++) { 1888 this.productGroups[i].products[j].showFeatures = false; 1889 } 1890 } 1891 } 1892 } 1893 1894 // Navigate to the product from the link if a product is specified in it. 1895 if (this.deeplinkProduct !== null) { 1896 this.navigateToProductInLink(); 1897 } 1898 1899 if (this.subscriptionId !== null) { 1900 this.navigateToPaymentToRenew(); 1901 } 1902 })); 1903 1904 promises.push(this.fetchCountries().then((data) => { 1905 this.countryOptions = data; 1906 let foundCountry = this.countryOptions.find(country => { 1907 return parseInt(country.id) == this.pendingOrder.country; 1908 }); 1909 this.pendingOrder.callingCode = foundCountry.callingCode; 1910 })); 1911 1912 promises.push(this.getcheckoutLang().then((data) => { 1913 this.checkoutLanguage = data; 1914 })); 1915 1916 Promise.all(promises).finally(() => { 1917 this.loading = false;
1918 }); 1919 1920 // Check if it is a "deeplink". (A '' in the link.) 1921 // Save it to be used for navigating the product. 1922 1923 const queryString = window.location.search; 1924 const urlParams = new URLSearchParams(queryString); 1925 const deeplinkProduct = urlParams.get('product') 1926 if (deeplinkProduct) { 1927 this.deeplinkProduct = deeplinkProduct; 1928 } 1929 const paymentId = urlParams.get('paymentid'); 1930 if (paymentId) { 1931 this.paymentId = paymentId; 1932 } 1933 const subscriptionId = urlParams.get('subscriptionid'); 1934 if (subscriptionId) { 1935 this.subscriptionId = subscriptionId; 1936 } 1937 }, 1938 beforeDestroy: function () { 1939 this.mediaQuery.removeEventListener('change', this.updateScreenSize); 1940 }, 1941 destroyed: function () { 1942 window.removeEventListener('resize', this.handleResize); 1943 }, 1944 computed: { 1945 visibleProducts: function () { 1946 if (this.isSmallScreen) { 1947 return this.products; 1948 } 1949 return this.products.slice(this.productIndex, this.productIndex + this.productCols); 1950 } 1951 }, 1952 watch: { 1953 "pendingOrder.country": function (newId) { 1954 let foundCountry = this.countryOptions.find(country => parseInt(country.id) == this.pendingOrder.country); 1955 this.pendingOrder.callingCode = foundCountry.callingCode; 1956 1957 const country = this.countryOptions.find(country => parseInt(country.id) == newId); 1958 this.callingCode = country ? country.callingCode : ""; 1959 } 1960 }, 1961 methods: { 1962 updateScreenSize: function (e) { 1963 // If the media query matches, we're on 768+ pixels screens 1964 this.isSmallScreen = !e.matches; 1965 }, 1966 handleResize: function () { 1967 var widthBefore = this.window.width; 1968 1969 1970 this.window.width = window.innerWidth; 1971 this.window.height = window.innerHeight; 1972 1973 if (this.window.width < 768 && widthBefore > 767.9999) { 1974 for (var i = 0; i < this.productGroups.length; i++) { 1975 if (this.productGroups[i].products) { 1976 for (var j = 0; j < this.productGroups[i].products.length; j++) { 1977 this.productGroups[i].products[j].showFeatures = false; 1978 } 1979 } 1980 } 1981 } 1982 if (this.window.width >= 767.9999) { 1983 for (var i = 0; i < this.productGroups.length; i++) { 1984 if (this.productGroups[i].products) { 1985 for (var j = 0; j < this.productGroups[i].products.length; j++) { 1986 this.productGroups[i].products[j].showFeatures = true; 1987 } 1988 } 1989 } 1990 } 1991 }, 1992 scrollToTop: function () { 1993 this.$nextTick(() => { 1994 window.scrollTo(0, 0); 1995 }); 1996 }, 1997 computedFeatures: function (id) { 1998 var features = []; 1999 for (var i = 0; i < this.products.length; i++) { 2000 if (this.products[i].id === id) { 2001 features = this.products[i].features; 2002 break; 2003 } 2004 } 2005 return features; 2006 }, 2007 selectProduct: function (product) { 2008 this.selectedProduct = product; 2009 this.productPage = true; 2010 this.pendingOrder.product = product; 2011 if (!this.selectedProduct.price) { 2012 this.termsAccepted = true; 2013 } 2014 // Log selected product in the link 2015 // for it to be used for deeplinks for social medias and other places. 2016 2017 // Get the current URL 2018 let url = new URL(window.location); 2019 // Add or update the query parameter 2020 url.searchParams.set('product', product.id); 2021 // Update the URL without reloading the page 2022 history.pushState({}, '', url); 2023 2024 2025 // window.location = window.location + '?product=' + product.id; 2026 if (this.bordasModule) { 2027 this.fetchBordas().then((data) => { 2028 if (data === true) { 2029 this.bordas = true; 2030 } 2031 }); 2032 } 2033 2034 this.getCheckoutKey().then((data) => { 2035 if (data) { 2036 this.checkoutKeyValue = data; 2037 } 2038 }); 2039 2040 openOrder(this.pendingOrder.product.id, this.pendingOrder.product.name, this.productGroups); 2041 2042 if (!this.selectedProduct.price) { 2043 // this.pendingOrder.company = true; 2044 this.companyDisabled = true; 2045 } else { 2046 // this.pendingOrder.company = false;
2047 this.companyDisabled = false; 2048 } 2049 2050 this.scrollToTop(); 2051 }, 2052 deselectProduct: function (product) { 2053 2054 let url = new URL(window.location); 2055 // Add or update the query parameter 2056 url.searchParams.delete('product', product.id); 2057 // Update the URL without reloading the page 2058 history.pushState({}, '', url); 2059 2060 this.selectedProduct = null; 2061 this.productPage = false; 2062 this.pendingOrder.product = null; 2063 this.bordas = false; 2064 this.termsAccepted = false; 2065 this.pendingOrder.companyName = ''; 2066 this.pendingOrder.cvr = ''; 2067 this.pendingOrder.company = true; 2068 var length = this.products.length; 2069 2070 this.scrollToTop(); 2071 }, 2072 navigateToProductInLink: function () { 2073 for (var i = 0; i < this.productGroups.length; i++) { 2074 for (var j = 0; j < this.productGroups[i].products.length; j++) { 2075 // Found product group 2076 if (this.deeplinkProduct == this.productGroups[i].products[j].id) { 2077 for (var q = 0; q < this.products.length; q++) { 2078 // Found product 2079 if (this.deeplinkProduct == this.products[q].id) { 2080 var product = this.products[q]; 2081 2082 // Get the current URL 2083 let url = new URL(window.location); 2084 // Add or update the query parameter 2085 url.searchParams.set('product', product.id); 2086 this.selectedProduct = product; 2087 this.pendingOrder.product = product; 2088 this.fetchBordas().then((data) => { 2089 if (data === true) { 2090 this.bordas = true; 2091 } 2092 }); 2093 2094 if (!this.selectedProduct.price) { 2095 // this.pendingOrder.company = true; 2096 this.companyDisabled = true; 2097 } else { 2098 // this.pendingOrder.company = false; 2099 this.companyDisabled = false; 2100 } 2101 2102 if (this.paymentId) { 2103 url.searchParams.set('paymentid', this.paymentId); 2104 // Update the URL without reloading the page 2105 history.pushState({}, '', url); 2106 this.paymentPage = true; 2107 this.productPage = false; 2108 2109 this.getCheckoutKey().then((data) => { 2110 if (data) { 2111 this.checkoutKeyValue = data; 2112 this.$nextTick(() => { 2113 $('#progress-bar li:not(.active):first').addClass('active'); 2114 $('#progress-bar li.active:first').removeClass('active'); 2115 vm.setupCheckout(); 2116 }); 2117 } 2118 }); 2119 } else { 2120 this.productPage = true; 2121 // Update the URL without reloading the page 2122 history.pushState({}, '', url); 2123 } 2124 } 2125 } 2126 } 2127 } 2128 } 2129 }, 2130 navigateToPaymentToRenew: function () { 2131 let url = new URL(window.location); 2132 url.searchParams.set('subscriptionid', this.subscriptionId); 2133 if (this.paymentId != null) { 2134 url.searchParams.set('paymentid', this.paymentId); 2135 } 2136 2137 this.renewCard = true; 2138 2139 // Update the URL without reloading the page 2140 history.pushState({}, '', url); 2141 this.paymentPage = true; 2142 this.productPage = false;
2143 this.hideProducts = true; 2144 2145 this.$nextTick(() => { 2146 //APPEND NEW PAYMENT ID TO URL THEN SETUP 2147 vm.renewCardPayment(vm.subscriptionId, vm.paymentId); 2148 }); 2149 2150 }, 2151 showFeatures: function (product, showFeatures) { 2152 if (this.window.width < 768) { 2153 product.showFeatures = showFeatures; 2154 } 2155 }, 2156 prevProduct: function () { 2157 if (this.productIndex > 0) { 2158 this.slideDirection = 'col-4-slide-right'; 2159 this.productIndex--; 2160 } 2161 }, 2162 nextProduct: function () { 2163 if (this.productIndex + this.productCols < this.products.length) { 2164 this.slideDirection = 'col-4-slide-left'; 2165 this.productIndex++; 2166 } 2167 }, 2168 onTouchStart: function (event) { 2169 this.touchQueue.push(true); 2170 this.$nextTick(() => { 2171 this.touchStartX = event.changedTouches[0].screenX; 2172 this.touchEndX = this.touchStartX; 2173 }); 2174 }, 2175 onTouchMove: function (event) { 2176 this.$nextTick(() => { 2177 this.touchEndX = event.changedTouches[0].screenX; 2178 const deltaX = this.touchEndX - this.touchStartX; 2179 if (Math.abs(deltaX) > this.minSwipeDistance) { 2180 if (deltaX > 0) { 2181 this.prevProduct(); 2182 } else { 2183 this.nextProduct(); 2184 } 2185 } 2186 }); 2187 }, 2188 onTouchEnd: function (event) { 2189 this.onTouchMove(event); 2190 this.$nextTick(() => { 2191 setTimeout(() => { 2192 this.touchQueue.splice(0, 1); 2193 }, 500); 2194 }); 2195 }, 2196 formattedProductName: function (productName) { 2197 return productName.charAt(0).toUpperCase() + productName.slice(1).toLowerCase(); 2198 }, 2199 formattedPrice: function (price) { 2200 if (price) { 2201 var formattedPrice; 2202 // return price.replace(/^(\d+)/, '<b style="margin-right: 1mm">$1</b> '); 2203 formattedPrice = price.replace(/^(\d+)/, '[b]$1[/b]'); 2204 return Vue.$bbUtil.inline(formattedPrice); 2205 } 2206 return price; 2207 }, 2208 handleSubmit: async function (event) { 2209 const submitType = event.submitter?.value; 2210 this.pendingOrder.submitType = submitType; 2211 2212 this.$clearFormErrors(this.formErrors); 2213 var errors = false; 2214 var errorMessages = '{'; 2215 2216 if (!this.pendingOrder.postalCode) { 2217 errors = true; 2218 errorMessages = errorMessages + "\"postalCode\": [\"Obligatoriskt.\"], " 2219 } 2220 if (!this.pendingOrder.addressCity) { 2221 errors = true; 2222 errorMessages = errorMessages + "\"addressCity\": [\"Obligatoriskt.\"], " 2223 } 2224 if (this.bordas) { 2225 if (!this.pendingOrder.streetName) { 2226 errors = true; 2227 errorMessages = errorMessages + "\"streetName\": [\"Obligatoriskt.\"], " 2228 } 2229 if (!this.pendingOrder.streetNumber) { 2230 errors = true; 2231 errorMessages = errorMessages + "\"streetNumber\": [\"Obligatoriskt.\"], " 2232 } 2233 } else { 2234 if (!this.pendingOrder.addressFull) { 2235 errors = true; 2236 errorMessages = errorMessages + "\"addressFull\"
2236: [\"Obligatoriskt.\"], " 2237 } 2238 } 2239 2240 if (errors) { 2241 if (!this.showDetailedAddress) { 2242 this.showDetailedAddress = true; 2243 } 2244 errorMessages = errorMessages.substring(0, errorMessages.length - 2); 2245 errorMessages = errorMessages + "}" 2246 const obj = JSON.parse(errorMessages); 2247 this.$showFormErrors(this.formErrors, obj); 2248 return; 2249 } 2250 2251 //Serverside validation 2252 this.formErrors = {}; 2253 // Create order 2254 this.submitOrderForm(this.pendingOrder).then((data) => { 2255 if (!data) { 2256 //Do nothing if validation failed 2257 } else { 2258 if (this.pendingOrder.submitType === "company") { 2259 //Contact 2260 $('#progress-bar li:not(.active):first').addClass('active'); 2261 $('#progress-bar li.active:first').removeClass('active'); 2262 2263 this.receiptPage = true; 2264 this.productPage = false; 2265 successGA(window.ga); 2266 } else if (this.pendingOrder.submitType === "invoice") { 2267 //If bordas invoice payment then skip nexi payment and remember salespanel 2268 $('#progress-bar li:not(.active):first').addClass('active'); 2269 $('#progress-bar li:not(.active):first').addClass('active'); 2270 $('#progress-bar li.active:first').removeClass('active'); 2271 $('#progress-bar li.active:first').removeClass('active'); 2272 this.receiptPage = true; 2273 this.productPage = false; 2274 successGA(window.ga); 2275 salespanel(); 2276 } else if (this.pendingOrder.submitType === "card") { 2277 // Product that need payment 2278 $('#progress-bar li:not(.active):first').addClass('active'); 2279 $('#progress-bar li.active:first').removeClass('active'); 2280 this.paymentPage = true; 2281 this.productPage = false; 2282 this.handlePaymentAndCreate() 2283 } else if (this.pendingOrder.submitType === "trail") { 2284 //Free trial 2285 $('#progress-bar li:not(.active):first').addClass('active'); 2286 $('#progress-bar li:not(.active):first').addClass('active'); 2287 $('#progress-bar li.active:first').removeClass('active'); 2288 $('#progress-bar li.active:first').removeClass('active'); 2289 this.receiptPage = true; 2290 this.productPage = false; 2291 successGA(window.ga); 2292 salespanel(); 2293 } 2294 } 2295 }); 2296 }, 2297 fetchCountries: async function () { 2298 return this.$services.countryService.hostCountriesFirst(); 2299 }, 2300 getcheckoutLang: async function () { 2301 return this.$services.paymentService.getCheckoutLanguage(); 2302 }, 2303 fetchBordas: function () { 2304 if (this.selectedProduct) { 2305 return this.$services.paymentService.supportBordas(this.selectedProduct.id); 2306 } else { 2307 return false; 2308 } 2309 }, 2310 getCheckoutKey: async function () { 2311 return this.$services.paymentService.getCheckoutKey(); 2312 }, 2313 getProductGroups: async function (type, campaignId) { 2314 if (campaignId) { 2315 return this.$services.paymentService.getProductGroups(type, campaignId); 2316 } else { 2317 return this.$services.paymentService.getProductGroups(type); 2318 } 2319 }, 2320 submitOrderForm: async function (pendingOrder) { 2321 this.formErrors = {}; 2322 this.globalErrors = {}; 2323 return this.validationService().submit(pendingOrder).then(() => { 2324 return true; 2325 }).catch((data) => { 2326 this.$showFormErrors(this.formErrors, data.response.data.errors); 2327 2328 if (data.response.data.errors.globalError) { 2329 this.globalErrors = data.response.data.errors.globalError; 2330 } 2331 return false;
2332 }); 2333 }, 2334 handlePaymentOrder: async function (pendingOrder) { 2335 return this.validationService().payment(pendingOrder).then((data) => { 2336 this.paymentId = data; 2337 2338 let url = new URL(window.location); 2339 url.searchParams.set('paymentid', this.paymentId); 2340 // Update the URL without reloading the page 2341 history.pushState({}, '', url); 2342 this.getCheckoutKey().then((data) => { 2343 if (data) { 2344 this.checkoutKeyValue = data; 2345 this.setupCheckout(); 2346 } 2347 }); 2348 return true; 2349 }).catch((data) => { 2350 this.$showFormErrors(this.formErrors, data.response.data.errors); 2351 2352 if (data.response.data.errors.globalError) { 2353 this.globalErrors = data.response.data.errors.globalError; 2354 } 2355 return false; 2356 }); 2357 }, 2358 updateSubscription: async function (paymentId) { 2359 return this.validationService().subscription(paymentId).then(() => { 2360 return true; 2361 }).catch((data) => { 2362 this.$showFormErrors(this.formErrors, data.response.data.errors); 2363 2364 if (data.response.data.errors.globalError) { 2365 this.globalErrors = data.response.data.errors.globalError; 2366 } 2367 return false; 2368 }); 2369 }, 2370 renewCardPayment: async function (subscriptionId, paymentId) { 2371 return this.validationService().renew(subscriptionId, paymentId).then((data) => { 2372 this.paymentId = data; 2373 2374 let url = new URL(window.location); 2375 url.searchParams.set('paymentid', this.paymentId); 2376 // Update the URL without reloading the page 2377 history.pushState({}, '', url); 2378 2379 this.getCheckoutKey().then((data) => { 2380 if (data) { 2381 this.checkoutKeyValue = data; 2382 this.$nextTick(() => { 2383 $('#progress-bar li:not(.active):first').addClass('active'); 2384 $('#progress-bar li.active:first').removeClass('active'); 2385 vm.setupCheckout(); 2386 }); 2387 } 2388 }); 2389 return true; 2390 }).catch((data) => { 2391 this.$showFormErrors(this.formErrors, data.response.data.errors); 2392 2393 if (data.response.data.errors.globalError) { 2394 this.globalErrors = data.response.data.errors.globalError; 2395 } 2396 return false; 2397 }); 2398 }, 2399 validationService: function () { 2400 return { 2401 submit: async function (pendingOrder) { 2402 return vm.$services.paymentService.validateOrder(pendingOrder); 2403 }, 2404 payment: async function (pendingOrder) { 2405 return vm.$services.paymentService.handlePayment(pendingOrder); 2406 }, 2407 subscription: async function (paymentId) { 2408 return vm.$services.paymentService.setSubscription(paymentId); 2409 }, 2410 renew: async function (subscriptionId, paymentId) { 2411 return vm.$services.paymentService.renewCard(subscriptionId, paymentId); 2412 } 2413 } 2414 }, 2415 handlePaymentAndCreate: function () { 2416 this.formErrors = {}; 2417 // Create order 2418 this.handlePaymentOrder(this.pendingOrder).then((data) => { 2419 if (!data) { 2420 //Do nothing if validation failed 2421 } 2422 }); 2423 }, 2424 setupCheckout: function () {
2425 const urlParams = new URLSearchParams(window.location.search); 2426 const paymentId = urlParams.get('paymentid') ? urlParams.get('paymentid') : this.paymentId; 2427 if (paymentId) { 2428 const checkoutOptions = { 2429 checkoutKey: vm.checkoutKeyValue, 2430 paymentId: paymentId, 2431 containerId: "checkout-container-div", 2432 language: vm.checkoutLanguage 2433 }; 2434 const checkout = new Dibs.Checkout(checkoutOptions); 2435 checkout.on('payment-completed', function (response) { 2436 vm.formErrors = {}; 2437 //Updated payment/subscription row in Database 2438 if (!vm.subscriptionSet) { 2439 vm.subscriptionSet = true; 2440 vm.updateSubscription(vm.paymentId).then((data) => { 2441 if (!data) { 2442 //Do nothing if validation failed 2443 } else { 2444 $('#progress-bar li:not(.active):first').addClass('active'); 2445 $('#progress-bar li:not(.active):first').addClass('active'); 2446 $('#progress-bar li.active:first').removeClass('active'); 2447 $('#progress-bar li.active:first').removeClass('active'); 2448 vm.paymentPage = false; 2449 vm.receiptPage = true; 2450 var paymentFrame = document.getElementById("nets-checkout-iframe"); 2451 paymentFrame.style.display = "none"; 2452 2453 salespanel(); 2454 successGA(window.ga); 2455 } 2456 }); 2457 2458 } 2459 }); 2460 } else { 2461 $('#progress-bar li.active:last').removeClass('active'); 2462 this.paymentPage = false; 2463 this.productPage = true; 2464 } 2465 2466 }, 2467 formatFeatureTitle(title) { 2468 return title.replace(/\[\[(.*?)\]\]/g, '<span style="text-decoration: underline;">$1</span>'); 2469 }, 2470 containsDoubleBrackets(title) { 2471 return /\[\[.*?\]\]/.test(title); // Checks if title contains [[...]] 2472 }, 2473 } 2474 }); 2475 })(); 2476 2477 function openOrder(productId, productName, groups) { 2478 // Find related product group 2479 var productGroup = groups.find(group => { 2480 return group.products.find(groupProduct => { 2481 return groupProduct.id == productId; 2482 }) 2483 }); 2484 2485 // Make product and group name and available for GTM 2486 window.dataLayer = window.dataLayer || []; 2487 window.dataLayer.push({"product-name": productName}); 2488 window.dataLayer.push({"product-group": productGroup.name}); 2489 2490 } 2491 //]]> 2492 </script>
2492 2493 2494
2494<script> 2495 2496 //Hide the dropdown menus if the user clicks elsewhere 2497 $(document).on('click', function (e) { 2498 if ($(e.target).closest('#autocompleteDropdown').length == 0) { 2499 $("#autocompleteDropdown").hide(); 2500 } 2501 }); 2502 2503 var lastTime = 0; 2504 2505 //Construct call to autocomplete Api 2506 function makeAPICall() { 2507 var bordas = vm.$data.bordas; 2508 var now = new Date().getTime(); // Time in milliseconds 2509 if (now - lastTime < 500) { 2510 return; 2511 } else { 2512 lastTime = now; 2513 } 2514 2515 var inputValue; 2516 var country; 2517 2518 // Get value from the input box 2519 inputValue = document.getElementById('addressInput').value; 2520 country = vm.$data.pendingOrder.country; 2521 2522 if (inputValue && inputValue.length > 2 && country) { 2523 // Construct the API URL with input value 2524 var apiUrl; 2525 apiUrl = "/rest/autocompleteapi/gep-autocomplete?country=" + country + "&address=" + inputValue; 2526 2527 // Make the API call using fetch 2528 fetch(apiUrl) 2529 .then(response => response.json()) 2530 .then(data => { 2531 // Update the dropdown with names from the API response 2532 updateAutocompleteDropdown(data, bordas, country); 2533 }) 2534 .catch(error => { 2535 console.error('Error:', error); 2536 }); 2537 } 2538 } 2539 2540 //Display the result from autocomplete Api 2541 function updateAutocompleteDropdown(addresses, bordas, country) { 2542 var dropdown; 2543 2544 dropdown = document.getElementById('autocompleteDropdown'); 2545 2546 dropdown.innerHTML = ''; // Clear existing options 2547 2548 // Add new options based on the API response 2549 addresses.forEach(address => { 2550 var streetName = address.streetName;
2551 var houseNumber = address.houseNumber; 2552 var zipcode = address.zipcode; 2553 var city = address.city; 2554 var floor = address.floor != null ? address.floor : null; 2555 var door = address.door != null ? address.door : null; 2556 var letter = address.letter != null ? address.letter : null; 2557 2558 var textBordas = streetName + " " + houseNumber + (letter ? letter : "") + (floor ? " " + floor : "") + (door ? " " + door : "") + " " + zipcode + " " + city; 2559 var text = streetName + " " + houseNumber + (letter ? letter : "") + (floor ? " " + floor : "") + (door ? " " + door : ""); 2560 2561 var item = document.createElement('div'); 2562 item.className = 'autocomplete-suggestion'; 2563 item.textContent = textBordas; 2564 item.onclick = function () { 2565 // Set the selected value to the detailed input fields 2566 vm.$data.pendingOrder.streetName = streetName; 2567 vm.$data.pendingOrder.streetNumber = houseNumber + (letter ? letter : ""); 2568 vm.$data.pendingOrder.floor = floor; 2569 vm.$data.pendingOrder.door = door; 2570 vm.$data.pendingOrder.postalCode = zipcode; 2571 vm.$data.pendingOrder.addressCity = city; 2572 vm.$data.pendingOrder.addressFull = text; 2573 2574 //Set input of main field 2575 document.getElementById('addressInput').value = textBordas; 2576 2577 // Hide the dropdown 2578 dropdown.style.display = 'none'; 2579 }; 2580 dropdown.appendChild(item); 2581 }); 2582 2583 var item = document.createElement('div'); 2584 var reference = document.createElement('a'); 2585 reference.href = "https://geposit.se/"; 2586 reference.innerHTML = 'Adressuppgifter hämtade från Geposit';
2587 reference.rel = "nofollow"; 2588 reference.target = "_blank"; 2589 item.appendChild(reference); 2590 2591 item.className = 'autocomplete-credit'; 2592 dropdown.appendChild(item); 2593 2594 // Show the dropdown below the input field 2595 dropdown.style.display = addresses.length > 0 ? 'block' : 'none'; 2596 } 2597 </script>
2597 2598
2598<script> 2599 async function salespanel() { 2600 window.dataLayer = window.dataLayer || []; 2601 window.dataLayer.push({"event": "subscription_order"}); 2602 window.$salespanel = window.$salespanel || []; 2603 window.$salespanel.push(["set", "activity:customActivity", "website", "Abonnement bestilling"]); 2604 } 2605 </script>
2605 2606
2606<script> 2607 function successGA(ga) { 2608 if (typeof ga !== 'undefined' && ga) { 2609 ga('send', 'event', 'Subscriber', 'join'); 2610 } else if (typeof _gaq !== 'undefined' && _gaq) { 2611 _gaq.push(['_trackEvent', 'Subscriber', 'join', 'Metal Supply']); 2612 } 2613 } 2614 </script>
2614 2615
2615<script> 2616 function formatText(inputText, targetElement) { 2617 targetElement.textContent = ""; // Clear previous content 2618 2619 let parts = inputText.split(/(\[\[.*?\]\])/g); // Split by [[ ]] 2620 2621 parts.forEach(part => { 2622 if (part.match(/\[\[(.*?)\]\]/)) { 2623 let underlinedText = document.createElement("u"); 2624 underlinedText.textContent = part.replace(/\[\[|\]\]/g, ""); // Remove [[ ]] 2625 targetElement.appendChild(underlinedText); 2626 } else { 2627 let normalText = document.createTextNode(part); 2628 targetElement.appendChild(normalText); 2629 } 2630 }); 2631 } 2632 </script>
2632 2633 </main> 2634 2635 </div> 2636 </div> 2637 <footer id="main-footer" v-cloak="" class="unprintable"> 2638 <nav> 2639 <div class="row text-center text-md-start"> 2640 <div class="col-12 col-md-3 mb-5 mb-md-0"> 2641 <a href="/"> 2642 <img src="https://f.nordiskemedier.dk/logo/inverted/10.png" alt="Metal Supply" loading="lazy" class="logo" /> 2643 </a> 2644 </div> 2645 <div class="col-12 col-md-6"> 2646 <div class="row"> 2647 <div v-for="(option, i) in footerOptions" v-bind:key="i" class="col-12 mb-4" v-bind:class="footerOptionClass"> 2648 <h6>{{ option.name }}</h6> 2649 <ul> 2650 <li v-for="(child, j) in option.children" v-bind:key="j"> 2651 <a v-bind:href="child.target" v-bind:target="child.external ? '_blank' : ''">{{ child.name }}</a> 2652 </li> 2653 </ul> 2654 </div> 2655 </div> 2656 </div> 2657 </div> 2658 </nav> 2659 <nav> 2660 <div> 2661 <div> 2662 <div class="row"> 2663 <div class="col-12 col-md-8 order-1 order-md-2 mb-4 text-center address"><div>Metal Supply - metall- och verkstadsindustrins knutpunkt</div> 2664 </div> 2665 <div class="col-6 col-md-2 order-2 order-md-1 mb-4"> 2666 </div> 2667 <div class="col-6 col-md-2 order-3 mb-4 text-end"> 2668 <ip-access-toggle></ip-access-toggle> 2669 </div> 2670 </div> 2671 </div> 2672 </div> 2673 </nav> 2674 </footer> 2675
2675<script type="application/javascript"> 2676 //<![CDATA[ 2677 new Vue({ 2678 el: "#main-footer", 2679 data: { 2680 footerOptions: [{"children":[{"external":false,"name":"Bli prenumerant","target":"/products/subscription.html"},{"external":true,"name":"Bli medlem","target":"https://nordiskemediehus.dk/se/produkt/medlemskab/"},{"external":true,"name":"Annonsera","target":"https://nordiskemediehus.dk/se/se-metal-supply/"},{"external":true,"name":"Se alla produkter","target":"https://nordiskemediehus.dk/se/produkter/"}],"name":"Produkter"},{"children":[{"external":false,"name":"Cookies och personuppgiftspolicy","target":"/privacy"},{"external":false,"name":"Juridik och upphovsrätt","target":"/page/view.html?id=17921"},{"external":false,"name":"KlagomÃ¥l","target":"/page/view.html?id=17919"},{"external":false,"name":"Sponsrat innehÃ¥ll","target":"/lb-article"}],"name":"Ãvrigt"},{"children":[{"external":false,"name":"Kontakta redaktionen","target":"mailto:[email protected]"},{"external":false,"name":"Kontakta oss","target":"/page/view.html?id=18313"},{"external":true,"name":"Jobba hos oss","target":"https://nordiskemediehus.dk/se/om-os/karriere/"},{"external":true,"name":"Om Metal Supply","target":"https://nordiskemediehus.dk/se/se-metal-supply/"}],"name":"Kontakt"}] 2681 }, 2682 computed: { 2683 footerOptionClass: function () { 2684 const len = this.footerOptions.length; 2685 if (len <= 1) { 2686 return "col-sm-12"; 2687 } else if (len == 2) { 2688 return "col-sm-6"; 2689 } else if (len == 3) { 2690 return "col-sm-4"; 2691 } else if (len == 4) { 2692 return "col-sm-3"; 2693 } else if (len >= 5 && len <= 6) { 2694 return "col-sm-4"; 2695 } else if (len >= 7 && len <= 8) { 2696 return "col-sm-3"; 2697 } else if (len == 9) { 2698 return "col-sm-4"; 2699 } else { 2700 return "col-sm-3"; 2701 } 2702 } 2703 } 2704 }); 2705 //]]> 2706 </script>
2706 2707 </div></body> 2708 2709</html>
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.