Error executing template "Designs/Swift-v2/Paragraph/Swift-v2_ProductMedia.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_752edd84fd4a4f3e8b9fa59540b8f999.ExecuteAsync() at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader) at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag) at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer) at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter) at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag) 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.Core.Encoders 3 @using Dynamicweb.Ecommerce.ProductCatalog 4 @using Dynamicweb.Frontend 5 @using System.IO 6 @using System.Text.RegularExpressions; 7 8 @functions { 9 public ProductViewModel product { get; set; } = new ProductViewModel(); 10 public string galleryLayout { get; set; } 11 public string[] supportedImageFormats { get; set; } 12 public string[] supportedVideoFormats { get; set; } 13 public string[] supportedDocumentFormats { get; set; } 14 public string[] allSupportedFormats { get; set; } 15 16 public class RatioSettings 17 { 18 public string Ratio { get; set; } 19 public string CssClass { get; set; } 20 public string CssVariable { get; set; } 21 public string Fill { get; set; } 22 } 23 24 public RatioSettings GetRatioSettings(string size = "desktop") 25 { 26 var ratioSettings = new RatioSettings(); 27 28 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 29 ratio = ratio != "0" ? ratio : ""; 30 string cssClass = ratio != "" && ratio != "fill" ? " ratio" : ""; 31 string cssVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : ""; 32 cssClass = ratio == "fill" && size == "mobile" ? " ratio" : cssClass; 33 cssVariable = ratio == "fill" && size == "mobile" ? "--bs-aspect-ratio: 66%" : cssVariable; 34 35 ratioSettings.Ratio = ratio; 36 ratioSettings.CssClass = cssClass; 37 ratioSettings.CssVariable = cssVariable; 38 ratioSettings.Fill = ratio == "fill" ? " h-100" : ""; 39 40 return ratioSettings; 41 } 42 43 public string GetArrowsColor() 44 { 45 var invertColor = Model.Item.GetBoolean("InvertModalArrowsColor"); 46 var arrowsColor = invertColor ? " carousel-dark" : string.Empty; 47 return arrowsColor; 48 } 49 50 public string GetThumbnailPlacement() 51 { 52 return Model.Item.GetRawValueString("ThumbnailPlacement", "bottom"); 53 } 54 55 public string GetThumbnailRowSettingCss() 56 { 57 switch (GetThumbnailPlacement()) 58 { 59 case "bottom": 60 return "d-flex flex-wrap"; 61 case "left": 62 return "d-flex flex-column order-first"; 63 case "right": 64 return "d-flex flex-column order-last"; 65 default: 66 return "d-flex flex-wrap"; 67 } 68 } 69 70 public string GetVideoType(string assetValue) 71 { 72 string type = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "youtube" : string.Empty; 73 type = assetValue.IndexOf("vimeo", StringComparison.OrdinalIgnoreCase) >= 0 ? "vimeo" : type; 74 type = string.IsNullOrEmpty(type) ? "selfhosted" : type; 75 76 return type; 77 } 78 79 public string GetYoutubeScreenDump(string assetValue, string quality) 80 { 81 var regex = new Regex(@"(?:youtube\.com\/.*[\?&]v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]+)(?:\?.*)?"); 82 Match match = regex.Match(assetValue); 83 string videoId = match.Success ? match.Groups[1].Value : string.Empty; 84 string youtubeThumbnail = $"https://img.youtube.com/vi/{videoId}/{quality}.jpg"; 85 return youtubeThumbnail; 86 } 87 } 88 89 @{ 90 ProductViewModel product = null; 91 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 92 { 93 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 94 } 95 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 96 { 97 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 98 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 99 100 if (productList?.Products is object) 101 { 102 product = productList.Products[0]; 103 } 104 } 105 } 106 107 @if (product is object) 108 { 109 @* Supported formats *@ 110 supportedImageFormats = new string[] { ".jpg", ".jpeg", ".webp", ".png", ".gif", ".bmp", ".tiff"}; 111 supportedVideoFormats = new string[] { "youtu.be", "youtube", "vimeo", ".mp4", ".webm" }; 112 supportedDocumentFormats = new string[] { ".pdf", ".docx", ".xlsx", ".ppt", "pptx" }; 113 allSupportedFormats = supportedImageFormats.Concat(supportedVideoFormats).Concat(supportedDocumentFormats).ToArray(); 114 115 @* Collect the assets *@ 116 var selectedAssetCategories = Model.Item.GetList("ImageAssets")?.GetRawValue().OfType<string>(); 117 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages"); 118 119 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@ 120 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 121 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 122 assetsImages = assetsImages.OrderByDescending(x => x.Value.Equals(defaultImage)); 123 IEnumerable<MediaViewModel> assetsList = new MediaViewModel[] { }; 124 assetsList = assetsList.Union(assetsImages); 125 assetsList = includeImagePatternImages ? assetsList.Union(product.ImagePatternImages) : assetsList; 126 assetsList = includeImagePatternImages && assetsList.Count() == 0 ? assetsList.Append(product.DefaultImage) : assetsList; 127 128 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback"); 129 bool showOnlyPrimaryImage = Model.Item.GetBoolean("ShowOnlyPrimaryImage"); 130 131 int totalAssets = 0; 132 if (showOnlyPrimaryImage == false) 133 { 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 147 if ((totalAssets == 0 && product.DefaultImage != null && selectedAssetCategories.Count() == 0) || (showOnlyPrimaryImage == true && product.DefaultImage != null) || totalAssets == 0 && defaultImageFallback) 148 { 149 assetsList = new List<MediaViewModel>() { product.DefaultImage }; 150 totalAssets = 1; 151 } 152 153 @* Get assets from selected categories or get all assets *@ 154 if (totalAssets != 0) 155 { 156 int assetNumber = 0; 157 int thumbnailNumber = 0; 158 int modalAssetNumber = 0; 159 string thumbnailAxisCss = GetThumbnailPlacement() == "bottom" ? "flex-column" : string.Empty; 160 161 <div class="d-flex gap-3 h-100 @(thumbnailAxisCss) item_@Model.Item.SystemName.ToLower()" data-dw-colorscheme="@Model.ColorScheme?.Id"> 162 <div id="SmallScreenImages_@Model.ID" class="carousel@(GetArrowsColor()) col position-relative" data-bs-ride="carousel"> 163 <div class="carousel-inner h-100"> 164 @foreach (MediaViewModel asset in assetsList) 165 { 166 var assetValue = asset.Value; 167 foreach (string format in allSupportedFormats) 168 { 169 if (assetValue.IndexOf(format, StringComparison.OrdinalIgnoreCase) >= 0) 170 { 171 string activeSlide = assetNumber == 0 ? "active" : ""; 172 173 <div class="carousel-item @activeSlide" data-bs-interval="99999"> 174 @{ 175 string size = "mobile"; 176 177 <div class="h-100"> 178 @foreach (string imageFormat in supportedImageFormats) 179 { //Images 180 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 181 { 182 if (product is object) 183 { 184 string productName = product.Name; 185 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 186 187 RatioSettings ratioSettings = GetRatioSettings(size); 188 189 var parms = new Dictionary<string, object>(); 190 parms.Add("alt", productName + asset.Keywords); 191 parms.Add("itemprop", "image"); 192 parms.Add("columns", Model.GridRowColumnCount); 193 parms.Add("eagerLoadNewImages", Model.Item.GetBoolean("DisableLazyLoading")); 194 parms.Add("doNotUseGetimage", Model.Item.GetBoolean("DisableGetImage")); 195 if (!string.IsNullOrEmpty(asset.DisplayName)) 196 { 197 parms.Add("title", asset.DisplayName); 198 } 199 200 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 201 { 202 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 203 } 204 else 205 { 206 parms.Add("cssClass", "mw-100 mh-100"); 207 } 208 209 <a href="@imagePath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 210 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 211 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 212 </div> 213 </a> 214 } 215 } 216 } 217 @foreach (string videoFormat in supportedVideoFormats) 218 { //Videos 219 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 220 { 221 if (product is object) { 222 var video = asset.GetVideoViewModel(); 223 224 if (Model.Item.GetString("OpenVideoInModal") == "true") 225 { 226 string iconPath = "/Files/Images/Icons/"; 227 228 string productName = product.Name; 229 productName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 230 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? "title=\"" + asset.DisplayName + "\"" : ""; 231 232 RatioSettings ratioSettings = GetRatioSettings(size); 233 234 string type = GetVideoType(asset.Value); 235 236 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "maxresdefault") : string.Empty; 237 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : ""; 238 239 240 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable); cursor: pointer" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID"> 241 <div class="d-flex align-items-center justify-content-center overflow-hidden h-100" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide-to="@assetNumber"> 242 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 243 @if (video.IsExternalLink()) 244 { 245 <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;"> 246 } 247 else 248 { 249 250 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 251 <source src="@(asset.Value)#t=0.001" type="@video.GetVideoType()"> 252 </video> 253 } 254 </div> 255 </div> 256 } 257 else 258 { 259 @RenderPartial("Components/VideoPlayer.cshtml", video) 260 } 261 } 262 } 263 } 264 @foreach (string documentFormat in supportedDocumentFormats) 265 { //Documents 266 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 267 { 268 if (product is object) 269 { 270 string iconPath = "/Files/Images/Icons/"; 271 272 string productName = product.Name; 273 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 274 275 RatioSettings ratioSettings = GetRatioSettings(size); 276 277 var parms = new Dictionary<string, object>(); 278 parms.Add("alt", productName + asset.Keywords); 279 parms.Add("itemprop", "image"); 280 parms.Add("fullwidth", true); 281 parms.Add("columns", Model.GridRowColumnCount); 282 if (!string.IsNullOrEmpty(asset.DisplayName)) 283 { 284 parms.Add("title", asset.DisplayName); 285 } 286 287 if (ratioSettings.Ratio == "fill" && galleryLayout != "grid") 288 { 289 parms.Add("cssClass", "w-100 h-100 image-zoom-lg-l-hover"); 290 } 291 else 292 { 293 parms.Add("cssClass", "mw-100 mh-100"); 294 } 295 296 <a href="@imagePath" class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)" download alt="@Translate("Download")"> 297 <div class="d-flex align-items-center justify-content-center text-center overflow-hidden h-100 border"> 298 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 299 <span>@Translate("Download") @(asset.Name)@Path.GetExtension(asset.Value).ToLower()</span> 300 </div> 301 </a> 302 } 303 304 } 305 } 306 </div> 307 } 308 309 310 </div> 311 assetNumber++; 312 } 313 } 314 } 315 </div> 316 317 </div> 318 319 @if (totalAssets > 1) 320 { 321 //Custom: added support for STL. Moved div to prevent empty thumbnails 322 <div style="width:100%; background-color:#E9EAEB; height:1px; margin:auto;"></div> 323 <div class="@(GetThumbnailRowSettingCss()) gap-3" id="SmallScreenImagesThumbnails_@Model.ID"> 324 @foreach (MediaViewModel asset in assetsList) 325 { 326 var assetValue = asset.Value; 327 string assetName = asset.Name; 328 assetName += !string.IsNullOrEmpty(asset.Keywords) ? " " + asset.Keywords : ""; 329 string assetTitle = !string.IsNullOrEmpty(asset.DisplayName) ? asset.DisplayName : null; 330 string iconPath = "/Files/Images/Icons/"; 331 332 string imagePath = assetValue; 333 imagePath = assetValue.IndexOf("youtu.be", StringComparison.OrdinalIgnoreCase) >= 0 || assetValue.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) >= 0 ? "https://img.youtube.com/vi/" + assetValue.Substring(assetValue.LastIndexOf('/') + 1) + "/mqdefault.jpg" : imagePath; 334 string imagePathThumb = assetValue.StartsWith("/Files/", StringComparison.OrdinalIgnoreCase) ? imagePath.IndexOf("youtube", StringComparison.OrdinalIgnoreCase) < 0 && imagePath.IndexOf(".mp4", StringComparison.OrdinalIgnoreCase) < 0 ? $"/Admin/Public/GetImage.ashx?image={imagePath}&width=180&format=webp" : imagePath : assetValue; 335 336 RatioSettings ratioSettings = GetRatioSettings("desktop"); 337 338 @foreach (string imageFormat in supportedImageFormats) 339 { //Images 340 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 341 { 342 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 343 <img src="@imagePathThumb" alt="@HtmlEncoder.HtmlAttributeEncode(assetName)" class="border-round p-0 p-lg-1 w-100 h-100" style="object-fit: contain;"> 344 </div> 345 thumbnailNumber++; 346 } 347 } 348 349 @foreach (string videoFormat in supportedVideoFormats) 350 { //Videos 351 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 352 { 353 var video = asset.GetVideoViewModel(); 354 string type = GetVideoType(asset.Value); 355 356 string videoScreendumpPath = type == "youtube" ? GetYoutubeScreenDump(asset.Value, "mqdefault") : ""; 357 videoScreendumpPath = type == "vimeo" ? string.Empty : videoScreendumpPath; 358 string videoJsClass = type == "vimeo" ? "js-vimeo-video-thumbnail" : string.Empty; 359 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 360 <div class="icon-5 position-absolute top-50 start-50 translate-middle" style="z-index: 1">@ReadFile(iconPath + "play-circle.svg")</div> 361 362 @if (video.IsExternalLink()) 363 { 364 <img src="@videoScreendumpPath" loading="lazy" decoding="async" alt="@assetTitle" @assetTitle class="@videoJsClass mw-100 mh-100" data-asset-value="@asset.Value" style="object-fit: cover;" /> 365 } 366 else 367 { 368 <video preload="auto" class="h-100 w-100" style="object-fit: contain;"> 369 <source src="@(asset.Value)#t=0.001" type="@video.GetVideoType()"> 370 </video> 371 } 372 </div> 373 thumbnailNumber++; 374 } 375 } 376 377 @foreach (string documentFormat in supportedDocumentFormats) 378 { //Documents 379 if (assetValue.IndexOf(documentFormat, StringComparison.OrdinalIgnoreCase) >= 0) 380 { 381 <div class="border border-round outline-none position-relative @(ratioSettings.CssClass)" style="@(ratioSettings.CssVariable); cursor: pointer; min-width: 50px; max-width: 50px; min-height: 50px; max-height: 50px;" data-bs-target="#SmallScreenImages_@Model.ID" data-bs-slide-to="@thumbnailNumber"> 382 <a href="@assetValue" class="ratio ratio-4x3 border outline-none" style="cursor: pointer; min-width: 7rem; max-width: 8rem;" download title="@asset.Value"> 383 <div class="d-flex align-items-center justify-content-center text-center overflow-hidden h-100 border"> 384 <div class="icon-5 position-absolute" style="z-index: 1">@ReadFile(iconPath + "download.svg")</div> 385 <span>@(asset.Name)@Path.GetExtension(asset.Value).ToLower()</span> 386 </div> 387 </a> 388 </div> 389 390 thumbnailNumber++; 391 } 392 } 393 394 if (assetValue.IndexOf(".stl", StringComparison.OrdinalIgnoreCase) >= 0) 395 { 396 var materialColor = "#009870"; 397 var backgroundColor = "#FFFFFF"; 398 399 product.ProductFields.TryGetValue("CustomSp3DModelColor", out FieldValueViewModel color); 400 401 if (!string.IsNullOrEmpty(Dynamicweb.Core.Converter.ToString(color?.Value))) 402 { 403 materialColor = Dynamicweb.Core.Converter.ToString(color.Value); 404 } 405 406 <div class="@(Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Desktop ? "flex-fill text-end" : "")"> 407 <button class="btn btn-link text-decoration-none" data-dw-button="btn-link" data-bs-toggle="modal" data-bs-target="#STL3DModelModal" onclick="swift.Custom.STLViewer('stl3dmodel');"> 408 <span class="text-decoration-none"> 409 @ReadFile("Files/Images/Icons/Custom/3d-model.svg") 410 <span class="text-decoration-underline ms-1"> 411 @Translate("Custom.MediaGallery.ViewModel", "View 3D model") 412 </span> 413 </span> 414 </button> 415 <div class="modal" id="STL3DModelModal"> 416 <div class="modal-dialog modal-dialog-centered"> 417 <div class="modal-content"> 418 <div class="modal-header text-center border-0"> 419 <div class="modal-title text-md semi-bold flex-fill paragraph-md-semibold">@Translate("Custom.MediaGallery.ViewModel.Modal.Header", "3D model")</div> 420 <button type="button" class="btn-close position-absolute top-1 end-0 me-2" data-bs-dismiss="modal" aria-label="Close" onclick="document.querySelector('body').style = ''"></button> 421 </div> 422 <div class="modal-body"> 423 <div id="stl3dmodel" style="width: 400px; height: 400px; margin: auto;" data-file="@assetValue" data-name="@assetName" data-color="@materialColor" data-background-color="@backgroundColor" data-light-skycolor="#FFFFFF" data-light-groundcolor="#000000"> 424 </div> 425 </div> 426 </div> 427 </div> 428 </div> 429 </div> 430 } 431 } 432 </div> 433 } 434 </div> 435 @* Modal with slides *@ 436 <div class="modal fade" id="modal_@Model.ID" tabindex="-1" aria-labelledby="mediaModalTitle_@Model.ID" aria-hidden="true"> 437 <div class="modal-dialog modal-dialog-centered modal-xl"> 438 <div class="modal-content"> 439 <div class="modal-header visually-hidden"> 440 <h5 class="modal-title" id="mediaModalTitle_@Model.ID">@product.Title</h5> 441 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 442 </div> 443 <div class="modal-body p-2 p-lg-3 h-100"> 444 <div id="ModalCarousel_@Model.ID" class="carousel@(GetArrowsColor()) h-100" data-bs-ride="carousel"> 445 <div class="carousel-inner h-100" data-dw-colorscheme="@Model.ColorScheme?.Id"> 446 @foreach (MediaViewModel asset in assetsList) 447 { 448 var assetValue = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value; 449 foreach (string supportedFormat in supportedImageFormats.Concat(supportedVideoFormats).ToArray()) 450 { 451 if (assetValue.IndexOf(supportedFormat, StringComparison.OrdinalIgnoreCase) >= 0) 452 { 453 string imagePath = assetValue; 454 string activeSlide = modalAssetNumber == 0 ? "active" : ""; 455 456 var parms = new Dictionary<string, object>(); 457 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 458 parms.Add("fullwidth", true); 459 parms.Add("columns", Model.GridRowColumnCount); 460 461 <div class="carousel-item @activeSlide h-100" data-bs-interval="99999"> 462 @foreach (string imageFormat in supportedImageFormats) 463 { //Images 464 if (assetValue.IndexOf(imageFormat, StringComparison.OrdinalIgnoreCase) >= 0) 465 { 466 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 467 } 468 } 469 470 @foreach (string videoFormat in supportedVideoFormats) 471 { //Videos 472 if (assetValue.IndexOf(videoFormat, StringComparison.OrdinalIgnoreCase) >= 0) 473 { 474 @RenderPartial("Components/VideoPlayer.cshtml", asset.GetVideoViewModel()) 475 } 476 } 477 </div> 478 modalAssetNumber++; 479 } 480 } 481 } 482 <button class="carousel-control-prev carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="prev"> 483 <span class="carousel-control-prev-icon" aria-hidden="true"></span> 484 <span class="visually-hidden">@Translate("Previous")</span> 485 </button> 486 <button class="carousel-control-next carousel-control-area" type="button" data-bs-target="#ModalCarousel_@Model.ID" data-bs-slide="next"> 487 <span class="carousel-control-next-icon" aria-hidden="true"></span> 488 <span class="visually-hidden">@Translate("Next")</span> 489 </button> 490 </div> 491 </div> 492 </div> 493 </div> 494 </div> 495 </div> 496 } 497 else if (Pageview.IsVisualEditorMode) 498 { 499 RatioSettings ratioSettings = GetRatioSettings("desktop"); 500 501 <div class="h-100" data-dw-colorscheme="@Model.ColorScheme?.Id"> 502 <div class="d-block @(ratioSettings.CssClass)@(ratioSettings.Fill)" style="@(ratioSettings.CssVariable)"> 503 <img src="/Files/Images/nopic.png" loading="lazy" decoding="async" class="mh-100 mw-100" style="object-fit: cover;"> 504 </div> 505 </div> 506 } 507 } 508 else if (Pageview.IsVisualEditorMode) 509 { 510 <div class="alert alert-dark m-0">@Translate("The images will be shown here, if any")</div> 511 } 512 513 514 515
Flange Protectors with handle (EF)
Complete protection for flange surfaces and sealing zones. EF Flange Protectors cover circuit outlets/inlets, such as turbo connections, and surrounding flange faces, preventing scratches and cracks that can affect sealing. Perfect for transport, storage, and assembly where flange integrity is critical.
|
|
CO2
|
A
mm
|
B
mm
|
C
mm
|
H
mm
|
J
mm
|
L
mm
|
Q
mm
|
|---|
| EF0004A-AA00A | Red | Recycled LDPE | 0 | 5.15 | 22.1 | 0 | 0 | 64 | 6.7 | 3.3 |
|
| EF0010B-AA00B | Red | Recycled LDPE | 0 | 8.7 | 10.1 | 0 | 0 | 86.4 | 6.35 | 6.35 |
|
| EF0011A-AA00B | Red | Recycled LDPE | 0 | 7 | 7.7 | 0 | 0 | 105.9 | 10 | 36.15 |
|
| EF0015A-AA00B | Red | Recycled LDPE | 0 | 33.1 | 32 | 32 | 8 | 0 | 0 | 0 |
|
| EF0098A-AA30B | Grey | Recycled LDPE | 0 | 6.95 | 10.7 | 0 | 0 | 30 | 5 | 11.4 |
|
| EF0305A-AA20A | Yellow | Recycled LDPE | 0 | 30.5 | 0 | 0 | 0 | 76 | 9.8 | 0 |
|
| EF0330A-AA00A | Red | Recycled LDPE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
|
| EF0362B-AA20B | Yellow | Recycled LDPE | 0 | 36 | 34 | 34 | 0 | 0 | 0 | 0 |
|
Learn how our full service can add value to your business
Your full-service protection solution partner