Komponent editor til: CustomProductStickyEcom

Inkl. moms

Du vil blive kontaktet, når produktet er tilbage på lager.

Product Specification will be shown here if any
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGalleryForIcons_Custom.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
   at CompiledRazorTemplates.Dynamic.RazorEngine_0d52f8c8571a4224b71e9a1cf6425f25.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\T3L.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGalleryForIcons_Custom.cshtml:line 124
   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 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Frontend 4 @using System.IO 5 @using System.Text.RegularExpressions; 6 7 @functions { 8 public ProductViewModel product { get; set; } = new ProductViewModel(); 9 public string galleryLayout { get; set; } 10 public string[] supportedImageFormats { get; set; } 11 public string[] supportedVideoFormats { get; set; } 12 public string[] supportedDocumentFormats { get; set; } 13 public string[] allSupportedFormats { get; set; } 14 15 public class RatioSettings 16 { 17 public string Ratio { get; set; } 18 public string CssClass { get; set; } 19 public string CssVariable { get; set; } 20 public string Fill { get; set; } 21 } 22 23 public RatioSettings GetRatioSettings(string size = "desktop") 24 { 25 var ratioSettings = new RatioSettings(); 26 27 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 28 ratio = ratio != "0" ? ratio : ""; 29 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 30 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 31 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 32 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 33 34 ratioSettings.Ratio = ratio; 35 ratioSettings.CssClass = cssClass; 36 ratioSettings.CssVariable = cssVariable; 37 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 38 39 return ratioSettings; 40 } 41 42 public string GetColumnClass(int total, int assetNumber) 43 { 44 string colClass = Model.Item.GetRawValueString("ItemsPerRow", "g-col-2"); 45 46 return colClass; 47 } 48 49 public string GetArrowsColor() 50 { 51 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 52 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 53 return arrowsColor; 54 } 55 56 public Dictionary<string, object> GetVideoParams(MediaViewModel asset, string size) 57 { 58 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 59 string type = GetVideoType(asset.Value); 60 bool openInModal = Model.Item.GetString("OpenVideoInModal") == "true" ? true : false; 61 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 62 63 var videoParams = new Dictionary<string, object>(); 64 videoParams.Add("AssetName", asset.Name); 65 videoParams.Add("AssetVideoType", type); 66 videoParams.Add("AssetDisplayName", asset.DisplayName); 67 videoParams.Add("OpenVideoInModal", openInModal); 68 videoParams.Add("VideoAutoPlay", autoPlay); 69 videoParams.Add("Size", size); 70 videoParams.Add("Id", Model.ID); 71 return videoParams; 72 } 73 74 public string GetVideoType(string assetValue) 75 { 76 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 77 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 78 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 79 return type; 80 } 81 82 public string GetYoutubeScreenDump(string assetValue, string quality) 83 { 84 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 85 Match match = regex.Match(assetValue); 86 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 87 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 88 return youtubeThumbnail; 89 } 90 } 91 92 @{ 93 @* Get the product data *@ 94 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 95 { 96 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 97 } 98 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 99 { 100 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 101 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 102 103 if (productList?.Products is object) 104 { 105 product = productList.Products[0]; 106 } 107 } 108 } 109 110 @if (product is object) 111 { 112 @* Supported formats *@ 113 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 114 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 115 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 116 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 117 118 @* Collect the assets *@ 119 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 120 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 121 122 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 123 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 124 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 125 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 126 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 127 assetsList = assetsList.Union(assetsImages); 128 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 129 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 130 131 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 132 133 int totalAssets = 0; 134 foreach (MediaViewModel asset in assetsList) 135 { 136 var assetValue = asset.Value; 137 foreach (string format in allSupportedFormats) 138 { 139 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 140 { 141 totalAssets++; 142 } 143 } 144 } 145 146 if (totalAssets == 0) 147 { 148 if (defaultImageFallback) 149 { 150 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 151 totalAssets = 1; 152 } 153 else 154 { 155 assetsList = new List<MediaViewModel>() { }; 156 totalAssets = 0; 157 } 158 } 159 160 @* Layout settings *@ 161 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 162 spacing = spacing == "none" ? "gap-0" : spacing; 163 spacing = spacing == "small" ? "gap-3" : spacing; 164 spacing = spacing == "large" ? "gap-4" : spacing; 165 166 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 167 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 168 169 var badgeParms = new Dictionary<string, object>(); 170 badgeParms.Add("size", "h5"); 171 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 172 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 173 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 174 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 175 badgeParms.Add("campaignBadgesValues", Model.Item.GetList("CampaignBadges")?.GetRawValue().OfType<string>().ToList()); 176 177 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 178 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 179 DateTime createdDate = product.Created.Value; 180 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 181 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 182 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 183 184 185 @* Get assets from selected categories or get all assets *@ 186 if (totalAssets != 0 && assetsList.Count() != 0) 187 { 188 int desktopAssetNumber = 0; 189 int mobileAssetNumber = 0; 190 int mobileAssetThumbnailNumber = 0; 191 int modalAssetNumber = 0; 192 193 @* Desktop: Show the gallery on large screens *@ 194 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 195 <div class="grid @spacing"> 196 @foreach (MediaViewModel asset in assetsList) 197 { 198 var assetName = asset.Value; 199 foreach (string format in allSupportedFormats) 200 { 201 if (assetName.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 202 { 203 string size = "desktop"; 204 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 205 string assetValue = asset.Value; 206 207 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 208 <div class="h-100 @(imageTheme)"> 209 @foreach (string imageFormat in supportedImageFormats) 210 { //Images 211 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 212 { 213 string productName = product.Name; 214 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 215 string imageLinkPath = !string.IsNullOrEmpty(imagePath) ? $"/Admin/Public/GetImage.ashx?image={Uri.EscapeDataString(imagePath)}&width=1920&format=webp" : string.Empty; ; 216 217 RatioSettings ratioSettings = GetRatioSettings(size); 218 219 var parms = new Dictionary<string, object>(); 220 parms.Add("alt", productName); 221 parms.Add("itemprop", "image"); 222 if (totalAssets > 1) 223 { 224 parms.Add("columns", 2); 225 } 226 else 227 { 228 parms.Add("columns", 1); 229 } 230 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 231 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 232 233 if (!string.IsNullOrEmpty(asset.DisplayName)) 234 { 235 parms.Add("title", asset.DisplayName); 236 } 237 238 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 239 { 240 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 241 } 242 else 243 { 244 parms.Add("cssClass", "mw-100 mh-100"); 245 } 246 247 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 248 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 249 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 250 </div> 251 </a> 252 } 253 } 254 @foreach (string videoFormat in supportedVideoFormats) 255 { //Videos 256 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 257 { 258 if (Model.Item.GetString("OpenVideoInModal") == "true") 259 { 260 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 261 262 string type = GetVideoType(asset.Value); 263 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 264 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 265 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 266 267 string productName = product.Name; 268 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 269 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 270 271 RatioSettings ratioSettings = GetRatioSettings(size); 272 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 273 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 274 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 275 @if (type != "selfhosted") 276 { 277 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 278 } 279 else 280 { 281 string videoType = Path.GetExtension(asset.Value).ToLower(); 282 283 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 284 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 285 </video> 286 } 287 </div> 288 </div> 289 290 } 291 else 292 { 293 var videoParams = GetVideoParams(asset, size); 294 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 295 } 296 } 297 } 298 @foreach (string documentFormat in supportedDocumentFormats) 299 { //Documents 300 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 301 { 302 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 303 304 string productName = product.Name; 305 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 306 string imageLinkPath = Uri.EscapeDataString(imagePath); 307 308 RatioSettings ratioSettings = GetRatioSettings(size); 309 310 var parms = new Dictionary<string, object>(); 311 parms.Add("alt", productName); 312 parms.Add("itemprop", "image"); 313 parms.Add("fullwidth", true); 314 parms.Add("columns", Model.GridRowColumnCount); 315 if (!string.IsNullOrEmpty(asset.DisplayName)) 316 { 317 parms.Add("title", asset.DisplayName); 318 } 319 320 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 321 { 322 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 323 } 324 else 325 { 326 parms.Add("cssClass", "mw-100 mh-100"); 327 } 328 329 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 330 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 331 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 332 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 333 { 334 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 335 } 336 else 337 { 338 339 } 340 </div> 341 </a> 342 } 343 } 344 </div> 345 </div> 346 desktopAssetNumber++; 347 } 348 } 349 } 350 </div> 351 352 @if (showBadges) 353 { 354 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 355 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 356 </div> 357 } 358 </div> 359 360 @* Mobile: Show the thumbs on small screens *@ 361 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 362 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 363 <div class="carousel-inner h-100"> 364 @foreach (MediaViewModel asset in assetsList) 365 { 366 var assetValue = asset.Value; 367 foreach (string format in allSupportedFormats) 368 { 369 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 370 { 371 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 372 string size = "mobile"; 373 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 374 375 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 376 <div class="h-100 @(imageTheme)"> 377 @foreach (string imageFormat in supportedImageFormats) 378 { //Images 379 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 380 { 381 string productName = product.Name; 382 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 383 string imageLinkPath = !string.IsNullOrEmpty(imagePath) ? $"/Admin/Public/GetImage.ashx?image={Uri.EscapeDataString(imagePath)}&width=1920&format=webp" : string.Empty; 384 385 RatioSettings ratioSettings = GetRatioSettings(size); 386 387 var parms = new Dictionary<string, object>(); 388 parms.Add("alt", productName); 389 parms.Add("itemprop", "image"); 390 if (totalAssets > 1) 391 { 392 parms.Add("columns", 2); 393 } 394 else 395 { 396 parms.Add("columns", 1); 397 } 398 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 399 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 400 401 if (!string.IsNullOrEmpty(asset.DisplayName)) 402 { 403 parms.Add("title", asset.DisplayName); 404 } 405 406 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 407 { 408 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 409 } 410 else 411 { 412 parms.Add("cssClass", "mw-100 mh-100"); 413 } 414 415 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 416 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@mobileAssetNumber"> 417 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 418 </div> 419 </a> 420 } 421 } 422 @foreach (string videoFormat in supportedVideoFormats) 423 { //Videos 424 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 425 { 426 if (Model.Item.GetString("OpenVideoInModal") == "true") 427 { 428 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 429 430 string type = GetVideoType(asset.Value); 431 432 string videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : string.Empty; 433 videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : videoScreendumpPath; 434 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 435 436 string productName = product.Name; 437 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 438 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 439 440 RatioSettings ratioSettings = GetRatioSettings(size); 441 442 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 443 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@desktopAssetNumber"> 444 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 445 @if (type != "selfhosted") 446 { 447 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;"> 448 } 449 else 450 { 451 string videoType = Path.GetExtension(asset.Value).ToLower(); 452 453 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 454 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 455 </video> 456 } 457 </div> 458 </div> 459 } 460 else 461 { 462 Dictionary<string, object> videoParams = GetVideoParams(asset, size); 463 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 464 } 465 } 466 } 467 </div> 468 </div> 469 mobileAssetNumber++; 470 } 471 } 472 } 473 </div> 474 </div> 475 476 @if (totalAssets > 1) 477 { 478 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 479 @foreach (MediaViewModel asset in assetsList) 480 { 481 var assetValue = asset.Value; 482 foreach (string format in allSupportedFormats) 483 { 484 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 485 { 486 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 487 string type = GetVideoType(asset.Value); 488 489 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 490 videoScreendumpPath = type == "selfhosted" ? System.Uri.EscapeUriString(asset.Value) : videoScreendumpPath; 491 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 492 493 string productName = product.Name; 494 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 495 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 496 497 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 498 @foreach (string imageFormat in supportedImageFormats) 499 { //Images 500 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 501 { 502 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 503 <img src="@imagePath" class="p-1 mw-100 mh-100" style="object-fit: cover;" alt="@productName" @assetTitle> 504 } 505 } 506 @foreach (string videoFormat in supportedVideoFormats) 507 { //Videos 508 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 509 { 510 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 511 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 512 </div> 513 if (type != "selfhosted") 514 { 515 516 <img src="@(videoScreendumpPath)" class="p-1 @videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" alt="@productName" @assetTitle> 517 518 } 519 else 520 { 521 string videoType = Path.GetExtension(asset.Value).ToLower(); 522 523 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 524 <source src="@(videoScreendumpPath)#t=0.001" type="video/@videoType.Replace(".", "")"> 525 </video> 526 } 527 } 528 } 529 @foreach (string documentFormat in supportedDocumentFormats) 530 { //Documents 531 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 532 { 533 string imagePath = !string.IsNullOrEmpty(asset.Value) ? $"/Admin/Public/GetImage.ashx?image={asset.Value}&width=180&format=webp" : string.Empty; 534 535 <a href="@Uri.EscapeDataString(assetValue)" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 536 @if (asset.Value.IndexOf(".pdf", StringComparison.OrdinalIgnoreCase) >= 0) 537 { 538 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 539 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 540 </div> 541 <img src="@imagePath" alt="@productName" @assetTitle class="p-0 p-lg-1 mw-100 mh-100" style="object-fit: cover;"> 542 } 543 else 544 { 545 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 546 <div class="icon-3 position-absolute" style="z-index: 1">@ReadFile(iconPath + "file-text.svg")</div> 547 </div> 548 } 549 </a> 550 } 551 } 552 </div> 553 554 mobileAssetThumbnailNumber++; 555 } 556 } 557 } 558 </div> 559 } 560 561 @if (showBadges) 562 { 563 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 564 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 565 </div> 566 } 567 </div> 568 569 @* Modal with slides *@ 570 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 571 <div class="modal-dialog modal-dialog-centered modal-xl"> 572 <div class="modal-content"> 573 <div class="modal-header visually-hidden"> 574 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 575 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 576 </div> 577 <div class="modal-body p-2 p-lg-3 h-100"> 578 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 579 <div class="carousel-inner h-100 @theme"> 580 @foreach (MediaViewModel asset in assetsList) 581 { 582 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 583 foreach (string format in allSupportedFormats) 584 { 585 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 586 { 587 string imagePath = assetValue; 588 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 589 590 var parms = new Dictionary<string, object>(); 591 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 592 parms.Add("columns", Model.GridRowColumnCount); 593 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 594 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 595 596 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 597 @foreach (string imageFormat in supportedImageFormats) 598 { 599 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 600 { 601 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 602 } 603 } 604 605 @foreach (string videoFormat in supportedVideoFormats) 606 { 607 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 608 { 609 610 Dictionary<string, object> videoParams = GetVideoParams(asset, "modal"); 611 @RenderPartial("Components/VideoPlayer.cshtml", new FileViewModel { Path = asset.Value }, videoParams); 612 613 } 614 } 615 </div> 616 617 modalAssetNumber++; 618 } 619 } 620 } 621 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 622 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 623 <span class="visually-hidden">@Translate("Previous")</span> 624 </button> 625 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 626 <span class="carousel-control-next-icon" aria-hidden="true"></span> 627 <span class="visually-hidden">@Translate("Next")</span> 628 </button> 629 </div> 630 </div> 631 </div> 632 </div> 633 </div> 634 </div> 635 } 636 else if (Pageview.IsVisualEditorMode) 637 { 638 RatioSettings ratioSettings = GetRatioSettings("desktop"); 639 640 <div class="h-100 @theme"> 641 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 642 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 643 </div> 644 </div> 645 } 646 } 647
Ved at klikke på "Accepter alle" accepterer du, at vi kan indsamle oplysninger om dig til forskellige formål, herunder: Statistik og markedsføring