• Original SCANPAN
  • Worldclass cookware
  • Danish heritage
English Dansk Norsk Deutsch Nederlands
Scanpan
  • Pans
    Gå til Pans
    • Inspiration
      • A pan for every purpose
      • Induction cookware
      • Non-stick pans
    • Category
      • Fry pans
      • Roasting pans
      • Grill pans
      • Sauté pans
      • Wok
      • Special purpose pans
    • Series
      • NEW! Pro SB+
      • NEW! Urban
      • TechnIQ
      • CTX
      • Black Iron
      • Classic
      • See all ranges
    • NEW!
      Black Iron pans
  • Pots
    Gå til Pots
    • Category
      • Pots
      • Saucepans
      • Special purpose pots
      • Accessories for pots
    • Series
      • NEW! Pro SB+
      • STS
      • CTX
      • Impact
      • Classic Induction
      • Fusion 5
      • See all ranges
    • Great for baking
      Get the perfect crust
  • Knives
    Gå til Knives
    • Category
      • Knives for meat
      • Knives for veggies
      • Knives for bread
      • Knife sets
      • Knife sharpening
      • Steak cutlery
      • Accessories
    • Series
      • Classic
      • Spectrum
    • Santoku Knife
      Ideal for paper-thin slices
  • Accessories
    Gå til Accessories
    • Category
      • Kitchen tools
      • Cutting boards
      • Lids
      • Racks and inserts
      • Maintenance
    • Kitchen tools
      In beautiful carbonized ash
  • TO GO
    Gå til TO GO
    • Category
      • Vacuum bottle
      • NEW! Explore vacuum bottle
      • Travel mug
      • Cup
      • French press coffee maker
      • Serving bottle
      • Tumbler
      • Serving bottle gift set
    • READY, SET...GO!
      Vacuum bottles and cups
  • Series
    Gå til Series
    • Overview
      • See all ranges
      • NEW! Pro SB+
      • NEW! Urban
    • Our universes
      • Crafted by Chefs - Cookware. Reinvented.
      • Classic - a universe of highest performance cookware
      • Maitre D' - a universe of luxury and aesthetics
    • Danish since 1956
      Proudly handmade
  • SCANPAN Guide
