Error executing template "/Designs/Swift/Paragraph/Swift_ProductDetailsGallery_custom.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_cdc2e85c7fca4c508fed79d89ecb9ae4.<>c__DisplayClass29_0.<RenderImage>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\S_DW_AGF\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery_custom.cshtml:line 360
   at CompiledRazorTemplates.Dynamic.RazorEngine_cdc2e85c7fca4c508fed79d89ecb9ae4.<>c__DisplayClass28_0.<RenderAsset>b__0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\S_DW_AGF\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery_custom.cshtml:line 317
   at CompiledRazorTemplates.Dynamic.RazorEngine_cdc2e85c7fca4c508fed79d89ecb9ae4.Execute() in D:\dynamicweb.net\Solutions\S_DW_AGF\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery_custom.cshtml:line 167
   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 6 @functions { 7 public ProductViewModel product { get; set; } = new ProductViewModel(); 8 public string galleryLayout { get; set; } 9 public string[] supportedImageFormats { get; set; } 10 public string[] supportedVideoFormats { get; set; } 11 public string[] supportedDocumentFormats { get; set; } 12 public string[] allSupportedFormats { get; set; } 13 14 public class RatioSettings { 15 public string Ratio { get; set; } 16 public string CssClass { get; set; } 17 public string CssVariable { get; set; } 18 public string Fill { get; set; } 19 } 20 21 public RatioSettings GetRatioSettings(string size = "desktop") { 22 var ratioSettings = new RatioSettings(); 23 24 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 25 ratio = ratio != "0" ? ratio : ""; 26 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 27 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 28 cssClass = ratio != "" && ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 29 cssVariable = ratio != "" && ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 30 31 ratioSettings.Ratio = ratio; 32 ratioSettings.CssClass = cssClass; 33 ratioSettings.CssVariable = cssVariable; 34 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 35 36 return ratioSettings; 37 } 38 39 public string GetColumnClass(int total, int assetNumber) { 40 string colClass = total > 1 ? "g-col-lg-6" : "g-col-12"; 41 colClass = galleryLayout == "full-first" && assetNumber == 0 ? "g-col-12" : colClass; 42 colClass = galleryLayout == "full-last" && assetNumber == (total - 1) ? "g-col-12" : colClass; 43 colClass = galleryLayout == "advanced-grid" && assetNumber > 1 ? "g-col-4" : colClass; 44 45 colClass = galleryLayout == "advanced-grid" && total == 1 ? "g-col-12" : colClass; 46 colClass = galleryLayout == "advanced-grid" && total == 3 && assetNumber == 2 ? "g-col-12" : colClass; 47 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 2 ? "g-col-6" : colClass; 48 colClass = galleryLayout == "advanced-grid" && total == 4 && assetNumber == 3 ? "g-col-6" : colClass; 49 colClass = galleryLayout == "advanced-grid" && total == 6 && assetNumber == 5 ? "g-col-12" : colClass; 50 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 5 ? "g-col-6" : colClass; 51 colClass = galleryLayout == "advanced-grid" && total == 7 && assetNumber == 6 ? "g-col-6" : colClass; 52 colClass = galleryLayout == "advanced-grid" && total == 9 && assetNumber == 8 ? "g-col-12" : colClass; 53 54 return colClass; 55 } 56 57 public string GetArrowsColor() 58 { 59 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 60 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 61 return arrowsColor; 62 } 63 } 64 65 @{ 66 @* Get the product data *@ 67 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 68 { 69 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 70 } 71 72 // CUSTOM START 73 if (product.ProductFields.TryGetValue("CustomCss", out FieldValueViewModel customCssFieldValueVm)) 74 { 75 if (customCssFieldValueVm.Value is string && !String.IsNullOrEmpty(customCssFieldValueVm.Value.ToString())) 76 { 77 var customCss = new System.Web.HtmlString(customCssFieldValueVm.Value.ToString()); 78 <style> 79 @customCss 80 </style> 81 } 82 } 83 // CUSTOM END 84 85 @* Supported formats *@ 86 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff" }; 87 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 88 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 89 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 90 91 @* Collect the assets *@ 92 var selectedAssetCategories = Model.Item.GetRawValueString("ImageAssets").Split(',').ToList(); 93 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 94 95 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 96 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 97 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 98 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 99 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[]{}; 100 assetsList = assetsList.Union(assetsImages); 101 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 102 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 103 104 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 105 106 int totalAssets = 0; 107 foreach (MediaViewModel asset in assetsList) { 108 var assetValue = asset.Value.ToLower(); 109 foreach (string format in allSupportedFormats) { 110 if (assetValue.Contains(format) ) { 111 totalAssets++; 112 } 113 } 114 } 115 116 if (totalAssets == 0) 117 { 118 if (defaultImageFallback) { 119 assetsList = new List<MediaViewModel>(){ product.DefaultImage }; 120 totalAssets = 1; 121 } else { 122 assetsList = new List<MediaViewModel>(){ }; 123 totalAssets = 0; 124 } 125 } 126 127 @* Layout settings *@ 128 string spacing = Model.Item.GetRawValueString("Spacing", "4"); 129 spacing = spacing == "none" ? "gap-0" : spacing; 130 spacing = spacing == "small" ? "gap-3" : spacing; 131 spacing = spacing == "large" ? "gap-4" : spacing; 132 133 galleryLayout = Model.Item.GetRawValueString("Layout", "grid"); 134 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 135 136 var badgeParms = new Dictionary<string, object>(); 137 badgeParms.Add("size", "h5"); 138 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 139 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 140 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 141 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 142 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 143 144 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 145 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 146 DateTime createdDate = product.Created.Value; 147 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 148 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 149 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 150 } 151 152 @* Get assets from selected categories or get all assets *@ 153 @if (totalAssets != 0 && assetsList.Count() != 0) { 154 int desktopAssetNumber = 0; 155 int mobileAssetNumber = 0; 156 int mobileAssetThumbnailNumber = 0; 157 int modalAssetNumber = 0; 158 159 @* Desktop: Show the gallery on large screens *@ 160 <div class="d-none d-lg-block h-100 position-relative @theme item_@Model.Item.SystemName.ToLower() desktop"> 161 <div class="grid @spacing"> 162 @foreach (MediaViewModel asset in assetsList) { 163 var assetName = asset.Value.ToLower(); 164 foreach (string format in allSupportedFormats) { 165 if (assetName.Contains(format)) { 166 <div class="@GetColumnClass(totalAssets, desktopAssetNumber)"> 167 @{@RenderAsset(asset, desktopAssetNumber)} 168 </div> 169 desktopAssetNumber++; 170 } 171 } 172 } 173 </div> 174 175 @if (showBadges) { 176 <div class="position-absolute top-0 left-0 p-2 p-lg-3 w-100"> 177 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 178 </div> 179 } 180 </div> 181 182 @* Mobile: Show the thumbs on small screens *@ 183 <div class="d-block d-lg-none mx-lg-0 position-relative @theme item_@Model.Item.SystemName.ToLower() mobile"> 184 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor())" data-bs-ride="carousel"> 185 <div class="carousel-inner h-100"> 186 @foreach (MediaViewModel asset in assetsList) { 187 var assetValue = asset.Value.ToLower(); 188 foreach (string format in allSupportedFormats) { 189 if (assetValue.Contains(format)) { 190 string activeSlide = mobileAssetNumber == 0 ? "active" : ""; 191 192 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 193 @{@RenderAsset(asset, mobileAssetNumber, "mobile")} 194 </div> 195 mobileAssetNumber++; 196 } 197 } 198 } 199 </div> 200 </div> 201 202 @if (totalAssets > 1) { 203 <div id="SmallScreenImagesThumbnails_@Model.ID" class="d-flex flex-nowrap gap-2 overflow-x-auto my-3"> 204 @foreach (MediaViewModel asset in assetsList) { 205 var assetValue = asset.Value; 206 foreach (string format in allSupportedFormats) { 207 if (assetValue.Contains(format) ) { 208 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetValue); 209 imagePath = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/default.jpg" : imagePath; 210 string imagePathThumb = !imagePath.Contains("youtube") ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp" : imagePath; 211 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 212 213 string videoId = assetValue.Substring(assetValue.LastIndexOf('/') + 1); 214 string vimeoJsClass = assetValue.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 215 216 string productName = product.Name; 217 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 218 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 219 220 <div class="ratio ratio-4x3 border outline-none" style="flex:0 0 80px" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@mobileAssetThumbnailNumber"> 221 @foreach (string videoFormat in supportedVideoFormats) { 222 if (assetValue.Contains(videoFormat)) { 223 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 224 <div class="icon-3 position-absolute text-light" style="z-index: 1">@ReadFile(iconPath+"play-circle.svg")</div> 225 </div> 226 } 227 } 228 <img src="@imagePathThumb" class="p-1 @vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;" alt="@productName" @assetTitle > 229 </div> 230 231 mobileAssetThumbnailNumber++; 232 } 233 } 234 } 235 </div> 236 } 237 238 @if (showBadges) { 239 <div class="position-absolute top-0 left-0 p-2 p-lg-3"> 240 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 241 </div> 242 } 243 </div> 244 245 @* Modal with slides *@ 246 <div class="modal fade swift_products-details-images-modal" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle_@Model.ID" aria-hidden="true"> 247 <div class="modal-dialog modal-dialog-centered modal-xl"> 248 <div class="modal-content"> 249 <div class="modal-header visually-hidden"> 250 <h5 class="modal-title" id="productDetailsGalleryModalTitle_@Model.ID">@product.Title</h5> 251 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 252 </div> 253 <div class="modal-body p-2 p-lg-3 h-100"> 254 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 255 <div class="carousel-inner h-100 @theme"> 256 @foreach (MediaViewModel asset in assetsList) { 257 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 258 foreach (string format in allSupportedFormats) { 259 if (assetValue.Contains(format) ) { 260 string imagePath = assetValue; 261 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 262 263 var parms = new Dictionary<string, object>(); 264 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 265 parms.Add("fullwidth", true); 266 parms.Add("columns", Model.GridRowColumnCount); 267 268 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 269 @foreach (string imageFormat in supportedImageFormats) { 270 if (assetValue.Contains(imageFormat)) { 271 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 272 } 273 } 274 275 @foreach (string videoFormat in supportedVideoFormats) { 276 if (assetValue.Contains(videoFormat)) { 277 {@RenderVideoPlayer(asset, "modal")} 278 } 279 } 280 </div> 281 282 modalAssetNumber++; 283 } 284 } 285 } 286 <button class="carousel-control-prev" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 287 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 288 <span class="visually-hidden">@Translate("Previous")</span> 289 </button> 290 <button class="carousel-control-next" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 291 <span class="carousel-control-next-icon" aria-hidden="true"></span> 292 <span class="visually-hidden">@Translate("Next")</span> 293 </button> 294 </div> 295 </div> 296 </div> 297 </div> 298 </div> 299 </div> 300 } else if (Pageview.IsVisualEditorMode) { 301 RatioSettings ratioSettings = GetRatioSettings("desktop"); 302 303 <div class="h-100 @theme"> 304 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 305 <img src="/Files/Images/missing_image.jpg" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;" alt="@Translate("Missing image")"> 306 </div> 307 </div> 308 } 309 310 @helper RenderAsset(MediaViewModel asset, int assetNumber, string size = "desktop") { 311 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 312 string assetValue = asset.Value; 313 314 <div class="h-100 @(theme)"> 315 @foreach (string format in supportedImageFormats) { //Images 316 if (assetValue.Contains(format)) { 317 {@RenderImage(asset, assetNumber, size)} 318 } 319 } 320 @foreach (string format in supportedVideoFormats) { //Videos 321 if (assetValue.Contains(format)) { 322 if (Model.Item.GetString("OpenVideoInModal") == "true") { 323 {@RenderVideoScreendump(asset, assetNumber, size)} 324 } else { 325 {@RenderVideoPlayer(asset, size)} 326 } 327 } 328 } 329 @foreach (string format in supportedDocumentFormats) { //Documents 330 if (assetValue.Contains(format)) { 331 {@RenderDocument(asset, assetNumber, size)} 332 } 333 } 334 </div> 335 } 336 337 @helper RenderImage(MediaViewModel asset, int number, string size = "desktop") { 338 string productName = product.Name; 339 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 340 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 341 342 RatioSettings ratioSettings = GetRatioSettings(size); 343 344 var parms = new Dictionary<string, object>(); 345 parms.Add("alt", productName); 346 parms.Add("itemprop", "image"); 347 parms.Add("fullwidth", true); 348 parms.Add("columns", Model.GridRowColumnCount); 349 if (!string.IsNullOrEmpty(asset.DisplayName)) { 350 parms.Add("title", asset.DisplayName); 351 } 352 353 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 354 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 355 } else { 356 parms.Add("cssClass", "mw-100 mh-100"); 357 } 358 359 // CUSTOM START 360 bool customPrintBaseAsset = size == "desktop" && asset.Keywords.Contains("CustomPrintBase"); 361 362 <a href="@imageLinkPath" @(customPrintBaseAsset ? "data-custom-print-base-image" : "") class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 363 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 364 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 365 </div> 366 @if (customPrintBaseAsset) 367 { 368 <div class="custom-print-container" id="js-custom-print-container"> 369 <div class="custom-print-name" id="js-custom-print-name"></div> 370 <div class="custom-print-number" id="js-custom-print-number"></div> 371 </div> 372 } 373 </a> 374 // CUSTOM END 375 } 376 377 @helper RenderVideoScreendump(MediaViewModel asset, int number, string size = "desktop") { 378 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 379 380 string videoScreendumpPath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : ""; 381 string videoId = videoScreendumpPath.Substring(videoScreendumpPath.LastIndexOf('/') + 1); 382 videoScreendumpPath = videoScreendumpPath.Contains("youtu.be") || videoScreendumpPath.Contains("youtube") ? "https://img.youtube.com/vi/" + videoId + "/maxresdefault.jpg" : videoScreendumpPath; 383 384 string vimeoJsClass = videoScreendumpPath.Contains("vimeo") ? "js-vimeo-video-thumbnail" : ""; 385 videoScreendumpPath = videoScreendumpPath.Contains("vimeo") ? "" : videoScreendumpPath; 386 387 string productName = product.Name; 388 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 389 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 390 391 RatioSettings ratioSettings = GetRatioSettings(size); 392 393 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 394 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@number"> 395 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 396 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@productName" @assetTitle class="@vimeoJsClass mw-100 mh-100" data-video-id="@videoId" style="object-fit: cover;"> 397 </div> 398 </div> 399 } 400 401 @helper RenderVideoPlayer(MediaViewModel asset, string size = "desktop") { 402 string assetName = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : asset.Name; 403 string assetValue = asset.Value; 404 string videoId = asset.Value.Substring(asset.Value.LastIndexOf('/') + 1); 405 string type = assetValue.Contains("youtu.be") || assetValue.Contains("youtube") ? "youtube" : ""; 406 type = assetValue.Contains("vimeo") ? "vimeo" : type; 407 type = assetValue.Contains(".mp4") || assetValue.Contains(".webm") ? "selfhosted" : type; 408 409 string openInModal = Model.Item.GetString("OpenVideoInModal"); 410 bool autoPlay = Model.Item.GetBoolean("VideoAutoPlay"); 411 412 <div class="h-100" itemscope itemtype="https://schema.org/VideoObject"> 413 <span class="visually-hidden" itemprop="name">@assetName</span> 414 <span class="visually-hidden" itemprop="contentUrl">@asset.Value</span> 415 <span class="visually-hidden" itemprop="thumbnailUrl">@asset.Value</span> 416 @if (type != "selfhosted") { 417 <div 418 id="player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size" 419 class="plyr__video-embed" 420 data-plyr-provider="@(type)" 421 data-plyr-embed-id="@videoId" 422 style="--plyr-color-main: var(--swift-foreground-color); height: 100%"> 423 </div> 424 425 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/plyr.js"></script> 426 427 <script type="module"> 428 var player = new Plyr('#player_@(Pageview.CurrentParagraph.ID)_@(videoId)_@size', { 429 type: 'video', 430 youtube: { 431 noCookie: true, 432 showinfo: 0 433 }, 434 fullscreen: { 435 enabled: true, 436 iosNative: true, 437 } 438 }); 439 440 @if (autoPlay && openInModal == "false") { 441 <text> 442 player.config.autoplay = true; 443 player.config.muted = true; 444 player.config.volume = 0; 445 player.media.loop = true; 446 447 player.on('ready', function() { 448 if (player.config.autoplay === true) { 449 player.media.play(); 450 } 451 }); 452 </text> 453 } 454 455 @if (openInModal == "true") { 456 <text> 457 var productDetailsGalleryModal = document.querySelector('#modal_@Model.ID') 458 productDetailsGalleryModal.addEventListener('hidden.bs.modal', function (event) { 459 player.media.pause(); 460 }) 461 </text> 462 } 463 </script> 464 } else { 465 string autoPlayAttributes = (autoPlay && openInModal == "false") ? "loop autoplay muted playsinline" : ""; 466 string videoType = Path.GetExtension(assetValue).ToLower(); 467 468 <video preload="auto" @autoPlayAttributes class="h-100 w-100" style="object-fit: cover;"> 469 <source src="@assetValue" type="video/@videoType.Replace(".", "")"> 470 </video> 471 } 472 </div> 473 } 474 475 @helper RenderDocument(MediaViewModel asset, int number, string size = "desktop") { 476 string iconPath = "/Files/Templates/Designs/Swift/Assets/icons/"; 477 478 string productName = product.Name; 479 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 480 string imageLinkPath = imagePath; 481 482 RatioSettings ratioSettings = GetRatioSettings(size); 483 484 var parms = new Dictionary<string, object>(); 485 parms.Add("alt", productName); 486 parms.Add("itemprop", "image"); 487 parms.Add("fullwidth", true); 488 parms.Add("columns", Model.GridRowColumnCount); 489 if (!string.IsNullOrEmpty(asset.DisplayName)) { 490 parms.Add("title", asset.DisplayName); 491 } 492 493 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") { 494 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 495 } else { 496 parms.Add("cssClass", "mw-100 mh-100"); 497 } 498 499 <a href="@imageLinkPath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download title="@Translate("Download")"> 500 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100"> 501 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 502 @if (asset.Value.Contains(".pdf")) { 503 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 504 } else { 505 506 } 507 </div> 508 </a> 509 } 510

Test BOM

625,00 kr. InStock
På lager: 1-3 dages levering

Relaterede produkter

Populære produkter