Error executing template "Designs/Scanpan/eCom/Product/product.cshtml"
System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at CompiledRazorTemplates.Dynamic.RazorEngine_09448fc037be412e9c1c717f3d60e826.Execute() in E:\dynamicweb.net\Solutions\scanpan-live\Files\Templates\Designs\Scanpan\eCom\Product\product.cshtml:line 328
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @using System.Web 2 @using System.Drawing 3 @using Dynamicweb 4 @using Dynamicweb.Admin 5 @using Dynamicweb.Data 6 @inherits RazorTemplateBase<RazorTemplateModel<Template>> 7 @using Dynamicweb.Rendering 8 @using NoZebra.Util 9 @using Dynamicweb.Content; 10 @using Dynamicweb.Content.Items; 11 @using System.Text.RegularExpressions; 12 @* Note that this file will be inserted directly into another one by Dynamicweb IncludeFile 13 before evaluating the Razor code, so keep the syntax right *@ 14 15 @functions { 16 17 /** 18 * Custom date format from string input 19 * String needs to be in a date format, though 20 * @param {String} d 21 */ 22 string FormatDate(string d) { 23 string output = d; 24 // Language 25 string lang = GetGlobalValue("Global:Area.Lang"); 26 // Date format 27 string dateFormat = (lang == "da") ? "d. MMM. yyyy" : "MMM d yyyy"; 28 // Try parsing string as DateTime 29 DateTime dateValue; 30 if (DateTime.TryParse(d, out dateValue)) { 31 output = dateValue.ToString(dateFormat); 32 } 33 // Format date input 34 // Return formatted date 35 return output; 36 } 37 38 /** 39 * Format number form string input 40 * Returns a clean number to be used in sorting loops 41 * @param {String} s 42 */ 43 string FormatNumber(string s) { 44 Regex rgx = new Regex(@"[^0-9.,]"); // match everything but numbers, comma and period 45 // Replace all letters with whitespace 46 s = rgx.Replace(s, " "); 47 // Remove everything after the first space 48 // This takes care of values like "28 x 28 cm" and "20 l" 49 return s.Split(new[]{" "}, StringSplitOptions.None)[0]; 50 } 51 52 /** 53 * Custom number formatting from string input 54 * @param {String} s 55 */ 56 decimal NumberFromString(String s) { 57 decimal output = decimal.Parse("0"); // fallback 58 string inputFormatted = FormatNumber(s); // strip input of anything but numbers 59 // Check if item has a value in the field 60 if( !string.IsNullOrEmpty(inputFormatted) ) { 61 decimal parsed; 62 // Try parsing formatted input as a decimal 63 if (decimal.TryParse(inputFormatted, out parsed)) { 64 output = parsed; 65 } 66 } 67 return output; 68 } 69 70 /** 71 * Order product loop by size field 72 * @param {LoopItem} v 73 */ 74 decimal OrderProductsBySize(LoopItem v) { 75 return NumberFromString(v.GetString("Ecom:Product:Field.SIZ")); 76 } 77 78 /** 79 * Order product loop by volume field 80 * @param {LoopItem} v 81 */ 82 decimal OrderProductsByVolume(LoopItem v) { 83 return NumberFromString(v.GetString("Ecom:Product:Field.VOL")); 84 } 85 86 /** 87 * Check wether a product id exists in a list of ids 88 * @param {List<string>} list 89 * @param {String} productId 90 */ 91 bool HasProduct(List<string> list, string productId) { 92 string productIdFormatted = "p_" + productId; 93 return list.Any(x => x == productIdFormatted); 94 } 95 96 /** 97 * Check wether a blog post page has a product selected 98 * in its related products field 99 * @param {Dynamicweb.Content.Page} blogPostAsPage 100 * @param {String} productId 101 */ 102 bool BlogPostHasRelatedProduct(Page blogPostAsPage, string productId) { 103 // Get page item 104 Item blogItem = ItemManager.Storage.GetById("BlogPost", blogPostAsPage.ItemId.ToString()); 105 // Get products from blog post fields 106 List<string> blogProducts = blogItem["Products"].ToString() 107 .Split(',') 108 .ToList(); 109 // Test wether provided product number matches any in the field 110 return HasProduct(blogProducts, productId); 111 } 112 113 bool BlogPostIsRelated(List<LoopItem> currentPageCategories, List<LoopItem> compareCategories) { 114 115 foreach(var i in compareCategories) { 116 if(currentPageCategories.Any(x => (x.GetInteger("ItemPublisher:Item.Categories.Id") == i.GetInteger("ItemPublisher:Item.Categories.Id")))) { 117 return true; 118 } 119 } 120 121 // No match 122 return false; 123 } 124 125 string ConvertPriceToSouthAfricanFormat(string price, int areaId) 126 { 127 if (areaId == 28) 128 { 129 int place = price.LastIndexOf(','); 130 131 if (place == -1) 132 return price; 133 134 string result = price.Remove(place, 1).Insert(place, "."); 135 return result; 136 } 137 return price; 138 } 139 140 } 141 142 @{ 143 string productNumber = GetString("Ecom:Product.Number"); 144 string productColor = GetString("Ecom:Product:Field.COL"); 145 string productSize = GetString("Ecom:Product:Field.SIZ"); 146 string productBottomDiameter = GetString("Ecom:Product:Field.ItemBaseDiameter"); 147 string productVolume = GetString("Ecom:Product:Field.VOL"); 148 string productWeight = GetString("Ecom:Product.Weight"); 149 150 // Getting list of products from product picker 151 List<string> selectedProducts = Pageview.Area.Item["ProductDetailCategories"] 152 .ToString() 153 .Split(',') 154 .ToList(); 155 // Formatting product id to match that of the list items above 156 string productIdFormatted = "p_" + GetString("Ecom:Product.ID"); 157 // Check if current product is in the list 158 bool hasDiameter = selectedProducts.IndexOf(productIdFormatted) == -1; 159 160 Dictionary<string, Dictionary<string, string>> details = new Dictionary<string, Dictionary<string, string>>() { 161 { 162 "weight", new Dictionary<string, string>(){ 163 { "text", "Weight" }, 164 { "description", null }, 165 { "value", productWeight + " kg" }, 166 { "is-empty", string.IsNullOrEmpty(productWeight).ToString() } 167 } 168 }, 169 { 170 "product-number", new Dictionary<string, string>(){ 171 { "text", "Product number" }, 172 { "description", null }, 173 { "value", productNumber }, 174 { "is-empty", "False" } 175 } 176 } 177 }; 178 179 /* Is pot or pan */ 180 if(hasDiameter) { 181 182 /** 183 * Creating new dictionary to merge 184 * This way we can prepend the new values to the existing dictionary 185 */ 186 Dictionary<string, Dictionary<string, string>> addValues = new Dictionary<string, Dictionary<string, string>>() { 187 { 188 "top-diameter", new Dictionary<string, string>(){ 189 { "text", "Top diameter" }, 190 { "description", Pageview.Area.Item["ProductDetailDiameterTopDescription"].ToString() }, 191 { "value", productSize }, 192 { "is-empty", string.IsNullOrEmpty(productSize).ToString() } 193 } 194 }, 195 { 196 /* TBD: This is not currently in eCom */ 197 "bottom-diameter", new Dictionary<string, string>(){ 198 { "text", "Bottom diameter" }, 199 { "description", Pageview.Area.Item["ProductDetailDiameterBottomDescription"].ToString() }, 200 { "value", productBottomDiameter }, 201 { "is-empty", string.IsNullOrEmpty(productBottomDiameter).ToString() } 202 } 203 } 204 }; 205 206 /* Merge dictionaries */ 207 details = addValues.Concat(details).ToDictionary(x=>x.Key,x=>x.Value); 208 209 /* Is not pot or pan */ 210 } else { 211 212 /** 213 * Creating new dictionary to merge 214 * This way we can prepend the new values to the existing dictionary 215 */ 216 Dictionary<string, Dictionary<string, string>> addValues = new Dictionary<string, Dictionary<string, string>>() { 217 { 218 /** 219 * NOTE: 220 * The same field is used for length and top diameter 221 * Pots/pans use diameter, knives use length 222 */ 223 "length", new Dictionary<string, string>(){ 224 { "text", "Length" }, 225 { "description", Pageview.Area.Item["ProductDetailLengthDescription"].ToString() }, 226 { "value", productSize }, 227 { "is-empty", string.IsNullOrEmpty(productSize).ToString() } 228 } 229 }, 230 { 231 "color", new Dictionary<string, string>(){ 232 { "text", "Color" }, 233 { "description", null }, 234 { "value", productColor }, 235 { "is-empty", string.IsNullOrEmpty(productColor).ToString() } 236 } 237 } 238 }; 239 240 /* Merge dictionaries */ 241 details = addValues.Concat(details).ToDictionary(x=>x.Key,x=>x.Value); 242 } 243 } 244 245 246 @SnippetStart("BodyClose") 247 <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5418262f1f54ac79"></script> 248 @SnippetEnd("BodyClose") 249 @{ 250 string productVariantId = GetString("Ecom:Product.VariantID"); 251 252 SetMetaTags(productNumber, productVariantId); 253 254 // Whether eCom features should be hidden in the frontend 255 bool disableEcommerce = Boolean.Parse(Pageview.Area.Item["DisableEcommerce"].ToString()); 256 257 string selectedVariantId = GetString("Ecom:Product.SelectedVariantComboID"); 258 259 bool isMainProduct = string.IsNullOrEmpty(productVariantId) ? true : false; 260 261 string productName = GetString("Ecom:Product.Name"); 262 263 var dimensions = productSize.Split('x'); 264 265 string height = string.Empty; 266 string width = string.Empty; 267 268 if (dimensions.Count() > 1) 269 { 270 height = dimensions[0] + "cm"; 271 width = dimensions[1]; 272 } 273 274 string weight = productWeight + " kg"; 275 276 // Price 277 var productPrice = GetString("Ecom:Product.Price.PriceWithVATFormatted"); 278 279 var productNameAndVariant = GetString("Ecom:Product.Name") + ", " + GetString("Ecom:Product.SelectedVariantComboName"); 280 281 var symbolsLoop = GetLoop("NZNIQSymbols"); 282 283 string productVideo = GetString("Ecom:Product:Field.ProductVideo"); 284 285 // Media product images 286 string mainImage = GetString("Ecom:Product:Field.ProductImage"); 287 288 string imagePath; 289 List<string> imageIds = new List<string>(); 290 291 if (!string.IsNullOrEmpty(mainImage)) 292 { 293 imagePath = "https://cdn.scanpan.dk/Perfion/Image.aspx?id={{imageId}}&amp;size=1600x1200"; 294 imageIds.Add(mainImage); 295 string extraImages = GetString("Ecom:Product:Field.ProductExtraImages"); 296 if (!string.IsNullOrEmpty(extraImages)) 297 { 298 imageIds.AddRange(extraImages.Split(',')); 299 } 300 } 301 else 302 { 303 imagePath = "/Files/Templates/Designs/Scanpan/assets/images/noimage.gif"; 304 } 305 306 var numImages = imageIds.Count; 307 308 // Product guide 309 310 var guideId = GetString("Ecom:Product:Field.guide"); 311 string guidePath = string.Empty; 312 313 if (!string.IsNullOrEmpty(guideId)) 314 { 315 guidePath = string.Format("https://cdn.scanpan.dk/Perfion/File.aspx?id={0}&action=save", guideId); 316 } 317 318 319 // Get variants sorted by size (SIZ field) 320 var variants = GetLoop("VariantCombinations") 321 .OrderBy(v => OrderProductsBySize(v)) 322 .ThenBy(v => OrderProductsByVolume(v)) 323 .ThenBy(v => v.GetString("Ecom:Product:Field.COL")) 324 .ThenBy(v => v.GetDouble("Ecom:Product.Price.Price")) 325 .ToList(); 326 327 // Price 328 var priceWithVat = isMainProduct ? variants.Take(1).First().GetDouble("Ecom:Product.Price.PriceWithVAT") : GetDouble("Ecom:Product.Price.PriceWithVAT"); 329 var price = GetDouble("Ecom:Product.Price.PriceWithVAT"); 330 // Get product price or lowest variant price 331 var priceFormatted = isMainProduct ? variants.Take(1).First().GetString("Ecom:Product.Price") : GetString("Ecom:Product.Price"); 332 var vatPercentage = (GetDouble("Ecom:Product.Price.VATPercent") / 100) + 1; 333 var oldPrice = GetDouble("Ecom:Product:Field.ProductPriceBefore.Value.Raw") * vatPercentage; 334 // See http://developer.dynamicweb-cms.com/api8/#Dynamicweb~Dynamicweb.Ecommerce.Prices.Price~GetDoublePriceFormatted.html 335 string oldPriceFormatted = string.Format("{0:0.00}", oldPrice); 336 var savingsPrice = oldPrice - price; 337 string savingsPriceFormatted = string.Format("{0:0.00}", savingsPrice); 338 //var savingsPriceFormatted = Dynamicweb.Ecommerce.Prices.Price.GetDoublePriceFormatted(savingsPrice, Dynamicweb.Ecommerce.Common.Context.Currency, false); 339 string googlePrice = price.ToString().Replace(",", "."); 340 341 double discountPercentage = Math.Round(100 - (100 / oldPrice * price)); 342 string discountPercentageFormatted = discountPercentage.ToString() + "%"; 343 344 // Add price text to main product 345 var MainProductText = isMainProduct ? "<span class='product__price__text'>From</span>" : null; 346 347 // Determine stock status 348 var stockStatus = GetString("Ecom:Product:Stock.ID"); 349 var stockStatusText = GetString("Ecom:Product:Stock.Text"); 350 var stockDeliveryText = string.Format("{0} {1}", GetString("Ecom:Product:Stock.DeliveryText"), GetString("Ecom:Product:Stock.DeliveryUnit")); 351 bool isOutOfStock = false; 352 string stockClassModifier = string.Empty; 353 string stockItemPropContent = string.Empty; 354 switch (stockStatus) 355 { 356 case "STOCKSTASTUSLINEID1": 357 case "LANGUAGEVALUE2": 358 stockClassModifier = "product__stock--in-stock"; 359 stockItemPropContent = "in_stock"; 360 break; 361 case "STOCKSTASTUSLINEID2": 362 case "LANGUAGEVALUE3": 363 stockClassModifier = "product__stock--limited-stock"; 364 stockItemPropContent = "in_stock"; 365 break; 366 case "STOCKSTASTUSLINEID3": 367 case "LANGUAGEVALUE4": 368 stockClassModifier = "product__stock--sold-out"; 369 stockItemPropContent = "out_of_stock"; 370 isOutOfStock = true; 371 break; 372 } 373 374 @* Only on Denmark ( area id 1 ) *@ 375 var productId = GetString("Ecom:Product.Number"); 376 var pageId = GetString("Ecom:Product.PrimaryOrCurrentPageID"); 377 var groupId = GetString("Ecom:Product.PrimaryOrFirstGroupID"); 378 var path = GetGlobalValue("Global:Pageview.Url").Replace('/', '#').Replace(".aspx", ""); 379 var queryCount = path.Split('#').Count(); 380 string seriesKey = GetString("Ecom:Product:Field.Series"); 381 var series = NoZebra.S_DW_Scanpan.CustomCode.Helpers.ProductsHelper.GetSeries(seriesKey, Pageview.AreaID); 382 bool isOenskeskyenEnabled = Pageview.AreaSettings.GetBoolean("EnableOenskeskyen"); 383 } 384 385 @if (isMainProduct) 386 { 387 // Disable out of stock 388 isOutOfStock = false; 389 } 390 <section class="product" itemscope itemtype="http://schema.org/Product"> 391 <div itemprop="brand" itemscope itemtype="http://schema.org/Brand" content="Scanpan"> 392 <meta itemprop="name" content="Scanpan"> 393 </div> 394 395 @* productNumber found in eCom/partials/product-details.cshtml *@ 396 <meta itemprop="sku" content="@productNumber"> 397 <meta itemprop="gtin8" content="@productNumber"> 398 <meta itemprop="height" content="@height"> 399 <meta itemprop="width" content="@width"> 400 <meta itemprop="weight" content="@weight"> 401 <meta itemprop="description" content="@StripHTML(GetString("Ecom:Product.ShortDescription"))"> 402 403 <div class="product__container"> 404 <div class="product__container-left"> 405 <h1 class="product__name product__name--mobile" itemprop="name">@productName</h1> 406 @if (imageIds.Any() || !string.IsNullOrEmpty(productVideo)) 407 { 408 <div class="product__image royalSlider rsUni js-royal-slider is-loading"> 409 @if (!string.IsNullOrEmpty(productVideo)) 410 { 411 <div class="product__video-slide"> 412 <video class="product__video-element js-product-video" autoplay muted controls playsinline> 413 <source src="https://cdn.scanpan.dk/Perfion/File.aspx?id=@productVideo" type="video/mp4"> 414 Your browser does not support HTML5 video. 415 </video> 416 <span class="product__video-play js-product-video-play"></span> 417 <img width="85" class="rsTmb" src="/Files/Templates/Designs/Scanpan/assets/images/play-thumbnail.png" alt="@productNameAndVariant"> 418 </div> 419 } 420 @foreach (string imageId in imageIds) 421 { 422 string imageSrc = imagePath.Replace("{{imageId}}", imageId); 423 <a class="rsImg" data-rsw="1600" data-rsh="1200" data-rsBigImg="@imageSrc?quality=75" href="@imageSrc.Replace("size=1600x1200", "size=588x441")"> 424 <img itemprop="image" width="85" class="rsTmb" src="@imageSrc.Replace("size=1600x1200", "size=85x65")" alt="@productNameAndVariant"> 425 </a> 426 } 427 </div> 428 } 429 @* Symbol splash *@ 430 @if (symbolsLoop.FirstOrDefault(item => item.GetInteger("NZPriority") == 1) != null) 431 { 432 var splash = symbolsLoop.FirstOrDefault(item => item.GetInteger("NZPriority") == 1).GetString("NZLink"); 433 <img class="splat" src="@splash?width=160&amp;format=png&amp;quality=80"> 434 } 435 </div> 436 <div class="product__container-right"> 437 <h1 class="product__name product__name--desktop" itemprop="name">@productName</h1> 438 439 <div class="product__info"> 440 <div class="product__shop-info"> 441 <form class="product__form js-product-form" autocomplete="off"> 442 @* For adding this product to the cart by submitting the wrapping form *@ 443 <input type="hidden" name="CartCmd" id="CartCmd" value="add"> 444 @GetString("Ecom:Product.Form.Multi.HiddenFields") 445 <div class="product__form-field"> 446 @foreach (LoopItem v in variants) 447 { 448 var variantId = v.GetString("Ecom:VariantCombination.VariantID"); 449 if (isMainProduct) 450 { 451 variantId = productVariantId; 452 } 453 454 // Build custom variant text due to some variants missing variant text 455 List<string> variantTexts = new List<string>() 456 { 457 v.GetString("Ecom:Product:Field.SIZ"), 458 v.GetString("Ecom:Product:Field.VOL"), 459 v.GetString("Ecom:Product:Field.COL") 460 }; 461 462 var variantText = string.Join(", ", variantTexts.Where(item => !string.IsNullOrEmpty(item))); 463 464 if (String.IsNullOrWhiteSpace(variantText)) 465 { 466 continue; 467 } 468 469 bool isChecked = (variantId == selectedVariantId); 470 string isCheckedText = isChecked ? "checked" : null; 471 string isActiveText = isChecked ? "active" : null; 472 473 <div class="product__form-radio-container radio-container js-radio-container @isActiveText"> 474 <input type="radio" checked="@isCheckedText" id="@variantText" class="product__form-radio js-product-variant-radio" data-variantlink="@v.GetString("Ecom:VariantCombination.Link.Clean")" name="variant" value="@variantId"> 475 <label class="product__form-radio-label" for="@variantText"><span class="product__form-radio-label--text">@variantText.Replace("cm,", "cm<br>")</span></label> 476 </div> 477 } 478 </div> 479 <div class="product__usp"> 480 <ul class="product__usp-list"> 481 <li class="product__usp-item">Original SCANPAN</li> 482 <li class="product__usp-item">Worldclass cookware</li> 483 <li class="product__usp-item">Danish heritage</li> 484 </ul> 485 </div><!-- .product__usps --> 486 @if (!disableEcommerce) 487 { 488 <div class="product__offer" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> 489 <link itemprop="url" href="@GetGlobalValue("Global:Pageview.Url")" /> 490 <meta itemprop="priceCurrency" content="@GetString("Ecom:Product.Currency.Code")"> 491 <meta itemprop="price" content="@googlePrice" /> 492 493 <meta itemprop="availability" content="@(isOutOfStock ? "https://schema.org/OutOfStock" : "https://schema.org/InStock")" /> 494 495 <div class="product__price"> 496 @if (oldPrice > 0) 497 { 498 <div class="product__price__current">@MainProductText@priceFormatted</div> 499 <span class="product__price__old">@oldPriceFormatted</span> 500 <span class="product__price__savings">Save @savingsPriceFormatted</span> 501 502 } 503 else 504 { 505 <div class="product__price__current">@MainProductText@priceFormatted</div> 506 } 507 </div> 508 @* Alin *@ 509 <div> 510 @* Alin -> TODO: Add translation and link to the Shipping terms page @Translate(InklMwStZzgl, "inkl. MwSt. Zzgl.", global) Translate(VersandKosten, "Versandkosten", global) *@ 511 @{ 512 var ShippingInfoPage = Pageview.Area.Item["ShippingInfoPage"]; 513 if (ShippingInfoPage != null && !string.IsNullOrEmpty(ShippingInfoPage.ToString())) 514 { 515 <p class="product-list__extra-text" style="font-size: 0.8em"> <a href="@ShippingInfoPage"> Shipping costs</a></p> 516 } 517 } 518 </div> 519 <div class="product__stock-wrapper"> 520 <span class="product__stock @stockClassModifier">@stockStatusText </span> 521 </div> 522 <div class="product__add-to-cart-container"> 523 @*<input class="product__quantity js-quantity-input" type="number" name="Quantity" value="1" />*@ 524 <input type="hidden" name="Quantity" value="1" /> 525 <button class="js-ga-four-add-to-cart product__form-submit product__add-to-cart js-fbq-add-to-cart @(isMainProduct ? "js-add-to-cart" : null)" type="submit" disabled="@isOutOfStock">Buy@(discountPercentage > 0 ? " (-" + discountPercentageFormatted + ")" : "")</button> 526 </div> 527 <style> 528 </style> 529 @if (!isOutOfStock) 530 { 531 <div class="product__availability"> 532 @* Render global message inside product info *@ 533 @{ 534 string theme = GetString("Item.Area.GlobalMessageTheme"); 535 536 int campaignDuration = GetInteger("Item.Area.GlobalMessageCampaignDuration") > 0 ? GetInteger("Item.Area.GlobalMessageCampaignDuration") : 30; 537 538 // Setting default global message 539 540 string name = "defaultMessage"; 541 542 int hour = int.Parse(Translate("product-countdown-hour", "13")); 543 544 string message = Translate("product-countdown-default", "Modtag i morgen,"); 545 546 DateTime current = DateTime.Now; 547 548 DateTime endDate = DateTime.Now.Date.Add(new TimeSpan(hour, 0, 0)); 549 550 bool hideCountDown = current.DayOfWeek == DayOfWeek.Friday && current.TimeOfDay.TotalSeconds >= 60 * 60 * hour 551 || current.DayOfWeek == DayOfWeek.Saturday 552 || current.DayOfWeek == DayOfWeek.Sunday && current.TimeOfDay.TotalSeconds <= 60 * 60 * hour; 553 554 if (hideCountDown) 555 { 556 message = Translate("product-countdown-weekend", "Vi sender din ordre på mandag kl. 13"); 557 } 558 559 if (current.TimeOfDay.TotalSeconds > 60 * 60 * hour) 560 { 561 endDate = endDate.AddDays(1); // Hour is passed 13. Adding one day to endDate 562 } 563 564 bool hideDays = false; 565 566 // Overruling default values when activated from website settings 567 568 DateTime campaignEndDate = GetDate("Item.Area.CampaignEndDate"); 569 570 bool globalMessageIsActive = GetBoolean("Item.Area.GlobalMessageIsActive"); 571 572 if (globalMessageIsActive && campaignEndDate >= DateTime.Now) 573 { 574 message = GetString("Item.Area.GlobalMessageText"); 575 endDate = campaignEndDate; 576 hideCountDown = GetBoolean("Item.Area.HideCountDown"); 577 if (!string.IsNullOrEmpty(GetString("Item.Area.GlobalMessageCampaignName"))) 578 { 579 name = GetString("Item.Area.GlobalMessageCampaignName"); 580 } 581 } 582 else 583 { 584 hideDays = true; 585 } 586 587 // Do not render section if cookie set (message dismissed) and area id not equals 1 or 30 or 34 588 if (Dynamicweb.Frontend.PageView.Current().Area.ID == 1 || Dynamicweb.Frontend.PageView.Current().Area.ID == 30 || Dynamicweb.Frontend.PageView.Current().Area.ID == 34) 589 { 590 <!--googleoff: all--> 591 <div class="product__timer js-count-down-active-state" data-id="@name" data-expires="@campaignDuration"> 592 <div class="product__timer-content"> 593 <div class="product__timer-text">@message</div> 594 @if (!hideCountDown) 595 { 596 <div class="product__timer-counter js-count-down" data-end-date="@(endDate.ToString("dd-MM-yyyy HH:mm:ss"))"> 597 <span>@Translate("product-countdown-text", "bestil inden")</span> 598 @if (!hideDays) 599 { 600 <span class="js-count-down-days"></span><span>@Translate("product-countdown-days", "d:")</span> 601 } 602 <span class="js-count-down-hours"></span><span>@Translate("product-countdown-hours", "t:")</span> 603 <span class="js-count-down-minutes"></span><span>@Translate("product-countdown-minutes", "m:")</span> 604 <span class="js-count-down-seconds"></span><span>@Translate("product-countdown-seconds", "s")</span> 605 </div> 606 } 607 </div> 608 </div> <!-- .global-message--@(theme) --> 609 <!--googleon: all--> 610 } 611 } 612 613 </div> 614 } 615 616 @{ 617 if (isOenskeskyenEnabled) 618 { 619 <button style="margin-top:16px" id="ov-onskeskyen-generated-wish-button" class="pill" type="button">@Translate("Add to Oenskeskyen")</button> 620 621 <script type="application/javascript" src="https://xn--nskeskyen-k8a.dk/onskeskyen-wish-button/external-wish-button.js" id="ov-onskeskyen-generate-wish-button-script"></script> 622 } 623 } 624 </div> 625 } 626 </form> 627 @if (series != null) 628 { 629 <div class="product__series-text"> 630 <h3 style="font-style: normal; font-family: Open Sans, sans-serif"> 631 <a href="@series.Link" style="text-decoration: underline;">@Translate("The {{SERIES NAME}} series").Replace("{{SERIES NAME}}", series.Name) </a> 632 </h3> 633 @series.ValuePropositions 634 </div> 635 } 636 </div><!-- .product__shop-info --> 637 </div><!-- .product__info --> 638 </div> 639 <div class="product__container-left"> 640 <div class="product__share-container"> 641 <h2 class="product__subheader">Share this product</h2> 642 <div class="product__share addthis_sharing_toolbox"></div> 643 </div><!-- .product__share-container --> 644 <div class="js-accordion"> 645 <div class="product__tabs-container"> 646 <button class="product__tab-button active js-accordion-button" rel="product-tabs" data-target="#accordion-target-id-1">About the product</button> 647 648 @if (GetLoop("ProductCategories").Any(item => item.GetLoop("ProductCategoryFields").Where(subitem => subitem.GetString("Ecom:Product.CategoryField.CategoryID") == "specsList").Any())) 649 { 650 <button class="product__tab-button js-accordion-button" rel="product-tabs" href="#accordion-target-id-2">Specifications</button> 651 } 652 @if (!string.IsNullOrEmpty(GetString("Ecom:Product:Field.guide"))) 653 { 654 <button class="product__tab-button js-accordion-button" rel="product-tabs" href="#accordion-target-id-3">Guides</button> 655 } 656 @if (!string.IsNullOrEmpty(GetString("Ecom:Product:Field.theSeries"))) 657 { 658 <button class="product__tab-button js-accordion-button" rel="product-tabs" href="#accordion-target-id-4">About the series</button> 659 } 660 </div> 661 <div class="active product__tab-content" id="accordion-target-id-1"> 662 <div class="product__text">@GetString("Ecom:Product.ShortDescription")</div> 663 <div class="product__features"> 664 @if (symbolsLoop.Where(symbol => symbol.GetInteger("NZPriority") == 0 || symbol.GetString("NZName") == "Not_induction_compatible" || symbol.GetString("NZName") == "Ikke_til_induktion").Any()) 665 { 666 <h2 class="product__subheader">Features</h2> 667 <ul class="feature-list"> 668 669 @foreach (var symbol in symbolsLoop.Where(symbol => symbol.GetString("NZName") == "Not_induction_compatible" || symbol.GetString("NZName") == "Ikke_til_induktion")) 670 { 671 <li class="feature-list__item"> 672 <img class="feature-list__image" width="25" height="25" src="@symbol.GetString("NZLink")?height=25&amp;width=25&amp;quality=80"> 673 <span class="feature-list__item-text">@Translate(symbol.GetString("NZName"))</span> 674 </li> 675 } 676 @foreach (var symbol in symbolsLoop.Where(symbol => symbol.GetInteger("NZPriority") == 0).Where(symbol => symbol.GetString("NZName") != "Not_induction_compatible" && symbol.GetString("NZName") != "Ikke_til_induktion")) 677 { 678 <li class="feature-list__item"> 679 <img class="feature-list__image" width="25" height="25" src="@symbol.GetString("NZLink")?height=25&amp;width=25&amp;quality=80"> 680 <span class="feature-list__item-text">@Translate(symbol.GetString("NZName"))</span> 681 </li> 682 } 683 </ul> 684 } 685 </div> 686 </div> 687 <!-- This is an accordion always --> 688 @if (GetLoop("ProductCategories").Any(item => item.GetLoop("ProductCategoryFields").Where(subitem => subitem.GetString("Ecom:Product.CategoryField.CategoryID") == "specsList").Any())) 689 { 690 <div class="product__tab-content" id="accordion-target-id-2"> 691 <dl class="product__specs-list"> 692 <div class="product__specs-item"> 693 <dt class="product__specs-key">Item number</dt> 694 <dd class="product__specs-value">@GetString("Ecom:Product.Number")</dd> 695 </div> 696 @foreach (var item in GetLoop("ProductCategories").SelectMany(item => item.GetLoop("ProductCategoryFields").Where(subitem => subitem.GetString("Ecom:Product.CategoryField.CategoryID") == "specsList" && !string.IsNullOrEmpty(subitem.GetString("Ecom:Product.CategoryField.Value"))))) 697 { 698 string specsKey = item.GetString("Ecom:Product.CategoryField.Label"); 699 string toolTipText = string.Concat("ToolTip", specsKey); 700 string toolTipId = item.GetString("Ecom:Product.CategoryField.ID"); 701 702 if (item.GetString("Ecom:Product.CategoryField.ID") == "specsWithLid" && (item.GetString("Ecom:Product.CategoryField.Value").ToLower() == "ikke relevant" || item.GetString("Ecom:Product.CategoryField.Value").ToLower() == "not relevant")) 703 { 704 continue; 705 } 706 <div class="product__specs-item"> 707 <dt class="product__specs-key"> 708 @Translate(specsKey) 709 @if (Translate(toolTipText) != toolTipText) 710 { 711 <span class="product__specs-tooltip-icon js-toggle-class" data-target="#@toolTipId"> 712 <span id="@toolTipId" class="product__specs-tooltip js-tooltip">@Translate(toolTipText)</span> 713 </span> 714 } 715 </dt> 716 <dd class="product__specs-value">@Translate(item.GetString("Ecom:Product.CategoryField.Value"))</dd> 717 </div> 718 } 719 </dl> 720 </div> 721 } 722 @if (!string.IsNullOrEmpty(guidePath)) 723 { 724 <div class="product__tab-content" id="accordion-target-id-3"> 725 <div class="product__text"> 726 <a href="@guidePath"> 727 Download user guide 728 </a> 729 </div> 730 </div> 731 732 } 733 @if (!string.IsNullOrEmpty(GetString("Ecom:Product:Field.theSeries"))) 734 { 735 <div class="product__tab-content" id="accordion-target-id-4"> 736 <div class="product__text">@GetString("Ecom:Product:Field.theSeries")</div> 737 </div> 738 } 739 </div> 740 </div> 741 742 </div><!-- .product__container --> 743 @*GA4 tracking*@ 744 745 <div style="display:none" class="js-ga-four-product" 746 data-itemprice="@GetDouble("Ecom:Product.Price.PriceWithVAT").ToString().Replace(",", ".")" 747 data-itemvat="@GetDouble("Ecom:Product.Price.VATPercent").ToString().Replace(",", ".")" 748 data-itempriceold="@GetDouble("Ecom:Product:Field.ProductPriceBefore.Value.Raw").ToString().Replace(",", ".")" 749 data-item_name="@GetString("Ecom:Product.Name")" 750 data-item_id="@GetString("Ecom:Product.Number")" 751 data-currency="@GetString("Ecom:Product.Currency.Code")"> 752 </div> 753 754 755 756 </section><!-- .product --> 757 @functions 758 { 759 struct Language 760 { 761 public string productId; 762 public string productVariantId; 763 public string productLanguage; 764 } 765 766 767 public static string StripHTML(string htmlString) 768 { 769 770 string pattern = @"<(.|\n)*?>"; 771 772 return Regex.Replace(htmlString, pattern, string.Empty); 773 } 774 775 776 private void SetMetaTags(string productNumber, string productVariantId) 777 { 778 var relatedLanguages = new List<Language>(); 779 780 //Create a DataReader on the default database 781 using (var myDr = Database.CreateDataReader($"SELECT ProductLanguageID, ProductID, ProductVariantID FROM EcomProducts WHERE ProductNumber='{productNumber}' AND ProductVariantID = '{productVariantId}'")) 782 { 783 while (myDr.Read()) 784 { 785 relatedLanguages.Add(new Language() 786 { 787 productId = Input.FormatString(myDr["ProductID"]), 788 productLanguage = Input.FormatString(myDr["ProductLanguageID"]), 789 productVariantId = Input.FormatString(myDr["ProductVariantID"]), 790 }); 791 } 792 } 793 794 string[] productPagesAndLanguages = ApplicationSettings.Instance.ProductPagesAndLanguageCode?.Split(';'); 795 796 if (productPagesAndLanguages == null) return; 797 798 foreach (var rl in relatedLanguages) 799 { 800 var languageService = new Dynamicweb.Ecommerce.International.LanguageService(); 801 string languageCode = languageService.GetLanguage(rl.productLanguage).Code2; 802 string page = String.Empty; 803 string culture = String.Empty; 804 var areaService = new Dynamicweb.Content.AreaService(); 805 Dynamicweb.Content.Area dkArea = areaService.GetArea(1); 806 807 string[] productPageAndLanguage = productPagesAndLanguages.FirstOrDefault(p => p.StartsWith($"{languageCode}|"))?.Split('|'); 808 809 if (productPageAndLanguage == null) 810 { 811 continue; 812 } 813 814 page = productPageAndLanguage[1]; 815 culture = productPageAndLanguage[2]; 816 817 string url = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={page}&ProductID={rl.productId}&VariantID={rl.productVariantId}"); 818 string urlLeftPart = System.Web.HttpContext.Current.Request.Url.GetLeftPart(System.UriPartial.Authority); 819 System.Web.HttpContext.Current.Items["MetaTags"] += $"<link rel=\"alternate\" href=\"{urlLeftPart}{url}\" hreflang=\"{culture}\" />"; 820 } 821 string mainImage = GetString("Ecom:Product:Field.ProductImage"); 822 System.Web.HttpContext.Current.Items["MetaTags"] += $"<meta property = og:image content = https://cdn.scanpan.dk/Perfion/Image.aspx?id={mainImage}&amp;size=600x315&fit=smart />"; 823 } 824 } 825 @SnippetStart("Tracking") 826 <script type="text/javascript"> 827 // FB view tracking 828 fbq('track', 'ViewContent', { 829 content_name: '@System.Web.HttpUtility.JavaScriptStringEncode(productName)', 830 content_category: '', 831 content_ids: ['@productNumber'], 832 content_type: 'product', 833 value: '@GetString("Ecom:Product.Price.Price")', 834 currency: '@GetString("Ecom:Product.Currency.Code")' 835 }); 836 // Click FB event 837 $('.js-fbq-add-to-cart').click(function () { 838 fbq('track', 'AddToCart', { 839 content_name: '@System.Web.HttpUtility.JavaScriptStringEncode(productName)', 840 content_category: '', 841 content_ids: ['@productNumber'], 842 content_type: 'product', 843 value: '@GetString("Ecom:Product.Price.Price")', 844 currency: '@GetString("Ecom:Product.Currency.Code")' 845 }); 846 }); 847 </script> 848 <script type="text/javascript"> 849 @{ 850 int areaId = Pageview.AreaID; 851 string priceTotal = GetString("Ecom:Product.Price.Price"); 852 853 string totalValue = areaId == 28 ? 854 System.Text.RegularExpressions.Regex.Replace(priceTotal, @"\s+", "").Replace(",", ".") : 855 priceTotal.Replace(",", "."); 856 } 857 858 var google_tag_params = { 859 ecomm_prodid: @productNumber, 860 ecomm_pagetype: 'product', 861 ecomm_totalvalue: '@totalValue' 862 }; 863 </script> 864 <script type="text/javascript"> 865 /* <![CDATA[ */ 866 var google_conversion_id = 976264288; 867 var google_custom_params = window.google_tag_params; 868 var google_remarketing_only = true; 869 /* ]]> */ 870 </script> 871 <script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js"> 872 </script> 873 <noscript> 874 <div style="display:inline;"> 875 <img height="1" width="1" style="border-style:none;" alt="" src="//googleads.g.doubleclick.net/pagead/viewthroughconversion/976264288/?guid=ON&amp;script=0" /> 876 </div> 877 </noscript> 878 @SnippetEnd("Tracking") 879 880 881 <script> 882 window.addEventListener("load", function (event) { 883 window.NzApp.GA4.trackAddToCart(".product") 884 window.NzApp.GA4.trackViewItem(".product") 885 }) 886 </script>
Scanpan
English Dansk Norsk Deutsch Nederlands
Pans
  • Inspiration
    • A pan for every purpose
    • Induction cookware
    • Non-stick pans
  • Category
    • Fry pans
    • Roasting pans
    • Grill pans
    • Sauté pans
    • Wok
    • Special purpose pans
  • Series
    • NEW! Pro SB+
    • NEW! Urban
    • TechnIQ
    • CTX
    • Black Iron
    • Classic
    • See all ranges
  • NEW!
Pots
  • Category
    • Pots
    • Saucepans
    • Special purpose pots
    • Accessories for pots
  • Series
    • NEW! Pro SB+
    • STS
    • CTX
    • Impact
    • Classic Induction
    • Fusion 5
    • See all ranges
  • Great for baking
Knives
  • Category
    • Knives for meat
    • Knives for veggies
    • Knives for bread
    • Knife sets
    • Knife sharpening
    • Steak cutlery
    • Accessories
  • Series
    • Classic
    • Spectrum
  • Santoku Knife
Accessories
  • Category
    • Kitchen tools
    • Cutting boards
    • Lids
    • Racks and inserts
    • Maintenance
  • Kitchen tools
TO GO
  • Category
    • Vacuum bottle
    • NEW! Explore vacuum bottle
    • Travel mug
    • Cup
    • French press coffee maker
    • Serving bottle
    • Tumbler
    • Serving bottle gift set
  • READY, SET...GO!
Series
  • Overview
    • See all ranges
    • NEW! Pro SB+
    • NEW! Urban
  • Our universes
    • Crafted by Chefs - Cookware. Reinvented.
    • Classic - a universe of highest performance cookware
    • Maitre D' - a universe of luxury and aesthetics
  • Danish since 1956
SCANPAN Guide

Copyright © SCANPAN Privacy Policy

For the love of good food…

Follow on

Facebook Instagram
About SCANPAN
  • SCANPAN - the company
  • Contact SCANPAN
Customer Service
  • Find distributor
  • Media bank
  • Cookies and Personal Data Policy
  • Distributor/retailer (login)
  • Warranty
Product Information
  • Use & Care
  • SCANPAN's coating
  • Frying and cleaning tips
Contact us (Denmark)
+45 87 74 14 00

Monday - Thursday 09.00-1600

Friday 09.00-15.30