From a8dd90d9d61538edb5069491afeaa7662f338407 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Jul 2021 13:48:44 +0200 Subject: [PATCH 01/54] Add CropWebProcessor --- .../UmbracoBuilder.ImageSharp.cs | 3 + .../ImageProcessors/CropWebProcessor.cs | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 96bf7017cb..775d36eaf5 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -10,6 +10,7 @@ using SixLabors.ImageSharp.Web.Processors; using SixLabors.ImageSharp.Web.Providers; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Web.Common.ImageProcessors; namespace Umbraco.Extensions { @@ -51,6 +52,8 @@ namespace Umbraco.Extensions .SetCache() .SetCacheHash() .AddProvider() + .ClearProcessors() // ImageSharp includes the processors by default, so we have to clear and re-add to control the order + .AddProcessor() .AddProcessor() .AddProcessor() .AddProcessor(); diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs new file mode 100644 index 0000000000..6393e9455f --- /dev/null +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Globalization; +using Microsoft.Extensions.Logging; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Web; +using SixLabors.ImageSharp.Web.Commands; +using SixLabors.ImageSharp.Web.Processors; +using ImageCropperCropCoordinates = Umbraco.Cms.Core.PropertyEditors.ValueConverters.ImageCropperValue.ImageCropperCropCoordinates; + +namespace Umbraco.Cms.Web.Common.ImageProcessors +{ + public class CropWebProcessor : IImageWebProcessor + { + /// + /// The command constant for the crop definition + /// + public const string Crop = "crop"; + + public FormattedImage Process( + FormattedImage image, + ILogger logger, + IDictionary commands, + CommandParser parser, + CultureInfo culture) + { + ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands); + if (cropCoordinates is null) + { + return image; + } + + Size size = image.Image.Size(); + Rectangle crop = GetCropRectangle(size.Width, size.Height, cropCoordinates); + image.Image.Mutate(x => x.Crop(crop)); + return image; + } + + private static readonly IEnumerable CropCommands = new[] {Crop}; + + public IEnumerable Commands { get; } = CropCommands; + + private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates) + { + + // Get coordinates of top left corner of the rectangle + var topX = decimal.ToInt32(width * coordinates.X1); + var topY = decimal.ToInt32(height * coordinates.Y1); + // Get coordinated of bottom right corner + var bottomX = decimal.ToInt32(width - (width * coordinates.X2)); + var bottomY = decimal.ToInt32(height - (height * coordinates.Y2)); + + // Get width and height of crop + var cropWidth = bottomX - topX; + var cropHeight = bottomY - topY; + + return new Rectangle(topX, topY, cropWidth, cropHeight); + } + + private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands) + { + if (!commands.TryGetValue(Crop, out var crop)) + { + return null; + } + + var crops = crop.Split(','); + + return new ImageCropperCropCoordinates() + { + X1 = decimal.Parse(crops[0], CultureInfo.InvariantCulture), + Y1 = decimal.Parse(crops[1], CultureInfo.InvariantCulture), + X2 = decimal.Parse(crops[2], CultureInfo.InvariantCulture), + Y2 = decimal.Parse(crops[3], CultureInfo.InvariantCulture) + }; + } + } +} From 865a5b999ca30d036329d16a38477f8237c0ad4e Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Jul 2021 14:08:06 +0200 Subject: [PATCH 02/54] Add ParseDecimal method --- .../ImageProcessors/CropWebProcessor.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 6393e9455f..e06f93a806 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -42,7 +42,6 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates) { - // Get coordinates of top left corner of the rectangle var topX = decimal.ToInt32(width * coordinates.X1); var topY = decimal.ToInt32(height * coordinates.Y1); @@ -68,11 +67,13 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return new ImageCropperCropCoordinates() { - X1 = decimal.Parse(crops[0], CultureInfo.InvariantCulture), - Y1 = decimal.Parse(crops[1], CultureInfo.InvariantCulture), - X2 = decimal.Parse(crops[2], CultureInfo.InvariantCulture), - Y2 = decimal.Parse(crops[3], CultureInfo.InvariantCulture) + X1 = ParseDecimal(crops[0]), + Y1 = ParseDecimal(crops[1]), + X2 = ParseDecimal(crops[2]), + Y2 = ParseDecimal(crops[3]) }; } + + private static decimal ParseDecimal(string decimalString) => decimal.Parse(decimalString, CultureInfo.InvariantCulture); } } From 753f6f6b1d30db9c704725d2c5417ed4561aa97a Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Jul 2021 14:11:30 +0200 Subject: [PATCH 03/54] Clean url generator a bit --- .../Media/ImageSharpImageUrlGenerator.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index e1bf2c197b..0d5f5202b0 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -49,10 +49,9 @@ namespace Umbraco.Cms.Infrastructure.Media private void AppendFocalPoint(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) { - imageProcessorUrl.Append("?center="); + imageProcessorUrl.Append("?rxy="); imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)).Append(","); imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); - imageProcessorUrl.Append("&mode=crop"); } private void AppendCrop(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) @@ -62,7 +61,6 @@ namespace Umbraco.Cms.Infrastructure.Media imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); - imageProcessorUrl.Append("&cropmode=percentage"); } } } From d7795cf316929779f4508f3a4a9471c3e8420767 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Jul 2021 15:53:48 +0200 Subject: [PATCH 04/54] Fix unit tests --- .../Media/ImageSharpImageUrlGeneratorTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index 52ee1ed0e4..fcf466411d 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -20,28 +20,28 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_CropAliasTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); } [Test] public void GetCropUrl_WidthHeightTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300 }); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=200&height=300", urlString); } [Test] public void GetCropUrl_FocalPointTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 100, Height = 100 }); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=100&height=100", urlString); } [Test] public void GetCropUrlFurtherOptionsTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" }); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); } /// @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetBaseCropUrlFromModelTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); } /// @@ -81,7 +81,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_CropAliasHeightRatioModeTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, HeightRatio = 1 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&heightratio=1&width=100", urlString); } /// @@ -91,7 +91,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_WidthHeightRatioModeTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 300, HeightRatio = 0.5m }); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&heightratio=0.5&width=300", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&heightratio=0.5&width=300", urlString); } /// @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_HeightWidthRatioModeTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Height = 150, WidthRatio = 2 }); - Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&widthratio=2&height=150", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&widthratio=2&height=150", urlString); } /// @@ -160,7 +160,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); - Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.41,0.4275&heightratio=0.5962962962962962962962962963&width=200", urlString); } /// @@ -170,7 +170,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 270, Height = 161 }); - Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.41,0.4275&width=270&height=161", urlString); } /// From 6376a32131283dbd40a0f3bcfa5a1ec8769a2207 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 08:04:20 +0200 Subject: [PATCH 05/54] Use CommandParser to parse commands Co-authored-by: Ronald Barendse --- .../ImageProcessors/CropWebProcessor.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index e06f93a806..9f8a1139e6 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors CommandParser parser, CultureInfo culture) { - ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands); + ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands, parser, culture); if (cropCoordinates is null) { return image; @@ -56,24 +56,22 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return new Rectangle(topX, topY, cropWidth, cropHeight); } - private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands) + private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) { - if (!commands.TryGetValue(Crop, out var crop)) + float[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); + + if (crops.Length != 4) { return null; } - var crops = crop.Split(','); - return new ImageCropperCropCoordinates() { - X1 = ParseDecimal(crops[0]), - Y1 = ParseDecimal(crops[1]), - X2 = ParseDecimal(crops[2]), - Y2 = ParseDecimal(crops[3]) + X1 = crops[0], + Y1 = crops[1], + X2 = crops[2], + Y2 = crops[3] }; } - - private static decimal ParseDecimal(string decimalString) => decimal.Parse(decimalString, CultureInfo.InvariantCulture); } } From ba2e4cd760fd112a3f65d088ac2c0db94022a54a Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 08:08:41 +0200 Subject: [PATCH 06/54] Use decimal instead of float for coordinates --- src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 9f8a1139e6..83c90d21f4 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -58,8 +58,8 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) { - float[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); - + decimal[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); + if (crops.Length != 4) { return null; From 1814b70bafd8389bcf505d6d71f1506d055b9feb Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 08:13:49 +0200 Subject: [PATCH 07/54] Only remove the resize processor --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 775d36eaf5..688e18fd90 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -52,7 +52,7 @@ namespace Umbraco.Extensions .SetCache() .SetCacheHash() .AddProvider() - .ClearProcessors() // ImageSharp includes the processors by default, so we have to clear and re-add to control the order + .RemoveProcessor() // The Resize processor is added by default, so remove it to ensure that the crop processor runs first .AddProcessor() .AddProcessor() .AddProcessor() From cacef54ef77bdfe725aafc7af64a02048e51a2e7 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 09:04:07 +0200 Subject: [PATCH 08/54] Round to int instead of just converting --- .../ImageProcessors/CropWebProcessor.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 83c90d21f4..fec27a8591 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Globalization; using Microsoft.Extensions.Logging; @@ -43,11 +44,11 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates) { // Get coordinates of top left corner of the rectangle - var topX = decimal.ToInt32(width * coordinates.X1); - var topY = decimal.ToInt32(height * coordinates.Y1); + var topX = RoundToInt(width * coordinates.X1); + var topY = RoundToInt(height * coordinates.Y1); // Get coordinated of bottom right corner - var bottomX = decimal.ToInt32(width - (width * coordinates.X2)); - var bottomY = decimal.ToInt32(height - (height * coordinates.Y2)); + var bottomX = RoundToInt(width - (width * coordinates.X2)); + var bottomY = RoundToInt(height - (height * coordinates.Y2)); // Get width and height of crop var cropWidth = bottomX - topX; @@ -56,6 +57,8 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return new Rectangle(topX, topY, cropWidth, cropHeight); } + private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number)); + private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) { decimal[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); From c93fe0d2e77f134e160b33cef735a6cecad13dc6 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 09:08:26 +0200 Subject: [PATCH 09/54] Add method descriptions --- .../ImageProcessors/CropWebProcessor.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index fec27a8591..3fe4db8344 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -41,6 +41,13 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors public IEnumerable Commands { get; } = CropCommands; + /// + /// Gets a rectangle that is calculated from crop coordinates. + /// + /// The width of the image being cropped. + /// The height of the image being cropped. + /// Coordinate set to calculate rectangle from. + /// Rectangle with the position and sized described in coordinates. private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates) { // Get coordinates of top left corner of the rectangle @@ -57,8 +64,20 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return new Rectangle(topX, topY, cropWidth, cropHeight); } + /// + /// Converts a decimal to an int with rounding. + /// + /// Decimal to convert. + /// The decimal rounded to an int. private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number)); + /// + /// Gets the crop coordinates from the query string. + /// + /// Commands dictionary to parse the coordinates from. + /// Parser provided by imagesharp. + /// Culture to use for parsing. + /// Coordinates of the crop. private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) { decimal[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); From c255729be00a80737ebecde790eb2fa98c57cc1b Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Jul 2021 09:26:19 +0200 Subject: [PATCH 10/54] Change back to ClearProcessors --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 688e18fd90..368d6f5116 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -51,7 +51,7 @@ namespace Umbraco.Extensions }) .SetCache() .SetCacheHash() - .AddProvider() + .ClearProcessors() .RemoveProcessor() // The Resize processor is added by default, so remove it to ensure that the crop processor runs first .AddProcessor() .AddProcessor() From ca5af1e9a7bcef3aead07f486fa8c6b25cfce3fb Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 6 Aug 2021 12:56:49 +0200 Subject: [PATCH 11/54] Only add our custom CropWebProcessor to ImageSharp --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 368d6f5116..f6bb54ac98 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -51,12 +51,7 @@ namespace Umbraco.Extensions }) .SetCache() .SetCacheHash() - .ClearProcessors() - .RemoveProcessor() // The Resize processor is added by default, so remove it to ensure that the crop processor runs first - .AddProcessor() - .AddProcessor() - .AddProcessor() - .AddProcessor(); + .AddProcessor(); return services; } From 20007db7f2d2a99d085b8fc2fd90f915db65007a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 6 Aug 2021 12:59:23 +0200 Subject: [PATCH 12/54] Add cropmode support and align logic to ImageProcessors Crop processor --- .../ImageProcessors/CropWebProcessor.cs | 137 +++++++++--------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 3fe4db8344..d5e2adb2e5 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Globalization; using Microsoft.Extensions.Logging; @@ -7,93 +6,101 @@ using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Web; using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.Processors; -using ImageCropperCropCoordinates = Umbraco.Cms.Core.PropertyEditors.ValueConverters.ImageCropperValue.ImageCropperCropCoordinates; namespace Umbraco.Cms.Web.Common.ImageProcessors { + /// + /// Allows the cropping of images. + /// public class CropWebProcessor : IImageWebProcessor { /// - /// The command constant for the crop definition + /// The command constant for the crop definition. /// public const string Crop = "crop"; - public FormattedImage Process( - FormattedImage image, - ILogger logger, - IDictionary commands, - CommandParser parser, - CultureInfo culture) + /// + /// The command constant for the crop mode. + /// + public const string Mode = "cropmode"; + + + /// + public IEnumerable Commands { get; } = new[] { - ImageCropperCropCoordinates cropCoordinates = GetCropCoordinates(commands, parser, culture); - if (cropCoordinates is null) + Crop, + Mode + }; + + /// + public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary commands, CommandParser parser, CultureInfo culture) + { + if (GetCrop(commands, parser, culture) is RectangleF crop) { - return image; + Size size = image.Image.Size(); + + // Convert from percentages to pixels based on crop mode + if (GetMode(commands, parser, culture) is CropMode.Percentage) + { + // Fix for whole numbers + float percentageLeft = crop.Left > 1 ? crop.Left / 100 : crop.Left; + float percentageRight = crop.Right > 1 ? crop.Right / 100 : crop.Right; + float percentageTop = crop.Top > 1 ? crop.Top / 100 : crop.Top; + float percentageBottom = crop.Bottom > 1 ? crop.Bottom / 100 : crop.Bottom; + + // Work out the percentages + float left = percentageLeft * size.Width; + float top = percentageTop * size.Height; + float width = percentageRight < 1 ? (1 - percentageLeft - percentageRight) * size.Width : size.Width; + float height = percentageBottom < 1 ? (1 - percentageTop - percentageBottom) * size.Height : size.Height; + + crop = new RectangleF(left, top, width, height); + } + + // Round and validate crop rectangle + var rectangle = Rectangle.Round(crop); + if (rectangle.X < size.Width && rectangle.Y < size.Height) + { + if (rectangle.Width > (size.Width - rectangle.X)) + { + rectangle.Width = size.Width - rectangle.X; + } + + if (rectangle.Height > (size.Height - rectangle.Y)) + { + rectangle.Height = size.Height - rectangle.Y; + } + + image.Image.Mutate(x => x.Crop(rectangle)); + } } - Size size = image.Image.Size(); - Rectangle crop = GetCropRectangle(size.Width, size.Height, cropCoordinates); - image.Image.Mutate(x => x.Crop(crop)); return image; } - private static readonly IEnumerable CropCommands = new[] {Crop}; - - public IEnumerable Commands { get; } = CropCommands; - - /// - /// Gets a rectangle that is calculated from crop coordinates. - /// - /// The width of the image being cropped. - /// The height of the image being cropped. - /// Coordinate set to calculate rectangle from. - /// Rectangle with the position and sized described in coordinates. - private static Rectangle GetCropRectangle(int width, int height, ImageCropperCropCoordinates coordinates) + private static RectangleF? GetCrop(IDictionary commands, CommandParser parser, CultureInfo culture) { - // Get coordinates of top left corner of the rectangle - var topX = RoundToInt(width * coordinates.X1); - var topY = RoundToInt(height * coordinates.Y1); - // Get coordinated of bottom right corner - var bottomX = RoundToInt(width - (width * coordinates.X2)); - var bottomY = RoundToInt(height - (height * coordinates.Y2)); + float[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); - // Get width and height of crop - var cropWidth = bottomX - topX; - var cropHeight = bottomY - topY; - - return new Rectangle(topX, topY, cropWidth, cropHeight); + return (crops.Length != 4) ? null : new RectangleF(crops[0], crops[1], crops[2], crops[3]); } + private static CropMode GetMode(IDictionary commands, CommandParser parser, CultureInfo culture) + => parser.ParseValue(commands.GetValueOrDefault(Mode), culture); + } + + /// + /// Enumerated cop modes to apply to cropped images. + /// + public enum CropMode + { /// - /// Converts a decimal to an int with rounding. + /// Crops the image using the standard rectangle model of x, y, width, height. /// - /// Decimal to convert. - /// The decimal rounded to an int. - private static int RoundToInt(decimal number) => decimal.ToInt32(Math.Round(number)); - + Pixels, /// - /// Gets the crop coordinates from the query string. + /// Crops the image using percentages model left, top, right, bottom. /// - /// Commands dictionary to parse the coordinates from. - /// Parser provided by imagesharp. - /// Culture to use for parsing. - /// Coordinates of the crop. - private static ImageCropperCropCoordinates GetCropCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) - { - decimal[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); - - if (crops.Length != 4) - { - return null; - } - - return new ImageCropperCropCoordinates() - { - X1 = crops[0], - Y1 = crops[1], - X2 = crops[2], - Y2 = crops[3] - }; - } + Percentage } } From 760c15e5ce3a57d6a12a09aab3632fc34ed3a09a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 6 Aug 2021 12:59:56 +0200 Subject: [PATCH 13/54] Add cropmode paramerter back to tests --- .../Media/ImageSharpImageUrlGenerator.cs | 3 ++- .../Media/ImageSharpImageUrlGeneratorTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 0d5f5202b0..084dee0177 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Text; using Umbraco.Cms.Core.Media; @@ -61,6 +61,7 @@ namespace Umbraco.Cms.Infrastructure.Media imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); + imageProcessorUrl.Append("&cropmode=percentage"); } } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index fcf466411d..db46b6a827 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_CropAliasTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); } [Test] @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetBaseCropUrlFromModelTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); + Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); } /// @@ -81,7 +81,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_CropAliasHeightRatioModeTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, HeightRatio = 1 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&heightratio=1&width=100", urlString); + Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString); } /// From 4b85edd1d91db27f65bcf4db790780c9a6758c32 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 6 Aug 2021 14:22:27 +0200 Subject: [PATCH 14/54] Update ImageSharpImageUrlGenerator --- .../Media/ImageSharpImageUrlGenerator.cs | 117 ++++++++++++------ .../UmbracoBuilder.ImageSharp.cs | 2 - 2 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 084dee0177..d556b0f314 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -9,59 +9,102 @@ namespace Umbraco.Cms.Infrastructure.Media { public class ImageSharpImageUrlGenerator : IImageUrlGenerator { - public IEnumerable SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png" }; + public IEnumerable SupportedImageFileTypes => new[] + { + "jpeg", + "jpg", + "gif", + "bmp", + "png" + }; public string GetImageUrl(ImageUrlGenerationOptions options) { - if (options == null) return null; + if (options == null) + { + return null; + } - var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty); + var imageUrl = new StringBuilder(options.ImageUrl); - if (options.FocalPoint != null) AppendFocalPoint(imageProcessorUrl, options); - else if (options.Crop != null) AppendCrop(imageProcessorUrl, options); - else if (options.DefaultCrop) imageProcessorUrl.Append("?anchor=center&mode=crop"); + if (options.FocalPoint != null) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "?rxy={0},{1}", options.FocalPoint.Top, options.FocalPoint.Left); + } + else if (options.Crop != null) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "?crop={0},{1},{2},{3}&cropmode=percentage", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); + } + else if (options.DefaultCrop) + { + imageUrl.Append("?anchor=center&mode=crop"); + } else { - imageProcessorUrl.Append("?mode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLower()); + imageUrl.Append("?mode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLowerInvariant()); - if (options.ImageCropAnchor != null) imageProcessorUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToString().ToLower()); + if (options.ImageCropAnchor != null) + { + imageUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToString().ToLowerInvariant()); + } } var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&format="); - //Only put quality here, if we don't have a format specified. - //Otherwise we need to put quality at the end to avoid it being overridden by the format. - if (options.Quality.HasValue && hasFormat == false) imageProcessorUrl.Append("&quality=").Append(options.Quality); - if (options.HeightRatio.HasValue) imageProcessorUrl.Append("&heightratio=").Append(options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture)); - if (options.WidthRatio.HasValue) imageProcessorUrl.Append("&widthratio=").Append(options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture)); - if (options.Width.HasValue) imageProcessorUrl.Append("&width=").Append(options.Width); - if (options.Height.HasValue) imageProcessorUrl.Append("&height=").Append(options.Height); - if (options.UpScale == false) imageProcessorUrl.Append("&upscale=false"); - if (!string.IsNullOrWhiteSpace(options.AnimationProcessMode)) imageProcessorUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode); - if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) imageProcessorUrl.Append(options.FurtherOptions); + // Only put quality here, if we don't have a format specified. + // Otherwise we need to put quality at the end to avoid it being overridden by the format. + if (options.Quality.HasValue && hasFormat == false) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&quality={0}", options.Quality.Value); + } - //If furtherOptions contains a format, we need to put the quality after the format. - if (options.Quality.HasValue && hasFormat) imageProcessorUrl.Append("&quality=").Append(options.Quality); - if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) imageProcessorUrl.Append("&rnd=").Append(options.CacheBusterValue); + if (options.HeightRatio.HasValue) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&heightratio={0}", options.HeightRatio.Value); + } - return imageProcessorUrl.ToString(); - } + if (options.WidthRatio.HasValue) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&widthratio={0}", options.WidthRatio.Value); + } - private void AppendFocalPoint(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) - { - imageProcessorUrl.Append("?rxy="); - imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); - } + if (options.Width.HasValue) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&width={0}", options.Width.Value); + } - private void AppendCrop(StringBuilder imageProcessorUrl, ImageUrlGenerationOptions options) - { - imageProcessorUrl.Append("?crop="); - imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); - imageProcessorUrl.Append("&cropmode=percentage"); + if (options.Height.HasValue) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&height={0}", options.Height.Value); + } + + if (options.UpScale == false) + { + imageUrl.Append("&upscale=false"); + } + + if (!string.IsNullOrWhiteSpace(options.AnimationProcessMode)) + { + imageUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode); + } + + if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) + { + imageUrl.Append(options.FurtherOptions); + } + + // If furtherOptions contains a format, we need to put the quality after the format. + if (options.Quality.HasValue && hasFormat) + { + imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&quality={0}", options.Quality.Value); + } + + if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) + { + imageUrl.Append("&rnd=").Append(options.CacheBusterValue); + } + + return imageUrl.ToString(); } } } diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index f6bb54ac98..3d753975fe 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -2,12 +2,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Web.Caching; using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.DependencyInjection; using SixLabors.ImageSharp.Web.Processors; -using SixLabors.ImageSharp.Web.Providers; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Web.Common.ImageProcessors; From 6430b36a226e90a8061c440a0ef5a83c56236a86 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 6 Aug 2021 14:44:16 +0200 Subject: [PATCH 15/54] Update resize mode and anchor query string parameters --- .../Media/ImageSharpImageUrlGenerator.cs | 6 ++--- .../Media/ImageSharpImageUrlGeneratorTests.cs | 26 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index d556b0f314..d72ee2b4eb 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -37,15 +37,15 @@ namespace Umbraco.Cms.Infrastructure.Media } else if (options.DefaultCrop) { - imageUrl.Append("?anchor=center&mode=crop"); + imageUrl.Append("?ranchor=center&rmode=crop"); } else { - imageUrl.Append("?mode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLowerInvariant()); + imageUrl.Append("?rmode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLowerInvariant()); if (options.ImageCropAnchor != null) { - imageUrl.Append("&anchor=").Append(options.ImageCropAnchor.ToString().ToLowerInvariant()); + imageUrl.Append("&ranchor=").Append(options.ImageCropAnchor.ToString().ToLowerInvariant()); } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index db46b6a827..c57c53b52e 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrlEmptyTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null)); - Assert.AreEqual("?mode=crop", urlString); + Assert.AreEqual("?rmode=crop", urlString); } /// @@ -116,11 +116,11 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media var urlStringMax = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Max, Width = 300, Height = 150 }); var urlStringStretch = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Stretch, Width = 300, Height = 150 }); - Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin); - Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad); - Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad); - Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlStringMax); - Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch); + Assert.AreEqual(MediaPath + "?rmode=min&width=300&height=150", urlStringMin); + Assert.AreEqual(MediaPath + "?rmode=boxpad&width=300&height=150", urlStringBoxPad); + Assert.AreEqual(MediaPath + "?rmode=pad&width=300&height=150", urlStringPad); + Assert.AreEqual(MediaPath + "?rmode=max&width=300&height=150", urlStringMax); + Assert.AreEqual(MediaPath + "?rmode=stretch&width=300&height=150", urlStringStretch); } /// @@ -130,7 +130,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_UploadTypeTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Crop, ImageCropAnchor = ImageCropAnchor.Center, Width = 100, Height = 270 }); - Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString); + Assert.AreEqual(MediaPath + "?rmode=crop&ranchor=center&width=100&height=270", urlString); } /// @@ -140,7 +140,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreferFocalPointCenter() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 }); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString); + Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&width=300&height=150", urlString); } /// @@ -150,7 +150,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); + Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); } /// @@ -180,7 +180,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m }); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString); + Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString); } /// @@ -190,7 +190,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_WidthOnlyParameter() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 }); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString); + Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&width=200", urlString); } /// @@ -200,7 +200,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_HeightOnlyParameter() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 }); - Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString); + Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&height=200", urlString); } /// @@ -210,7 +210,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_BackgroundColorParameter() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = ImageCropMode.Pad, Width = 400, Height = 400, FurtherOptions = "&bgcolor=fff" }); - Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString); + Assert.AreEqual(MediaPath + "?rmode=pad&width=400&height=400&bgcolor=fff", urlString); } } } From fd93248d112ad93d3dbcab9efa0d2bcb1c052581 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 9 Aug 2021 11:59:23 +0200 Subject: [PATCH 16/54] Move CropMode enum into seperate file --- .../ImageProcessors/CropMode.cs | 18 ++++++++++++++++++ .../ImageProcessors/CropWebProcessor.cs | 15 --------------- 2 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Web.Common/ImageProcessors/CropMode.cs diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs b/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs new file mode 100644 index 0000000000..6e08c6e05c --- /dev/null +++ b/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Cms.Web.Common.ImageProcessors +{ + /// + /// Represents the mode used to calculate a crop. + /// + public enum CropMode + { + /// + /// Crops the image using the standard rectangle model of x, y, width, height. + /// + Pixels, + + /// + /// Crops the image using the percentages model of left, top, right, bottom. + /// + Percentage + } +} diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index d5e2adb2e5..828154447c 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -88,19 +88,4 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors private static CropMode GetMode(IDictionary commands, CommandParser parser, CultureInfo culture) => parser.ParseValue(commands.GetValueOrDefault(Mode), culture); } - - /// - /// Enumerated cop modes to apply to cropped images. - /// - public enum CropMode - { - /// - /// Crops the image using the standard rectangle model of x, y, width, height. - /// - Pixels, - /// - /// Crops the image using percentages model left, top, right, bottom. - /// - Percentage - } } From 5cdcd021fa5afc9e3bcd016cb8bf9c84036ae484 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 9 Aug 2021 12:00:37 +0200 Subject: [PATCH 17/54] Remove support for whole percentage crop values --- .../ImageProcessors/CropWebProcessor.cs | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 828154447c..c523d6e424 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -38,40 +38,34 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors if (GetCrop(commands, parser, culture) is RectangleF crop) { Size size = image.Image.Size(); + CropMode mode = GetMode(commands, parser, culture); - // Convert from percentages to pixels based on crop mode - if (GetMode(commands, parser, culture) is CropMode.Percentage) + if (mode == CropMode.Percentage) { - // Fix for whole numbers - float percentageLeft = crop.Left > 1 ? crop.Left / 100 : crop.Left; - float percentageRight = crop.Right > 1 ? crop.Right / 100 : crop.Right; - float percentageTop = crop.Top > 1 ? crop.Top / 100 : crop.Top; - float percentageBottom = crop.Bottom > 1 ? crop.Bottom / 100 : crop.Bottom; + // Convert the percentage based model of left, top, right, bottom to x, y, width, height + float x = crop.Left * size.Width; + float y = crop.Top * size.Height; + float width = crop.Right < 1 ? (1 - crop.Left - crop.Right) * size.Width : size.Width; + float height = crop.Bottom < 1 ? (1 - crop.Top - crop.Bottom) * size.Height : size.Height; - // Work out the percentages - float left = percentageLeft * size.Width; - float top = percentageTop * size.Height; - float width = percentageRight < 1 ? (1 - percentageLeft - percentageRight) * size.Width : size.Width; - float height = percentageBottom < 1 ? (1 - percentageTop - percentageBottom) * size.Height : size.Height; - - crop = new RectangleF(left, top, width, height); + crop = new RectangleF(x, y, width, height); } - // Round and validate crop rectangle - var rectangle = Rectangle.Round(crop); - if (rectangle.X < size.Width && rectangle.Y < size.Height) + // Round and validate/clamp crop rectangle + var cropRectangle = Rectangle.Round(crop); + if (cropRectangle.X < size.Width && cropRectangle.Y < size.Height) { - if (rectangle.Width > (size.Width - rectangle.X)) + if (cropRectangle.Width > (size.Width - cropRectangle.X)) { - rectangle.Width = size.Width - rectangle.X; + cropRectangle.Width = size.Width - cropRectangle.X; } - if (rectangle.Height > (size.Height - rectangle.Y)) + if (cropRectangle.Height > (size.Height - cropRectangle.Y)) { - rectangle.Height = size.Height - rectangle.Y; + cropRectangle.Height = size.Height - cropRectangle.Y; } - image.Image.Mutate(x => x.Crop(rectangle)); + image.Image.Mutate(x => x.Crop(cropRectangle)); } } @@ -80,9 +74,14 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors private static RectangleF? GetCrop(IDictionary commands, CommandParser parser, CultureInfo culture) { - float[] crops = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); + float[] coordinates = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); - return (crops.Length != 4) ? null : new RectangleF(crops[0], crops[1], crops[2], crops[3]); + if (coordinates.Length != 4) + { + return null; + } + + return new RectangleF(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); } private static CropMode GetMode(IDictionary commands, CommandParser parser, CultureInfo culture) From c21656aff2d4d8ade73f5c8f897e7e9aacdae056 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 9 Aug 2021 14:52:07 +0200 Subject: [PATCH 18/54] Change processor order and fix crop percentage calculation --- .../UmbracoBuilder.ImageSharp.cs | 4 ++- .../ImageProcessors/CropWebProcessor.cs | 35 ++++++------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 3d753975fe..ec14091092 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -49,7 +49,9 @@ namespace Umbraco.Extensions }) .SetCache() .SetCacheHash() - .AddProcessor(); + .RemoveProcessor() + .AddProcessor() + .AddProcessor(); return services; } diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index c523d6e424..c51af9a532 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -37,36 +37,23 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors { if (GetCrop(commands, parser, culture) is RectangleF crop) { - Size size = image.Image.Size(); - CropMode mode = GetMode(commands, parser, culture); - - if (mode == CropMode.Percentage) + if (GetMode(commands, parser, culture) == CropMode.Percentage) { // Convert the percentage based model of left, top, right, bottom to x, y, width, height - float x = crop.Left * size.Width; - float y = crop.Top * size.Height; - float width = crop.Right < 1 ? (1 - crop.Left - crop.Right) * size.Width : size.Width; - float height = crop.Bottom < 1 ? (1 - crop.Top - crop.Bottom) * size.Height : size.Height; + int sourceWidth = image.Image.Width; + int sourceHeight = image.Image.Height; - crop = new RectangleF(x, y, width, height); + float left = crop.Left * sourceWidth; + float top = crop.Top * sourceHeight; + float width = sourceWidth - (sourceWidth * crop.Width) - left; + float height = sourceHeight - (sourceHeight * crop.Height) - top; + + crop = new RectangleF(left, top, width, height); } - // Round and validate/clamp crop rectangle var cropRectangle = Rectangle.Round(crop); - if (cropRectangle.X < size.Width && cropRectangle.Y < size.Height) - { - if (cropRectangle.Width > (size.Width - cropRectangle.X)) - { - cropRectangle.Width = size.Width - cropRectangle.X; - } - - if (cropRectangle.Height > (size.Height - cropRectangle.Y)) - { - cropRectangle.Height = size.Height - cropRectangle.Y; - } - - image.Image.Mutate(x => x.Crop(cropRectangle)); - } + + image.Image.Mutate(x => x.Crop(cropRectangle)); } return image; From 50d8e74b5bb3fb8af09a49729d855f2ea26ed6c1 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 9 Aug 2021 15:52:55 +0200 Subject: [PATCH 19/54] Obsolete unsupported image URL generation options and update ImageSharp implementation --- .../Models/ImageUrlGenerationOptions.cs | 45 +++++-- .../Media/ImageSharpImageUrlGenerator.cs | 87 +++++-------- .../Media/ImageSharpImageUrlGeneratorTests.cs | 77 ++--------- .../Umbraco.Web.Common/ImageCropperTest.cs | 120 ++++++++---------- .../ImageCropperTemplateCoreExtensions.cs | 45 ++++--- 5 files changed, 159 insertions(+), 215 deletions(-) diff --git a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs index 99f42bbefa..3118162786 100644 --- a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs +++ b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs @@ -1,55 +1,71 @@ -namespace Umbraco.Cms.Core.Models +using System; + +namespace Umbraco.Cms.Core.Models { /// - /// These are options that are passed to the IImageUrlGenerator implementation to determine - /// the propery URL that is needed + /// These are options that are passed to the IImageUrlGenerator implementation to determine the URL that is generated. /// public class ImageUrlGenerationOptions { - public ImageUrlGenerationOptions (string imageUrl) - { - ImageUrl = imageUrl; - } + public ImageUrlGenerationOptions(string imageUrl) => ImageUrl = imageUrl; public string ImageUrl { get; } + public int? Width { get; set; } + public int? Height { get; set; } + + [Obsolete("This property is unsupported by the default implementation, manually calculate the width based on the height instead.")] public decimal? WidthRatio { get; set; } + + [Obsolete("This property is unsupported by the default implementation, manually calculate the height based on the width instead.")] public decimal? HeightRatio { get; set; } + public int? Quality { get; set; } + public ImageCropMode? ImageCropMode { get; set; } + public ImageCropAnchor? ImageCropAnchor { get; set; } + + [Obsolete("Images are already cropped from the center to the specified width/height by default.")] public bool DefaultCrop { get; set; } + public FocalPointPosition FocalPoint { get; set; } + public CropCoordinates Crop { get; set; } + public string CacheBusterValue { get; set; } + public string FurtherOptions { get; set; } + + [Obsolete("This property is unsupported by the default implementation, images should always be resized to the specified dimensions (within the configured maximums) to prevent different sizes depending on the source image.")] public bool UpScale { get; set; } = true; + + [Obsolete("This property is unsupported by the default implementation, all frames should be processed by default.")] public string AnimationProcessMode { get; set; } /// - /// The focal point position, in whatever units the registered IImageUrlGenerator uses, - /// typically a percentage of the total image from 0.0 to 1.0. + /// The focal point position, in whatever units the registered IImageUrlGenerator uses, typically a percentage of the total image from 0.0 to 1.0. /// public class FocalPointPosition { - public FocalPointPosition (decimal top, decimal left) + public FocalPointPosition(decimal top, decimal left) { Left = left; Top = top; } public decimal Left { get; } + public decimal Top { get; } } /// - /// The bounds of the crop within the original image, in whatever units the registered - /// IImageUrlGenerator uses, typically a percentage between 0 and 100. + /// The bounds of the crop within the original image, in whatever units the registered IImageUrlGenerator uses, typically a percentage between 0.0 and 1.0. /// public class CropCoordinates { - public CropCoordinates (decimal x1, decimal y1, decimal x2, decimal y2) + public CropCoordinates(decimal x1, decimal y1, decimal x2, decimal y2) { X1 = x1; Y1 = y1; @@ -58,8 +74,11 @@ } public decimal X1 { get; } + public decimal Y1 { get; } + public decimal X2 { get; } + public decimal Y2 { get; } } } diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index d72ee2b4eb..8e0a8eb350 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -1,22 +1,19 @@ +using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; +using SixLabors.ImageSharp; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; -using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Media { public class ImageSharpImageUrlGenerator : IImageUrlGenerator { - public IEnumerable SupportedImageFileTypes => new[] - { - "jpeg", - "jpg", - "gif", - "bmp", - "png" - }; + private static readonly string[] s_supportedImageFileTypes = Configuration.Default.ImageFormats.SelectMany(f => f.FileExtensions).ToArray(); + + public IEnumerable SupportedImageFileTypes { get; } = s_supportedImageFileTypes; public string GetImageUrl(ImageUrlGenerationOptions options) { @@ -27,84 +24,66 @@ namespace Umbraco.Cms.Infrastructure.Media var imageUrl = new StringBuilder(options.ImageUrl); + bool queryStringHasStarted = false; + void AppendQueryString(string value) + { + imageUrl.Append(queryStringHasStarted ? '&' : '?'); + queryStringHasStarted = true; + + imageUrl.Append(value); + } + void AddQueryString(string key, params IConvertible[] values) + => AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture)))); + if (options.FocalPoint != null) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "?rxy={0},{1}", options.FocalPoint.Top, options.FocalPoint.Left); - } - else if (options.Crop != null) - { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "?crop={0},{1},{2},{3}&cropmode=percentage", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); - } - else if (options.DefaultCrop) - { - imageUrl.Append("?ranchor=center&rmode=crop"); - } - else - { - imageUrl.Append("?rmode=").Append((options.ImageCropMode ?? ImageCropMode.Crop).ToString().ToLowerInvariant()); - - if (options.ImageCropAnchor != null) - { - imageUrl.Append("&ranchor=").Append(options.ImageCropAnchor.ToString().ToLowerInvariant()); - } + AddQueryString("rxy", options.FocalPoint.Top, options.FocalPoint.Left); } - var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&format="); - - // Only put quality here, if we don't have a format specified. - // Otherwise we need to put quality at the end to avoid it being overridden by the format. - if (options.Quality.HasValue && hasFormat == false) + if (options.Crop != null) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&quality={0}", options.Quality.Value); + AddQueryString("crop", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); + AddQueryString("cropmode", "percentage"); } - if (options.HeightRatio.HasValue) + if (options.ImageCropMode.HasValue) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&heightratio={0}", options.HeightRatio.Value); + AddQueryString("rmode", options.ImageCropMode.Value.ToString().ToLowerInvariant()); } - if (options.WidthRatio.HasValue) + if (options.ImageCropAnchor.HasValue) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&widthratio={0}", options.WidthRatio.Value); + AddQueryString("ranchor", options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); } if (options.Width.HasValue) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&width={0}", options.Width.Value); + AddQueryString("width", options.Width.Value); } if (options.Height.HasValue) { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&height={0}", options.Height.Value); + AddQueryString("height", options.Height.Value); } - if (options.UpScale == false) + if (options.Quality.HasValue) { - imageUrl.Append("&upscale=false"); - } - - if (!string.IsNullOrWhiteSpace(options.AnimationProcessMode)) - { - imageUrl.Append("&animationprocessmode=").Append(options.AnimationProcessMode); + AddQueryString("quality", options.Quality.Value); } if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) { - imageUrl.Append(options.FurtherOptions); - } - - // If furtherOptions contains a format, we need to put the quality after the format. - if (options.Quality.HasValue && hasFormat) - { - imageUrl.AppendFormat(CultureInfo.InvariantCulture, "&quality={0}", options.Quality.Value); + AppendQueryString(options.FurtherOptions.TrimStart('?', '&')); } if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) { - imageUrl.Append("&rnd=").Append(options.CacheBusterValue); + AddQueryString("rnd", options.CacheBusterValue); } return imageUrl.ToString(); } + + } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index c57c53b52e..f6d87ef5a1 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media } /// - /// Test that if a crop alias has been specified that doesn't exist the method returns null + /// Test that if options is null, the generated image URL is also null. /// [Test] public void GetCropUrlNullTest() @@ -55,13 +55,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media } /// - /// Test that if a crop alias has been specified that doesn't exist the method returns null + /// Test that if the image URL is null, the generated image URL is empty. /// [Test] public void GetCropUrlEmptyTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null)); - Assert.AreEqual("?rmode=crop", urlString); + Assert.AreEqual(string.Empty, urlString); } /// @@ -74,35 +74,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); } - /// - /// Test the height ratio mode with predefined crop dimensions - /// - [Test] - public void GetCropUrl_CropAliasHeightRatioModeTest() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, HeightRatio = 1 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString); - } - - /// - /// Test the height ratio mode with manual width/height dimensions - /// - [Test] - public void GetCropUrl_WidthHeightRatioModeTest() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 300, HeightRatio = 0.5m }); - Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&heightratio=0.5&width=300", urlString); - } - - /// - /// Test the height ratio mode with width/height dimensions - /// - [Test] - public void GetCropUrl_HeightWidthRatioModeTest() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Height = 150, WidthRatio = 2 }); - Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&widthratio=2&height=150", urlString); - } /// /// Test that if Crop mode is specified as anything other than Crop the image doesn't use the crop @@ -139,28 +110,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media [Test] public void GetCropUrl_PreferFocalPointCenter() { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 }); - Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&width=300&height=150", urlString); - } - - /// - /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point in centre when a width parameter is passed - /// - [Test] - public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); - Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString); - } - - /// - /// Test to check if height ratio is returned for a predefined crop without coordinates and focal point is custom when a width parameter is passed - /// - [Test] - public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m }); - Assert.AreEqual(MediaPath + "?rxy=0.41,0.4275&heightratio=0.5962962962962962962962962963&width=200", urlString); + var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 300, Height = 150 }); + Assert.AreEqual(MediaPath + "?width=300&height=150", urlString); } /// @@ -173,24 +124,14 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media Assert.AreEqual(MediaPath + "?rxy=0.41,0.4275&width=270&height=161", urlString); } - /// - /// Test to check if width ratio is returned for a predefined crop without coordinates and focal point in centre when a height parameter is passed - /// - [Test] - public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight() - { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m }); - Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString); - } - /// /// Test to check result when only a width parameter is passed, effectivly a resize only /// [Test] public void GetCropUrl_WidthOnlyParameter() { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 }); - Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&width=200", urlString); + var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 200 }); + Assert.AreEqual(MediaPath + "?width=200", urlString); } /// @@ -199,8 +140,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media [Test] public void GetCropUrl_HeightOnlyParameter() { - var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 }); - Assert.AreEqual(MediaPath + "?ranchor=center&rmode=crop&height=200", urlString); + var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Height = 200 }); + Assert.AreEqual(MediaPath + "?height=200", urlString); } /// diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs index 1873b30c99..16b2268a47 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs @@ -1,8 +1,10 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -130,21 +132,21 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common public void GetCropUrl_WidthHeightTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300); - Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300", urlString); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300", urlString); } [Test] public void GetCropUrl_FocalPointTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true); - Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=100&h=100", urlString); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=100&h=100", urlString); } [Test] public void GetCropUrlFurtherOptionsTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff"); - Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); } /// @@ -175,7 +177,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common public void GetCropUrl_CropAliasHeightRatioModeTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode: ImageCropRatioMode.Height); - Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&hr=1&w=100", urlString); + Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } /// @@ -185,7 +187,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common public void GetCropUrl_WidthHeightRatioModeTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Height); - Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&hr=0.5&w=300", urlString); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString); } /// @@ -195,7 +197,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common public void GetCropUrl_HeightWidthRatioModeTest() { var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width); - Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&wr=2&h=150", urlString); + Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString); } /// @@ -236,7 +238,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint: true); - Assert.AreEqual(MediaPath + "?m=defaultcrop&w=300&h=150", urlString); + Assert.AreEqual(MediaPath + "?w=300&h=150", urlString); } /// @@ -248,7 +250,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200); - Assert.AreEqual(MediaPath + "?m=defaultcrop&hr=0.5962962962962962962962962963&w=200", urlString); + Assert.AreEqual(MediaPath + "?w=200&h=119", urlString); } /// @@ -260,7 +262,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200); - Assert.AreEqual(MediaPath + "?f=0.41x0.4275&hr=0.5962962962962962962962962963&w=200", urlString); + Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=200&h=119", urlString); } /// @@ -272,7 +274,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true); - Assert.AreEqual(MediaPath + "?f=0.41x0.4275&w=270&h=161", urlString); + Assert.AreEqual(MediaPath + "?f=0.41,0.4275&w=270&h=161", urlString); } /// @@ -284,7 +286,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", height: 200); - Assert.AreEqual(MediaPath + "?m=defaultcrop&wr=1.6770186335403726708074534161&h=200", urlString); + Assert.AreEqual(MediaPath + "?w=335&h=200", urlString); } /// @@ -296,7 +298,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 200); - Assert.AreEqual(MediaPath + "?m=defaultcrop&w=200", urlString); + Assert.AreEqual(MediaPath + "?w=200", urlString); } /// @@ -308,7 +310,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, height: 200); - Assert.AreEqual(MediaPath + "?m=defaultcrop&h=200", urlString); + Assert.AreEqual(MediaPath + "?h=200", urlString); } /// @@ -325,92 +327,82 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common internal class TestImageUrlGenerator : IImageUrlGenerator { - public IEnumerable SupportedImageFileTypes => new[] { "jpeg", "jpg", "gif", "bmp", "png", "tiff", "tif" }; + public IEnumerable SupportedImageFileTypes => new[] + { + "jpeg", + "jpg", + "gif", + "bmp", + "png", + "tiff", + "tif" + }; public string GetImageUrl(ImageUrlGenerationOptions options) { - var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty); + if (options == null) + { + return null; + } + + var imageUrl = new StringBuilder(options.ImageUrl); + + bool queryStringHasStarted = false; + void AppendQueryString(string value) + { + imageUrl.Append(queryStringHasStarted ? '&' : '?'); + queryStringHasStarted = true; + + imageUrl.Append(value); + } + void AddQueryString(string key, params IConvertible[] values) + => AppendQueryString(key + '=' + string.Join(",", values.Select(x => x.ToString(CultureInfo.InvariantCulture)))); if (options.FocalPoint != null) { - imageProcessorUrl.Append("?f="); - imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture)); - imageProcessorUrl.Append("x"); - imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture)); + AddQueryString("f", options.FocalPoint.Top, options.FocalPoint.Left); } else if (options.Crop != null) { - imageProcessorUrl.Append("?c="); - imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(","); - imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture)); - } - else if (options.DefaultCrop) - { - imageProcessorUrl.Append("?m=defaultcrop"); - } - else - { - imageProcessorUrl.Append("?m=" + options.ImageCropMode.ToString().ToLower()); - if (options.ImageCropAnchor != null) - { - imageProcessorUrl.Append("&a=" + options.ImageCropAnchor.ToString().ToLower()); - } + AddQueryString("c", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); } - var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&f="); - if (options.Quality != null && hasFormat == false) + if (options.ImageCropMode.HasValue) { - imageProcessorUrl.Append("&q=" + options.Quality); + AddQueryString("m", options.ImageCropMode.Value.ToString().ToLowerInvariant()); } - if (options.HeightRatio != null) + if (options.ImageCropAnchor.HasValue) { - imageProcessorUrl.Append("&hr=" + options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture)); - } - - if (options.WidthRatio != null) - { - imageProcessorUrl.Append("&wr=" + options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture)); + AddQueryString("a", options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); } if (options.Width != null) { - imageProcessorUrl.Append("&w=" + options.Width); + AddQueryString("w", options.Width.Value); } if (options.Height != null) { - imageProcessorUrl.Append("&h=" + options.Height); + AddQueryString("h", options.Height.Value); } - - if (options.UpScale == false) + + if (options.Quality.HasValue) { - imageProcessorUrl.Append("&u=no"); - } - - if (options.AnimationProcessMode != null) - { - imageProcessorUrl.Append("&apm=" + options.AnimationProcessMode); + AddQueryString("q", options.Quality.Value); } if (options.FurtherOptions != null) { - imageProcessorUrl.Append(options.FurtherOptions); - } - - if (options.Quality != null && hasFormat) - { - imageProcessorUrl.Append("&q=" + options.Quality); + AppendQueryString(options.FurtherOptions.TrimStart('?', '&')); } if (options.CacheBusterValue != null) { - imageProcessorUrl.Append("&r=").Append(options.CacheBusterValue); + AddQueryString("r", options.CacheBusterValue); } - return imageProcessorUrl.ToString(); + return imageUrl.ToString(); } } } diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index 879a70cdb5..27de0d22b2 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using Newtonsoft.Json.Linq; using Umbraco.Cms.Core; @@ -449,16 +449,17 @@ namespace Umbraco.Extensions height = crop.Height; } - // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a width parameter has been passed we can get the crop ratio for the height - if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width != null && height == null) + // Calculate missing dimension if a predefined crop has been specified, there are no coordinates and no ratio mode + if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null) { - options.HeightRatio = (decimal)crop.Height / crop.Width; - } - - // If a predefined crop has been specified & there are no coordinates & no ratio mode, but a height parameter has been passed we can get the crop ratio for the width - if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null && width == null && height != null) - { - options.WidthRatio = (decimal)crop.Width / crop.Height; + if (width != null && height == null) + { + height = (int)MathF.Round(width.Value * ((float)crop.Height / crop.Width)); + } + else if (width == null && height != null) + { + width = (int)MathF.Round(height.Value * ((float)crop.Width / crop.Height)); + } } } else @@ -477,16 +478,28 @@ namespace Umbraco.Extensions if (ratioMode == ImageCropRatioMode.Width && height != null) { - // if only height specified then assume a square - if (width == null) width = height; - options.WidthRatio = (decimal)width / (decimal)height; + // If only height specified then assume a square + if (width == null) + { + options.Width = height; + } + else + { + options.Width = (int)MathF.Round(height.Value * ((float)width.Value / height.Value)); + } } if (ratioMode == ImageCropRatioMode.Height && width != null) { - // if only width specified then assume a square - if (height == null) height = width; - options.HeightRatio = (decimal)height / (decimal)width; + // If only width specified then assume a square + if (height == null) + { + options.Height = width; + } + else + { + options.Height = (int)MathF.Round(width.Value * ((float)height.Value / width.Value)); + } } options.UpScale = upScale; From 0b9f1f4f868b08c25eec717fe41417814d079af7 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 11:23:37 +0200 Subject: [PATCH 20/54] Remove usage of obsolete image URL generation properties (width/height ratio, default crop, upscale and animation process mode) --- .../ValueConverters/ImageCropperValue.cs | 4 +- .../Controllers/BackOfficeServerVariables.cs | 2 +- .../ImageUrlGeneratorController.cs | 21 +- .../Controllers/ImagesController.cs | 45 +-- .../FriendlyImageCropperTemplateExtensions.cs | 249 ++++---------- .../ImageCropperTemplateCoreExtensions.cs | 315 ++++++------------ .../Extensions/UrlHelperExtensions.cs | 12 +- .../resources/imageurlgenerator.resource.js | 6 +- .../common/services/mediahelper.service.js | 26 +- .../src/common/services/tinymce.service.js | 20 +- 10 files changed, 218 insertions(+), 482 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 32101d6cf7..09e080e0b0 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -77,7 +77,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters } else { - return new ImageUrlGenerationOptions(url) { DefaultCrop = true }; + return new ImageUrlGenerationOptions(url); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index c3fb203ec1..c822b61d67 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -365,7 +365,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "imageUrlGeneratorApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetCropUrl(null, null, null, null, null)) + controller => controller.GetCropUrl(null, null, null, null)) }, { "elementTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs index 1d72c80ad8..7546fdf38d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Web.Common.Attributes; using Constants = Umbraco.Cms.Core.Constants; @@ -21,20 +21,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { private readonly IImageUrlGenerator _imageUrlGenerator; - public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) - { - _imageUrlGenerator = imageUrlGenerator; - } + public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) => _imageUrlGenerator = imageUrlGenerator; - public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null, string animationProcessMode = null) + public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null) => _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath) { - return _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath) - { - Width = width, - Height = height, - ImageCropMode = imageCropMode, - AnimationProcessMode = animationProcessMode - }); - } + Width = width, + Height = height, + ImageCropMode = imageCropMode + }); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 24135bcbe6..00a18ec8b7 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.IO; @@ -76,9 +76,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null; var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) { - UpScale = false, Width = width, - AnimationProcessMode = "first", ImageCropMode = ImageCropMode.Max, CacheBusterValue = rnd }); @@ -94,9 +92,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - /// /// - /// /// /// /// If there is no media, image property or image file is found then this will return not found. @@ -106,9 +102,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers int? height = null, decimal? focalPointLeft = null, decimal? focalPointTop = null, - string animationProcessMode = "first", ImageCropMode mode = ImageCropMode.Max, - bool upscale = false, string cacheBusterValue = "", decimal? cropX1 = null, decimal? cropX2 = null, @@ -116,45 +110,24 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers decimal? cropY2 = null ) { - - var options = new ImageUrlGenerationOptions(imagePath) { - AnimationProcessMode = animationProcessMode, - CacheBusterValue = cacheBusterValue, + Width = width, Height = height, ImageCropMode = mode, - UpScale = upscale, - Width = width, - Crop = (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue) ? new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value) : null, - FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.GetValueOrDefault(0.5m), focalPointLeft.GetValueOrDefault(0.5m)), + CacheBusterValue = cacheBusterValue }; + if (focalPointLeft.HasValue && focalPointTop.HasValue) { - options.FocalPoint = - new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.Value, focalPointLeft.Value); + options.FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.Value, focalPointLeft.Value); + } + else if (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue) + { + options.Crop = new ImageUrlGenerationOptions.CropCoordinates(cropX1.Value, cropY1.Value, cropX2.Value, cropY2.Value); } return _imageUrlGenerator.GetImageUrl(options); } - - public class FocalPointPositionModel - { - public decimal Left { get; set; } - public decimal Top { get; set; } - } - - /// - /// The bounds of the crop within the original image, in whatever units the registered - /// IImageUrlGenerator uses, typically a percentage between 0 and 100. - /// - public class CropCoordinatesModel - { - - public decimal X1 { get; set; } - public decimal Y1 { get; set; } - public decimal X2 { get; set;} - public decimal Y2 { get; set;} - } } } diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs index 94e24a5027..19090ee12e 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; @@ -11,25 +11,17 @@ namespace Umbraco.Extensions { public static class FriendlyImageCropperTemplateExtensions { - private static IImageUrlGenerator ImageUrlGenerator { get; } = - StaticServiceProvider.Instance.GetRequiredService(); + private static IImageUrlGenerator ImageUrlGenerator { get; } = StaticServiceProvider.Instance.GetRequiredService(); - private static IPublishedValueFallback PublishedValueFallback { get; } = - StaticServiceProvider.Instance.GetRequiredService(); - - private static IPublishedUrlProvider PublishedUrlProvider { get; } = - StaticServiceProvider.Instance.GetRequiredService(); + private static IPublishedValueFallback PublishedValueFallback { get; } = StaticServiceProvider.Instance.GetRequiredService(); + private static IPublishedUrlProvider PublishedUrlProvider { get; } = StaticServiceProvider.Instance.GetRequiredService(); /// - /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item + /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// - /// The crop alias e.g. thumbnail - /// + /// The IPublishedContent item. + /// The crop alias e.g. thumbnail. /// /// The URL of the cropped image. /// @@ -57,17 +49,11 @@ namespace Umbraco.Extensions => ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, imageCropperValue, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider); /// - /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. + /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// - /// The property alias of the property containing the Json data e.g. umbracoFile - /// - /// - /// The crop alias e.g. thumbnail - /// + /// The IPublishedContent item. + /// The property alias of the property containing the JSON data e.g. umbracoFile. + /// The crop alias e.g. thumbnail. /// /// The URL of the cropped image. /// @@ -83,53 +69,22 @@ namespace Umbraco.Extensions /// /// Gets the underlying image processing service URL from the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// Property alias of the property containing the Json data. - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point, to generate an output image using the focal point instead of the predefined crop - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: - /// - /// The IPublishedContent item. + /// The width of the output image. + /// The height of the output image. + /// Property alias of the property containing the JSON data. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point, to generate an output image using the focal point instead of the predefined crop. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -146,8 +101,7 @@ namespace Umbraco.Extensions bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) + ImageCropRatioMode? ratioMode = null) => mediaItem.GetCropUrl( ImageUrlGenerator, PublishedValueFallback, @@ -163,62 +117,30 @@ namespace Umbraco.Extensions useCropDimensions, cacheBuster, furtherOptions, - ratioMode, - upScale + ratioMode ); /// /// Gets the underlying image processing service URL from the image path. /// - /// - /// The image URL. - /// - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// The Json data from the Umbraco Core Image Cropper property editor - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: - /// - /// The image URL. + /// The width of the output image. + /// The height of the output image. + /// The JSON data from the Umbraco Core Image Cropper property editor. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio. /// - /// The the URL of the cropped image. + /// The URL of the cropped image. /// public static string GetCropUrl( this string imageUrl, @@ -233,8 +155,7 @@ namespace Umbraco.Extensions bool useCropDimensions = false, string cacheBusterValue = null, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) + ImageCropRatioMode? ratioMode = null) => imageUrl.GetCropUrl( ImageUrlGenerator, width, @@ -248,60 +169,30 @@ namespace Umbraco.Extensions useCropDimensions, cacheBusterValue, furtherOptions, - ratioMode, - upScale - ); + ratioMode + ); /// /// Gets the underlying image processing service URL from the image path. /// - /// - /// The image URL. - /// - /// - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: - /// - /// The image URL. + /// The crop data set. + /// The width of the output image. + /// The height of the output image. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio /// - /// The the URL of the cropped image. + /// The URL of the cropped image. /// public static string GetCropUrl( this string imageUrl, @@ -316,9 +207,7 @@ namespace Umbraco.Extensions bool useCropDimensions = false, string cacheBusterValue = null, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true, - string animationProcessMode = null) + ImageCropRatioMode? ratioMode = null) => imageUrl.GetCropUrl( ImageUrlGenerator, cropDataSet, @@ -331,9 +220,7 @@ namespace Umbraco.Extensions useCropDimensions, cacheBusterValue, furtherOptions, - ratioMode, - upScale, - animationProcessMode + ratioMode ); @@ -341,10 +228,6 @@ namespace Umbraco.Extensions public static string GetLocalCropUrl( this MediaWithCrops mediaWithCrops, string alias, - string cacheBusterValue = null) - { - return mediaWithCrops.LocalCrops.Src + - mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue); - } + string cacheBusterValue = null) => mediaWithCrops.LocalCrops.Src + mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue); } } diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index 27de0d22b2..00d4dcdb77 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -13,17 +13,13 @@ namespace Umbraco.Extensions public static class ImageCropperTemplateCoreExtensions { /// - /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item + /// Gets the underlying image processing service URL by the crop alias (from the "umbracoFile" property alias) on the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// - /// The crop alias e.g. thumbnail - /// - /// The image url generator. + /// The IPublishedContent item. + /// The crop alias e.g. thumbnail. + /// The image URL generator. /// The published value fallback. - /// The published url provider. + /// The published URL provider. /// /// The URL of the cropped image. /// @@ -32,20 +28,14 @@ namespace Umbraco.Extensions string cropAlias, IImageUrlGenerator imageUrlGenerator, IPublishedValueFallback publishedValueFallback, - IPublishedUrlProvider publishedUrlProvider) - { - return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true); - } + IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true); public static string GetCropUrl( this MediaWithCrops mediaWithCrops, string cropAlias, IImageUrlGenerator imageUrlGenerator, IPublishedValueFallback publishedValueFallback, - IPublishedUrlProvider publishedUrlProvider) - { - return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true); - } + IPublishedUrlProvider publishedUrlProvider) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true); /// /// Gets the crop URL by using only the specified . @@ -54,6 +44,8 @@ namespace Umbraco.Extensions /// The image cropper value. /// The crop alias. /// The image URL generator. + /// The published value fallback. + /// The published URL provider. /// /// The image crop URL. /// @@ -63,26 +55,17 @@ namespace Umbraco.Extensions string cropAlias, IImageUrlGenerator imageUrlGenerator, IPublishedValueFallback publishedValueFallback, - IPublishedUrlProvider publishedUrlProvider) - { - return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true); - } + IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true); /// - /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. + /// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// - /// The property alias of the property containing the Json data e.g. umbracoFile - /// - /// - /// The crop alias e.g. thumbnail - /// - /// The image url generator. + /// The IPublishedContent item. + /// The property alias of the property containing the JSON data e.g. umbracoFile. + /// The crop alias e.g. thumbnail. + /// The image URL generator. /// The published value fallback. - /// The published url provider. + /// The published URL provider. /// /// The URL of the cropped image. /// @@ -92,74 +75,37 @@ namespace Umbraco.Extensions string cropAlias, IImageUrlGenerator imageUrlGenerator, IPublishedValueFallback publishedValueFallback, - IPublishedUrlProvider publishedUrlProvider) - { - return mediaItem.GetCropUrl( imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); - } + IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); public static string GetCropUrl(this MediaWithCrops mediaWithCrops, IPublishedValueFallback publishedValueFallback, IPublishedUrlProvider publishedUrlProvider, string propertyAlias, string cropAlias, - IImageUrlGenerator imageUrlGenerator) - { - return mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); - } + IImageUrlGenerator imageUrlGenerator) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); /// /// Gets the underlying image processing service URL from the IPublishedContent item. /// - /// - /// The IPublishedContent item. - /// - /// The image url generator. + /// The IPublishedContent item. + /// The image URL generator. /// The published value fallback. - /// The published url provider. - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// Property alias of the property containing the Json data. - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point, to generate an output image using the focal point instead of the predefined crop - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: - /// - /// The published URL provider. + /// The width of the output image. + /// The height of the output image. + /// Property alias of the property containing the JSON data. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point, to generate an output image using the focal point instead of the predefined crop. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -179,11 +125,7 @@ namespace Umbraco.Extensions bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) - { - return mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale); - } + ImageCropRatioMode? ratioMode = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); public static string GetCropUrl( this MediaWithCrops mediaWithCrops, @@ -201,12 +143,14 @@ namespace Umbraco.Extensions bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) + ImageCropRatioMode? ratioMode = null) { - if (mediaWithCrops == null) throw new ArgumentNullException(nameof(mediaWithCrops)); + if (mediaWithCrops == null) + { + throw new ArgumentNullException(nameof(mediaWithCrops)); + } - return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, upScale); + return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); } private static string GetCropUrl( @@ -227,15 +171,17 @@ namespace Umbraco.Extensions bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) + ImageCropRatioMode? ratioMode = null) { - if (mediaItem == null) throw new ArgumentNullException(nameof(mediaItem)); - - var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null; + if (mediaItem == null) + { + throw new ArgumentNullException(nameof(mediaItem)); + } if (mediaItem.HasProperty(propertyAlias) == false || mediaItem.HasValue(propertyAlias) == false) - return string.Empty; + { + return null; + } var mediaItemUrl = mediaItem.MediaUrl(publishedUrlProvider, propertyAlias: propertyAlias); @@ -269,63 +215,35 @@ namespace Umbraco.Extensions } } + var cacheBusterValue = cacheBuster ? mediaItem.UpdateDate.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture) : null; + return GetCropUrl( mediaItemUrl, imageUrlGenerator, localCrops, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, - cacheBusterValue, furtherOptions, ratioMode, upScale); + cacheBusterValue, furtherOptions, ratioMode); } /// /// Gets the underlying image processing service URL from the image path. /// - /// - /// The image URL. - /// - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// The Json data from the Umbraco Core Image Cropper property editor - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: - /// - /// The image URL. + /// The image URL generator. + /// The width of the output image. + /// The height of the output image. + /// The Json data from the Umbraco Core Image Cropper property editor. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio. /// - /// The the URL of the cropped image. + /// The URL of the cropped image. /// public static string GetCropUrl( this string imageUrl, @@ -341,10 +259,12 @@ namespace Umbraco.Extensions bool useCropDimensions = false, string cacheBusterValue = null, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true) + ImageCropRatioMode? ratioMode = null) { - if (string.IsNullOrEmpty(imageUrl)) return string.Empty; + if (string.IsNullOrWhiteSpace(imageUrl)) + { + return null; + } ImageCropperValue cropDataSet = null; if (string.IsNullOrEmpty(imageCropperValue) == false && imageCropperValue.DetectIsJson() && (imageCropMode == ImageCropMode.Crop || imageCropMode == null)) @@ -354,62 +274,31 @@ namespace Umbraco.Extensions return GetCropUrl( imageUrl, imageUrlGenerator, cropDataSet, width, height, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode); } /// /// Gets the underlying image processing service URL from the image path. /// - /// - /// The image URL. - /// - /// - /// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended - /// - /// - /// - /// The width of the output image. - /// - /// - /// The height of the output image. - /// - /// - /// The crop alias. - /// - /// - /// Quality percentage of the output image. - /// - /// - /// The image crop mode. - /// - /// - /// The image crop anchor. - /// - /// - /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - /// - /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - /// - /// - /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - /// - /// - /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: - /// - /// The image URL. + /// The generator that will process all the options and the image URL to return a full image URLs with all processing options appended. + /// The crop data set. + /// The width of the output image. + /// The height of the output image. + /// The crop alias. + /// Quality percentage of the output image. + /// The image crop mode. + /// The image crop anchor. + /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. + /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. + /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: + /// - /// - /// - /// - /// Use a dimension as a ratio - /// - /// - /// If the image should be upscaled to requested dimensions - /// + /// ]]> + /// Use a dimension as a ratio. /// - /// The . + /// The URL of the cropped image. /// public static string GetCropUrl( this string imageUrl, @@ -425,21 +314,23 @@ namespace Umbraco.Extensions bool useCropDimensions = false, string cacheBusterValue = null, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, - bool upScale = true, - string animationProcessMode = null) + ImageCropRatioMode? ratioMode = null) { - if (string.IsNullOrEmpty(imageUrl)) return string.Empty; + if (string.IsNullOrWhiteSpace(imageUrl)) + { + return null; + } ImageUrlGenerationOptions options; - if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null)) { var crop = cropDataSet.GetCrop(cropAlias); - // if a crop was specified, but not found, return null + // If a crop was specified, but not found, return null if (crop == null && !string.IsNullOrWhiteSpace(cropAlias)) + { return null; + } options = cropDataSet.GetCropBaseOptions(imageUrl, crop, string.IsNullOrWhiteSpace(cropAlias), preferFocalPoint); @@ -464,7 +355,7 @@ namespace Umbraco.Extensions } else { - options = new ImageUrlGenerationOptions (imageUrl) + options = new ImageUrlGenerationOptions(imageUrl) { ImageCropMode = (imageCropMode ?? ImageCropMode.Pad), ImageCropAnchor = imageCropAnchor @@ -474,7 +365,6 @@ namespace Umbraco.Extensions options.Quality = quality; options.Width = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Width ? null : width; options.Height = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Height ? null : height; - options.AnimationProcessMode = animationProcessMode; if (ratioMode == ImageCropRatioMode.Width && height != null) { @@ -502,7 +392,6 @@ namespace Umbraco.Extensions } } - options.UpScale = upScale; options.FurtherOptions = furtherOptions; options.CacheBusterValue = cacheBusterValue; diff --git a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs index e7dd5248e1..a6dd4ac1ca 100644 --- a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -247,7 +247,6 @@ namespace Umbraco.Extensions bool cacheBuster = true, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true, bool htmlEncode = true) { if (mediaItem == null) @@ -256,8 +255,8 @@ namespace Umbraco.Extensions } var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, - upScale); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); + return CreateHtmlString(url, htmlEncode); } @@ -274,15 +273,14 @@ namespace Umbraco.Extensions string cacheBusterValue = null, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true, bool htmlEncode = true) { if (imageCropperValue == null) return HtmlString.Empty; var imageUrl = imageCropperValue.Src; var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, - upScale); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode); + return CreateHtmlString(url, htmlEncode); } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js index a937cd2675..dd65f89526 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/imageurlgenerator.resource.js @@ -1,4 +1,4 @@ -/** +/** * @ngdoc service * @name umbraco.resources.imageUrlGeneratorResource * @function @@ -11,14 +11,14 @@ function imageUrlGeneratorResource($http, umbRequestHelper) { - function getCropUrl(mediaPath, width, height, imageCropMode, animationProcessMode) { + function getCropUrl(mediaPath, width, height, imageCropMode) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "imageUrlGeneratorApiBaseUrl", "GetCropUrl", - { mediaPath, width, height, imageCropMode, animationProcessMode })), + { mediaPath, width, height, imageCropMode })), 'Failed to get crop URL'); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js index 1b3765a5f5..e98a597e76 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js @@ -1,4 +1,4 @@ -/** +/** * @ngdoc service * @name umbraco.services.mediaHelper * @description A helper object used for dealing with media items @@ -408,16 +408,20 @@ function mediaHelper(umbRequestHelper, $http, $log) { * @param {string} imagePath Raw image path * @param {object} options Object describing image generation parameters: * { - * animationProcessMode: - * cacheBusterValue: + * width: + * height: * focalPoint: { * left: * top: * }, - * height: * mode: - * upscale: - * width: + * cacheBusterValue: + * crop: { + * x1: + * x2: + * y1: + * y2: + * }, * } */ getProcessedImageUrl: function (imagePath, options) { @@ -433,18 +437,16 @@ function mediaHelper(umbRequestHelper, $http, $log) { "GetProcessedImageUrl", { imagePath, - animationProcessMode: options.animationProcessMode, - cacheBusterValue: options.cacheBusterValue, + width: options.width, + height: options.height, focalPointLeft: options.focalPoint ? options.focalPoint.left : null, focalPointTop: options.focalPoint ? options.focalPoint.top : null, - height: options.height, mode: options.mode, - upscale: options.upscale || false, - width: options.width, + cacheBusterValue: options.cacheBusterValue, cropX1: options.crop ? options.crop.x1 : null, cropX2: options.crop ? options.crop.x2 : null, cropY1: options.crop ? options.crop.y1 : null, - cropY2: options.crop ? options.crop.y : null + cropY2: options.crop ? options.crop.y2 : null })), "Failed to retrieve processed image URL for image: " + imagePath); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index b83367ef6e..2738883b15 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -306,8 +306,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s if (imgUrl) { mediaHelper.getProcessedImageUrl(imgUrl, { - height: newSize.height, - width: newSize.width + width: newSize.width, + height: newSize.height }) .then(function (resizedImgUrl) { editor.dom.setAttrib(imageDomElement, 'data-mce-src', resizedImgUrl); @@ -1522,15 +1522,13 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s args.editor.on('ObjectResized', function (e) { var srcAttr = $(e.target).attr("src"); var path = srcAttr.split("?")[0]; - mediaHelper.getProcessedImageUrl(path, - { - height: e.height, - moded: "max", - width: e.width - }) - .then(function (resizedPath) { - $(e.target).attr("data-mce-src", resizedPath); - }); + mediaHelper.getProcessedImageUrl(path, { + width: e.width, + height: e.height, + mode: "max" + }).then(function (resizedPath) { + $(e.target).attr("data-mce-src", resizedPath); + }); syncContent(); }); From 474b63a59032fdc98afe9cbc58ce7237f5b4ef27 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 13:35:04 +0200 Subject: [PATCH 21/54] Update AngularJS image URL parameters --- .../views/components/blockcard/umbBlockCard.component.js | 2 +- .../propertyeditors/fileupload/fileupload.controller.js | 4 ++-- .../propertyeditors/grid/editors/media.controller.js | 9 +++------ .../imagecropper/imagecropper.controller.js | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js index 0c75bfbee3..a9993a0498 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/components/blockcard/umbBlockCard.component.js @@ -42,7 +42,7 @@ var path = umbRequestHelper.convertVirtualToAbsolutePath(vm.blockConfigModel.thumbnail); if (path.toLowerCase().endsWith(".svg") === false) { - path += "?upscale=false&width=400"; + path += "?width=400"; } vm.styleBackgroundImage = 'url(\''+path+'\')'; } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js index 4f1016e680..b4d59c683c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/fileupload/fileupload.controller.js @@ -1,4 +1,4 @@ -(function () { +(function () { 'use strict'; /** @@ -55,7 +55,7 @@ if (thumbnail) { if (mediaHelper.detectIfImageByExtension(property.value)) { //get default big thumbnail from image processor - var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first"; + var thumbnailUrl = property.value + "?width=500&rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss"); return thumbnailUrl; } else { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index 91da54d4ad..d87d2c30bf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -73,25 +73,22 @@ angular.module("umbraco") url += `?crop=${coords.x1},${coords.y1},${coords.x2},${coords.y2}&cropmode=percentage`; } else { // Here in order not to break existing content where focalPoint were used. - // For some reason width/height have to come first when mode=crop. if ($scope.control.value.focalPoint) { - url += `?center=${$scope.control.value.focalPoint.top},${$scope.control.value.focalPoint.left}`; - url += '&mode=crop'; + url += `?rxy=${$scope.control.value.focalPoint.top},${$scope.control.value.focalPoint.left}`; } else { // Prevent black padding and no crop when focal point not set / changed from default - url += '?center=0.5,0.5&mode=crop'; + url += '?rxy=0.5,0.5'; } } url += '&width=' + $scope.control.editor.config.size.width; url += '&height=' + $scope.control.editor.config.size.height; - url += '&animationprocessmode=first'; } // set default size if no crop present (moved from the view) if (url.includes('?') === false) { - url += '?width=800&upscale=false&animationprocessmode=false' + url += '?width=800' } return url; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index cbaf843d35..8bb50a07dc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -234,7 +234,7 @@ angular.module('umbraco') if (property.value && property.value.src) { if (thumbnail === true) { - return property.value.src + "?width=500&mode=max&animationprocessmode=first"; + return property.value.src + "?width=500"; } else { return property.value.src; From 0753353cf7e7ca5d899b3210d16f8d0d111f7c13 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 13:36:50 +0200 Subject: [PATCH 22/54] Remove obsolete/unused properties and parameters --- .../Models/ImageUrlGenerationOptions.cs | 17 ----------------- .../ValueConverters/ImageCropperValue.cs | 12 +++++------- .../ImageCropperTemplateCoreExtensions.cs | 2 +- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs index 3118162786..9f4943dc68 100644 --- a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs +++ b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs @@ -1,5 +1,3 @@ -using System; - namespace Umbraco.Cms.Core.Models { /// @@ -15,21 +13,12 @@ namespace Umbraco.Cms.Core.Models public int? Height { get; set; } - [Obsolete("This property is unsupported by the default implementation, manually calculate the width based on the height instead.")] - public decimal? WidthRatio { get; set; } - - [Obsolete("This property is unsupported by the default implementation, manually calculate the height based on the width instead.")] - public decimal? HeightRatio { get; set; } - public int? Quality { get; set; } public ImageCropMode? ImageCropMode { get; set; } public ImageCropAnchor? ImageCropAnchor { get; set; } - [Obsolete("Images are already cropped from the center to the specified width/height by default.")] - public bool DefaultCrop { get; set; } - public FocalPointPosition FocalPoint { get; set; } public CropCoordinates Crop { get; set; } @@ -38,12 +27,6 @@ namespace Umbraco.Cms.Core.Models public string FurtherOptions { get; set; } - [Obsolete("This property is unsupported by the default implementation, images should always be resized to the specified dimensions (within the configured maximums) to prevent different sizes depending on the source image.")] - public bool UpScale { get; set; } = true; - - [Obsolete("This property is unsupported by the default implementation, all frames should be processed by default.")] - public string AnimationProcessMode { get; set; } - /// /// The focal point position, in whatever units the registered IImageUrlGenerator uses, typically a percentage of the total image from 0.0 to 1.0. /// diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 09e080e0b0..6cce8899fb 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -63,11 +63,9 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters : Crops.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); } - public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint) + public ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool preferFocalPoint) { - if (preferFocalPoint && HasFocalPoint() - || crop != null && crop.Coordinates == null && HasFocalPoint() - || defaultCrop && HasFocalPoint()) + if ((preferFocalPoint && HasFocalPoint()) || (crop != null && crop.Coordinates == null && HasFocalPoint())) { return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) }; } @@ -92,7 +90,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters if (crop == null && !string.IsNullOrWhiteSpace(alias)) return null; - var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint); + var options = GetCropBaseOptions(null, crop, useFocalPoint || string.IsNullOrWhiteSpace(alias)); if (crop != null && useCropDimensions) { @@ -108,9 +106,9 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters /// /// Gets the value image URL for a specific width and height. /// - public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null) + public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, string cacheBusterValue = null) { - var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint); + var options = GetCropBaseOptions(null, null, false); options.Width = width; options.Height = height; diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index 00d4dcdb77..ae7e391bba 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -332,7 +332,7 @@ namespace Umbraco.Extensions return null; } - options = cropDataSet.GetCropBaseOptions(imageUrl, crop, string.IsNullOrWhiteSpace(cropAlias), preferFocalPoint); + options = cropDataSet.GetCropBaseOptions(imageUrl, crop, preferFocalPoint || string.IsNullOrWhiteSpace(cropAlias)); if (crop != null & useCropDimensions) { From 2362b9479b578ffd70dcbd6cf3d8904898e639b0 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 10 Aug 2021 15:44:19 +0300 Subject: [PATCH 23/54] Update DictionaryDto and LanguageTextDto to have unique indexes. --- .../Persistence/Dtos/DictionaryDto.cs | 4 ++-- .../Persistence/Dtos/LanguageTextDto.cs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs index cec6e86f83..ac95a2fdf2 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/DictionaryDto.cs @@ -11,6 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos public class DictionaryDto // public as required to be accessible from Deploy for the RepairDictionaryIdsWorkItem. { public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.DictionaryEntry; + [Column("pk")] [PrimaryKeyColumn] public int PrimaryKey { get; set; } @@ -25,10 +26,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_Parent")] public Guid? Parent { get; set; } - // TODO: This needs to have a unique index. [Column("key")] [Length(450)] - [Index(IndexTypes.NonClustered, Name = "IX_cmsDictionary_key")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_key")] public string Key { get; set; } [ResultColumn] diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs index 58b0f21542..79c2fa088d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs @@ -4,11 +4,13 @@ using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; namespace Umbraco.Cms.Infrastructure.Persistence.Dtos { - [TableName(Cms.Core.Constants.DatabaseSchema.Tables.DictionaryValue)] + [TableName(TableName)] [PrimaryKey("pk")] [ExplicitColumns] public class LanguageTextDto { + public const string TableName = Cms.Core.Constants.DatabaseSchema.Tables.DictionaryValue; + [Column("pk")] [PrimaryKeyColumn] public int PrimaryKey { get; set; } @@ -21,9 +23,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [ForeignKey(typeof(DictionaryDto), Column = "id")] public Guid UniqueId { get; set; } - // TODO: Need a unique constraint on LanguageId, UniqueId, Value [Column("value")] [Length(1000)] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_value", ForColumns = "languageId,UniqueId,value")] public string Value { get; set; } } } From 20a40ce0019c51a082419333326d98ba775570f8 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Tue, 10 Aug 2021 17:47:48 +0300 Subject: [PATCH 24/54] New migration for dropping existing indexes in the dictionary tables and re-creating the new ones. Silently log in case the migration fails - due to duplicate records --- .../Migrations/Upgrade/UmbracoPlan.cs | 1 + .../V_9_0_0/DictionaryTablesIndexes.cs | 136 ++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index f8d480bc8c..6f8b182504 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -229,6 +229,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade // - 8.15 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901} // - 9.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12} To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); + To("{12DCDE7F-9AB7-4617-804F-AB66BF360980}"); } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs new file mode 100644 index 0000000000..91a8b29bbb --- /dev/null +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs @@ -0,0 +1,136 @@ +using System.Linq; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute.Expressions; +using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions; +using Umbraco.Cms.Infrastructure.Persistence.Dtos; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 +{ + public class DictionaryTablesIndexes : MigrationBase + { + private const string IndexedDictionaryColumn = "key"; + private const string IndexedLanguageTextColumn = "value"; + + public DictionaryTablesIndexes(IMigrationContext context) + : base(context) + { + } + + protected override void Migrate() + { + var indexDictionaryDto = $"IX_{DictionaryDto.TableName}_{IndexedDictionaryColumn}"; + var indexLanguageTextDto = $"IX_{LanguageTextDto.TableName}_{IndexedLanguageTextColumn}"; + + // Delete existing + DeleteIndex(indexDictionaryDto); + // Re-create/Add + AddUniqueConstraint(new[] { IndexedDictionaryColumn }, indexDictionaryDto); + + // Delete existing + DeleteIndex(indexLanguageTextDto); + + var langTextcolumns = new[] { "languageId", "UniqueId", IndexedLanguageTextColumn }; + + // Re-create/Add + AddUniqueConstraint(langTextcolumns, indexLanguageTextDto); + } + + private void DeleteIndex(string indexName) + { + var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax); + + if (IndexExists(indexName)) + { + Delete.Index(indexName).OnTable(tableDef.Name).Do(); + } + } + + private void CreateIndex(string indexName) + { + var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax); + + // get the definition by name + var index = tableDef.Indexes.First(x => x.Name == indexName); + new ExecuteSqlStatementExpression(Context) { SqlStatement = Context.SqlContext.SqlSyntax.Format(index) }.Execute(); + } + + private void AddUniqueConstraint(string[] columns, string index) + { + var tableDef = DefinitionFactory.GetTableDefinition(typeof(TDto), Context.SqlContext.SqlSyntax); + + // Check the existing data to ensure the constraint can be successfully applied. + // This seems to be better than relying on catching an exception as this leads to + // transaction errors: "This SqlTransaction has completed; it is no longer usable". + var columnsDescription = string.Join("], [", columns); + if (ContainsDuplicates(columns)) + { + var message = $"Could not create unique constraint on [{tableDef.Name}] due to existing " + + $"duplicate records across the column{(columns.Length > 1 ? "s" : string.Empty)}: [{columnsDescription}]."; + + LogIncompleteMigrationStep(message); + return; + } + + CreateIndex(index); + } + + private bool ContainsDuplicates(string[] columns) + { + // Check for duplicates by comparing the total count of all records with the count of records distinct by the + // provided column. If the former is greater than the latter, there's at least one duplicate record. + int recordCount = GetRecordCount(); + int distinctRecordCount = GetDistinctRecordCount(columns); + + return recordCount > distinctRecordCount; + } + + private int GetRecordCount() + { + var countQuery = Database.SqlContext.Sql() + .SelectCount() + .From(); + + return Database.ExecuteScalar(countQuery); + } + + private int GetDistinctRecordCount(string[] columns) + { + string columnSpecification; + + // If using SQL CE, we don't have access to COUNT (DISTINCT *) or CONCAT, so will need to do this by querying all records. + if (DatabaseType.IsSqlCe()) + { + columnSpecification = columns.Length == 1 + ? StringConvertedAndQuotedColumnName(columns[0]) + : $"{string.Join(" + ", columns.Select(x => StringConvertedAndQuotedColumnName(x)))}"; + + var allRecordsQuery = Database.SqlContext.Sql() + .Select(columnSpecification) + .From(); + + var allRecords = Database.Fetch(allRecordsQuery); + + return allRecords.Distinct().Count(); + } + + columnSpecification = columns.Length == 1 + ? QuoteColumnName(columns[0]) + : $"CONCAT({string.Join(",", columns.Select(QuoteColumnName))})"; + + var distinctCountQuery = Database.SqlContext.Sql() + .Select($"COUNT(DISTINCT({columnSpecification}))") + .From(); + + return Database.ExecuteScalar(distinctCountQuery); + } + + private void LogIncompleteMigrationStep(string message) => + Logger.LogError($"Database migration step failed: {message}"); + + private string StringConvertedAndQuotedColumnName(string column) => $"CONVERT(nvarchar(1000),{QuoteColumnName(column)})"; + + private string QuoteColumnName(string column) => $"[{column}]"; + } +} From 766530fcd3880df48bd384f6f3119bd91086da42 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 17:17:40 +0200 Subject: [PATCH 25/54] Swap focal point order to left,top to match resize X,Y coordinates --- .../Media/ImageSharpImageUrlGenerator.cs | 2 +- .../Media/ImageSharpImageUrlGeneratorTests.cs | 10 +++++----- .../propertyeditors/grid/editors/media.controller.js | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 8e0a8eb350..1f72a68aec 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Infrastructure.Media if (options.FocalPoint != null) { - AddQueryString("rxy", options.FocalPoint.Top, options.FocalPoint.Left); + AddQueryString("rxy", options.FocalPoint.Left, options.FocalPoint.Top); } if (options.Crop != null) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index f6d87ef5a1..ab3ca6b23c 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -20,28 +20,28 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_CropAliasTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + Assert.AreEqual(MediaPath + "?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); } [Test] public void GetCropUrl_WidthHeightTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300 }); - Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=200&height=300", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300", urlString); } [Test] public void GetCropUrl_FocalPointTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 100, Height = 100 }); - Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=100&height=100", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=100&height=100", urlString); } [Test] public void GetCropUrlFurtherOptionsTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" }); - Assert.AreEqual(MediaPath + "?rxy=0.80827067669172936,0.96&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); } /// @@ -121,7 +121,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 270, Height = 161 }); - Assert.AreEqual(MediaPath + "?rxy=0.41,0.4275&width=270&height=161", urlString); + Assert.AreEqual(MediaPath + "?rxy=0.4275,0.41&width=270&height=161", urlString); } /// diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index d87d2c30bf..81a548a116 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -70,11 +70,11 @@ angular.module("umbraco") if ($scope.control.value.coordinates) { // New way, crop by percent must come before width/height. var coords = $scope.control.value.coordinates; - url += `?crop=${coords.x1},${coords.y1},${coords.x2},${coords.y2}&cropmode=percentage`; + url += `?cc=${coords.x1},${coords.y1},${coords.x2},${coords.y2}`; } else { // Here in order not to break existing content where focalPoint were used. if ($scope.control.value.focalPoint) { - url += `?rxy=${$scope.control.value.focalPoint.top},${$scope.control.value.focalPoint.left}`; + url += `?rxy=${$scope.control.value.focalPoint.left},${$scope.control.value.focalPoint.top}`; } else { // Prevent black padding and no crop when focal point not set / changed from default url += '?rxy=0.5,0.5'; From 22814036a1e7d54b8ffa0d70c286045d1fb8e2fa Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 17:20:03 +0200 Subject: [PATCH 26/54] Update FocalPointPosition and CropCoordinates constructors/properties --- .../Models/ImageUrlGenerationOptions.cs | 20 +++++++++---------- .../Media/ImageSharpImageUrlGenerator.cs | 2 +- .../ValueConverters/ImageCropperValue.cs | 2 +- .../Media/ImageSharpImageUrlGeneratorTests.cs | 6 +++--- .../Umbraco.Web.Common/ImageCropperTest.cs | 2 +- .../Controllers/ImagesController.cs | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs index 9f4943dc68..ba5c1d0e30 100644 --- a/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs +++ b/src/Umbraco.Core/Models/ImageUrlGenerationOptions.cs @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Core.Models /// public class FocalPointPosition { - public FocalPointPosition(decimal top, decimal left) + public FocalPointPosition(decimal left, decimal top) { Left = left; Top = top; @@ -48,21 +48,21 @@ namespace Umbraco.Cms.Core.Models /// public class CropCoordinates { - public CropCoordinates(decimal x1, decimal y1, decimal x2, decimal y2) + public CropCoordinates(decimal left, decimal top, decimal right, decimal bottom) { - X1 = x1; - Y1 = y1; - X2 = x2; - Y2 = y2; + Left = left; + Top = top; + Right = right; + Bottom = bottom; } - public decimal X1 { get; } + public decimal Left { get; } - public decimal Y1 { get; } + public decimal Top { get; } - public decimal X2 { get; } + public decimal Right { get; } - public decimal Y2 { get; } + public decimal Bottom { get; } } } } diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 1f72a68aec..b6906d304b 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -42,7 +42,7 @@ namespace Umbraco.Cms.Infrastructure.Media if (options.Crop != null) { - AddQueryString("crop", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); + AddQueryString("crop", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); AddQueryString("cropmode", "percentage"); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs index 6cce8899fb..c0efaac4ae 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValue.cs @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters { if ((preferFocalPoint && HasFocalPoint()) || (crop != null && crop.Coordinates == null && HasFocalPoint())) { - return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) }; + return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Left, FocalPoint.Top) }; } else if (crop != null && crop.Coordinates != null && preferFocalPoint == false) { diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index ab3ca6b23c..91dd1a8c53 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -12,8 +12,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media { private const string MediaPath = "/media/1005/img_0671.jpg"; private static readonly ImageUrlGenerationOptions.CropCoordinates s_crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m); - private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.80827067669172936m, 0.96m); - private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.41m, 0.4275m); + private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.96m, 0.80827067669172936m); + private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.4275m, 0.41m); private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator(); [Test] @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media public void GetBaseCropUrlFromModelTest() { var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = s_crop, Width = 100, Height = 100 }); - Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString); + Assert.AreEqual("?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs index 16b2268a47..599324b040 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs @@ -364,7 +364,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common } else if (options.Crop != null) { - AddQueryString("c", options.Crop.X1, options.Crop.Y1, options.Crop.X2, options.Crop.Y2); + AddQueryString("c", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); } if (options.ImageCropMode.HasValue) diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 00a18ec8b7..564d0dcdd9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (focalPointLeft.HasValue && focalPointTop.HasValue) { - options.FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointTop.Value, focalPointLeft.Value); + options.FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(focalPointLeft.Value, focalPointTop.Value); } else if (cropX1.HasValue && cropX2.HasValue && cropY1.HasValue && cropY2.HasValue) { From 01559ede97bd4f95eaa911c548d05caacd5fe2fd Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Tue, 10 Aug 2021 23:55:37 +0200 Subject: [PATCH 27/54] Only support crop coordinates in CropWebProcessor --- .../Media/ImageSharpImageUrlGenerator.cs | 3 +- .../ImageProcessors/CropMode.cs | 18 -------- .../ImageProcessors/CropWebProcessor.cs | 46 +++++++------------ 3 files changed, 17 insertions(+), 50 deletions(-) delete mode 100644 src/Umbraco.Web.Common/ImageProcessors/CropMode.cs diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index b6906d304b..12e85dfb88 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -42,8 +42,7 @@ namespace Umbraco.Cms.Infrastructure.Media if (options.Crop != null) { - AddQueryString("crop", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); - AddQueryString("cropmode", "percentage"); + AddQueryString("cc", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); } if (options.ImageCropMode.HasValue) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs b/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs deleted file mode 100644 index 6e08c6e05c..0000000000 --- a/src/Umbraco.Web.Common/ImageProcessors/CropMode.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Umbraco.Cms.Web.Common.ImageProcessors -{ - /// - /// Represents the mode used to calculate a crop. - /// - public enum CropMode - { - /// - /// Crops the image using the standard rectangle model of x, y, width, height. - /// - Pixels, - - /// - /// Crops the image using the percentages model of left, top, right, bottom. - /// - Percentage - } -} diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index c51af9a532..8945ce7f45 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Globalization; using Microsoft.Extensions.Logging; @@ -15,43 +16,31 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors public class CropWebProcessor : IImageWebProcessor { /// - /// The command constant for the crop definition. + /// The command constant for the crop coordinates. /// - public const string Crop = "crop"; - - /// - /// The command constant for the crop mode. - /// - public const string Mode = "cropmode"; - + public const string Coordinates = "cc"; /// public IEnumerable Commands { get; } = new[] { - Crop, - Mode + Coordinates }; /// public FormattedImage Process(FormattedImage image, ILogger logger, IDictionary commands, CommandParser parser, CultureInfo culture) { - if (GetCrop(commands, parser, culture) is RectangleF crop) + RectangleF? coordinates = GetCoordinates(commands, parser, culture); + if (coordinates != null) { - if (GetMode(commands, parser, culture) == CropMode.Percentage) - { - // Convert the percentage based model of left, top, right, bottom to x, y, width, height - int sourceWidth = image.Image.Width; - int sourceHeight = image.Image.Height; + // Convert the percentage based model of left, top, right, bottom to x, y, width, height + int sourceWidth = image.Image.Width; + int sourceHeight = image.Image.Height; + int x = (int)MathF.Round(coordinates.Value.Left * sourceWidth); + int y = (int)MathF.Round(coordinates.Value.Top * sourceHeight); + int width = sourceWidth - (int)MathF.Round(coordinates.Value.Right * sourceWidth); + int height = sourceHeight - (int)MathF.Round(coordinates.Value.Bottom * sourceHeight); - float left = crop.Left * sourceWidth; - float top = crop.Top * sourceHeight; - float width = sourceWidth - (sourceWidth * crop.Width) - left; - float height = sourceHeight - (sourceHeight * crop.Height) - top; - - crop = new RectangleF(left, top, width, height); - } - - var cropRectangle = Rectangle.Round(crop); + var cropRectangle = new Rectangle(x, y, width, height); image.Image.Mutate(x => x.Crop(cropRectangle)); } @@ -59,9 +48,9 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return image; } - private static RectangleF? GetCrop(IDictionary commands, CommandParser parser, CultureInfo culture) + private static RectangleF? GetCoordinates(IDictionary commands, CommandParser parser, CultureInfo culture) { - float[] coordinates = parser.ParseValue(commands.GetValueOrDefault(Crop), culture); + float[] coordinates = parser.ParseValue(commands.GetValueOrDefault(Coordinates), culture); if (coordinates.Length != 4) { @@ -70,8 +59,5 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return new RectangleF(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); } - - private static CropMode GetMode(IDictionary commands, CommandParser parser, CultureInfo culture) - => parser.ParseValue(commands.GetValueOrDefault(Mode), culture); } } From c5361ec54e309acfee206cca274cc6f5e4a35a37 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Wed, 11 Aug 2021 09:53:58 +0200 Subject: [PATCH 28/54] Improve crop coordinate calculations --- .../ImageProcessors/CropWebProcessor.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs index 8945ce7f45..7b3cc817f2 100644 --- a/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs +++ b/src/Umbraco.Web.Common/ImageProcessors/CropWebProcessor.cs @@ -32,13 +32,13 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors RectangleF? coordinates = GetCoordinates(commands, parser, culture); if (coordinates != null) { - // Convert the percentage based model of left, top, right, bottom to x, y, width, height + // Convert the coordinates to a pixel based rectangle int sourceWidth = image.Image.Width; int sourceHeight = image.Image.Height; - int x = (int)MathF.Round(coordinates.Value.Left * sourceWidth); - int y = (int)MathF.Round(coordinates.Value.Top * sourceHeight); - int width = sourceWidth - (int)MathF.Round(coordinates.Value.Right * sourceWidth); - int height = sourceHeight - (int)MathF.Round(coordinates.Value.Bottom * sourceHeight); + int x = (int)MathF.Round(coordinates.Value.X * sourceWidth); + int y = (int)MathF.Round(coordinates.Value.Y * sourceHeight); + int width = (int)MathF.Round(coordinates.Value.Width * sourceWidth); + int height = (int)MathF.Round(coordinates.Value.Height * sourceHeight); var cropRectangle = new Rectangle(x, y, width, height); @@ -57,7 +57,8 @@ namespace Umbraco.Cms.Web.Common.ImageProcessors return null; } - return new RectangleF(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); + // The right and bottom values are actually the distance from those sides, so convert them into real coordinates + return RectangleF.FromLTRB(coordinates[0], coordinates[1], 1 - coordinates[2], 1 - coordinates[3]); } } } From 902984f977831099857911361b20dc4efbc7ebca Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 11 Aug 2021 17:18:23 +0200 Subject: [PATCH 29/54] Added unit tests for CropWebProcessor and ImageCropperTemplateCoreExtensions. --- .../Media/ImageSharpImageUrlGenerator.cs | 4 +- ...ImageCropperTemplateCoreExtensionsTests.cs | 192 ++++++++++++++++++ .../ImageProcessors/CropWebProcessorTests.cs | 80 ++++++++ .../ImageCropperTemplateCoreExtensions.cs | 2 +- 4 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 12e85dfb88..6331ad24e1 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -70,12 +70,12 @@ namespace Umbraco.Cms.Infrastructure.Media AddQueryString("quality", options.Quality.Value); } - if (!string.IsNullOrWhiteSpace(options.FurtherOptions)) + if (string.IsNullOrWhiteSpace(options.FurtherOptions) == false) { AppendQueryString(options.FurtherOptions.TrimStart('?', '&')); } - if (!string.IsNullOrWhiteSpace(options.CacheBusterValue)) + if (string.IsNullOrWhiteSpace(options.CacheBusterValue) == false) { AddQueryString("rnd", options.CacheBusterValue); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs new file mode 100644 index 0000000000..0389c51726 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs @@ -0,0 +1,192 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions +{ + [TestFixture] + public class ImageCropperTemplateCoreExtensionsTests + { + [Test] + public void GetCropUrl_WithCropSpecifiedButNotFound_ReturnsNull() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + new ImageCropperValue { }, + imageCropMode: ImageCropMode.Crop, + cropAlias: "Missing"); + + Assert.IsNull(result); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndUsingCropDimensions_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + useCropDimensions: true); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 100 && + y.Height == 200))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndWidthAndHeightProvided_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + width: 50, + height: 80); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 50 && + y.Height == 80))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndWidthOnlyProvided_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + width: 50); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 50 && + y.Height == 100))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndHeightOnlyProvided_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + height: 50); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 25 && + y.Height == 50))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndWidthRatioModeProvidedWithHeight_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + ratioMode: ImageCropRatioMode.Width, + height: 50); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 50 && + y.Height == 50))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndWidthRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + ratioMode: ImageCropRatioMode.Width, + width: 35, + height: 50); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 35 && + y.Height == 50))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndHeightRatioModeProvidedWithWidth_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + ratioMode: ImageCropRatioMode.Height, + width: 60); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 60 && + y.Height == 60))); + } + + [Test] + public void GetCropUrl_WithCropSpecifiedAndHeightRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() + { + var imageUrl = "/test.jpg"; + Mock imageUrlGenerator = CreateMockImageUrlGenerator(); + var result = imageUrl.GetCropUrl( + imageUrlGenerator.Object, + CreateImageCropperValueWithCrops(), + imageCropMode: ImageCropMode.Crop, + cropAlias: "TestCrop", + ratioMode: ImageCropRatioMode.Height, + width: 60, + height: 40); + + imageUrlGenerator + .Verify(x => x.GetImageUrl( + It.Is(y => y.Width == 60 && + y.Height == 40))); + } + + private static Mock CreateMockImageUrlGenerator() => new Mock(); + + private static ImageCropperValue CreateImageCropperValueWithCrops() => new ImageCropperValue + { + Crops = new List + { + new ImageCropperValue.ImageCropperCrop { Alias = "TestCrop", Width = 100, Height = 200 }, + } + }; + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs new file mode 100644 index 0000000000..2c508d97d2 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageProcessors/CropWebProcessorTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using NUnit.Framework; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Web; +using SixLabors.ImageSharp.Web.Commands; +using SixLabors.ImageSharp.Web.Commands.Converters; +using Umbraco.Cms.Web.Common.ImageProcessors; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.ImageProcessors +{ + [TestFixture] + public class CropWebProcessorTests + { + [Test] + public void CropWebProcessor_CropsImage() + { + var converters = new List + { + CreateArrayConverterOfFloat(), + CreateSimpleCommandConverterOfFloat(), + }; + + var parser = new CommandParser(converters); + CultureInfo culture = CultureInfo.InvariantCulture; + + var commands = new Dictionary + { + { CropWebProcessor.Coordinates, "0.1,0.2,0.1,0.4" }, // left, top, right, bottom + }; + + using var image = new Image(50, 80); + using FormattedImage formatted = CreateFormattedImage(image, PngFormat.Instance); + new CropWebProcessor().Process(formatted, null, commands, parser, culture); + + Assert.AreEqual(40, image.Width); // Cropped 5 pixels from each side. + Assert.AreEqual(32, image.Height); // Cropped 16 pixels from the top and 32 from the bottom. + } + + private static ICommandConverter CreateArrayConverterOfFloat() + { + // ImageSharp.Web's ArrayConverter is internal, so we need to use reflection to instantiate. + var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.ArrayConverter`1, SixLabors.ImageSharp.Web"); + Type[] typeArgs = { typeof(float) }; + Type genericType = type.MakeGenericType(typeArgs); + return (ICommandConverter)Activator.CreateInstance(genericType); + } + + private static ICommandConverter CreateSimpleCommandConverterOfFloat() + { + // ImageSharp.Web's SimpleCommandConverter is internal, so we need to use reflection to instantiate. + var type = Type.GetType("SixLabors.ImageSharp.Web.Commands.Converters.SimpleCommandConverter`1, SixLabors.ImageSharp.Web"); + Type[] typeArgs = { typeof(float) }; + Type genericType = type.MakeGenericType(typeArgs); + return (ICommandConverter)Activator.CreateInstance(genericType); + } + + private FormattedImage CreateFormattedImage(Image image, PngFormat format) + { + // Again, the constructor of FormattedImage useful for tests is internal, so we need to use reflection. + Type type = typeof(FormattedImage); + var instance = type.Assembly.CreateInstance( + type.FullName, + false, + BindingFlags.Instance | BindingFlags.NonPublic, + null, + new object[] { image, format }, + null, + null); + return (FormattedImage)instance; + } + } +} diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index ae7e391bba..a6d82e3352 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -324,7 +324,7 @@ namespace Umbraco.Extensions ImageUrlGenerationOptions options; if (cropDataSet != null && (imageCropMode == ImageCropMode.Crop || imageCropMode == null)) { - var crop = cropDataSet.GetCrop(cropAlias); + ImageCropperValue.ImageCropperCrop crop = cropDataSet.GetCrop(cropAlias); // If a crop was specified, but not found, return null if (crop == null && !string.IsNullOrWhiteSpace(cropAlias)) From 8b1bd14df1bcccb4bf536a6c5a65fae765d94c4b Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 12 Aug 2021 09:03:02 +0200 Subject: [PATCH 30/54] Remove ImageCropRatioMode and clean up GetCropUrl overloads --- src/Umbraco.Core/Models/ImageCropRatioMode.cs | 8 --- ...ImageCropperTemplateCoreExtensionsTests.cs | 40 ----------- .../Umbraco.Web.Common/ImageCropperTest.cs | 10 +-- .../FriendlyImageCropperTemplateExtensions.cs | 27 +++----- .../ImageCropperTemplateCoreExtensions.cs | 69 +++++-------------- .../Extensions/UrlHelperExtensions.cs | 6 +- 6 files changed, 33 insertions(+), 127 deletions(-) delete mode 100644 src/Umbraco.Core/Models/ImageCropRatioMode.cs diff --git a/src/Umbraco.Core/Models/ImageCropRatioMode.cs b/src/Umbraco.Core/Models/ImageCropRatioMode.cs deleted file mode 100644 index 19f69cbeac..0000000000 --- a/src/Umbraco.Core/Models/ImageCropRatioMode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Cms.Core.Models -{ - public enum ImageCropRatioMode - { - Width, - Height - } -} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs index 0389c51726..2d16805a89 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs @@ -101,25 +101,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions y.Height == 50))); } - [Test] - public void GetCropUrl_WithCropSpecifiedAndWidthRatioModeProvidedWithHeight_CallsImageGeneratorWithCorrectParameters() - { - var imageUrl = "/test.jpg"; - Mock imageUrlGenerator = CreateMockImageUrlGenerator(); - var result = imageUrl.GetCropUrl( - imageUrlGenerator.Object, - CreateImageCropperValueWithCrops(), - imageCropMode: ImageCropMode.Crop, - cropAlias: "TestCrop", - ratioMode: ImageCropRatioMode.Width, - height: 50); - - imageUrlGenerator - .Verify(x => x.GetImageUrl( - It.Is(y => y.Width == 50 && - y.Height == 50))); - } - [Test] public void GetCropUrl_WithCropSpecifiedAndWidthRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() { @@ -130,7 +111,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions CreateImageCropperValueWithCrops(), imageCropMode: ImageCropMode.Crop, cropAlias: "TestCrop", - ratioMode: ImageCropRatioMode.Width, width: 35, height: 50); @@ -140,25 +120,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions y.Height == 50))); } - [Test] - public void GetCropUrl_WithCropSpecifiedAndHeightRatioModeProvidedWithWidth_CallsImageGeneratorWithCorrectParameters() - { - var imageUrl = "/test.jpg"; - Mock imageUrlGenerator = CreateMockImageUrlGenerator(); - var result = imageUrl.GetCropUrl( - imageUrlGenerator.Object, - CreateImageCropperValueWithCrops(), - imageCropMode: ImageCropMode.Crop, - cropAlias: "TestCrop", - ratioMode: ImageCropRatioMode.Height, - width: 60); - - imageUrlGenerator - .Verify(x => x.GetImageUrl( - It.Is(y => y.Width == 60 && - y.Height == 60))); - } - [Test] public void GetCropUrl_WithCropSpecifiedAndHeightRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() { @@ -169,7 +130,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions CreateImageCropperValueWithCrops(), imageCropMode: ImageCropMode.Crop, cropAlias: "TestCrop", - ratioMode: ImageCropRatioMode.Height, width: 60, height: 40); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs index 599324b040..ce5e62d799 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/ImageCropperTest.cs @@ -145,7 +145,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common [Test] public void GetCropUrlFurtherOptionsTest() { - var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff"); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "filter=comic&roundedcorners=radius-26|bgcolor-fff"); Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString); } @@ -176,7 +176,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common [Test] public void GetCropUrl_CropAliasHeightRatioModeTest() { - var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode: ImageCropRatioMode.Height); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true); Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString); } @@ -186,7 +186,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common [Test] public void GetCropUrl_WidthHeightRatioModeTest() { - var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Height); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150); Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString); } @@ -196,7 +196,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common [Test] public void GetCropUrl_HeightWidthRatioModeTest() { - var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150); Assert.AreEqual(MediaPath + "?f=0.80827067669172936,0.96&w=300&h=150", urlString); } @@ -321,7 +321,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common { var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + MediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; - var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff"); + var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "bgcolor=fff"); Assert.AreEqual(MediaPath + "?m=pad&w=400&h=400&bgcolor=fff", urlString); } diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs index 19090ee12e..38f6f47235 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs @@ -82,9 +82,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: /// - /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -100,8 +99,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, bool cacheBuster = true, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) => mediaItem.GetCropUrl( ImageUrlGenerator, PublishedValueFallback, @@ -116,8 +114,7 @@ namespace Umbraco.Extensions preferFocalPoint, useCropDimensions, cacheBuster, - furtherOptions, - ratioMode + furtherOptions ); /// @@ -136,9 +133,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: /// - /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -154,8 +150,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, string cacheBusterValue = null, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) => imageUrl.GetCropUrl( ImageUrlGenerator, width, @@ -168,8 +163,7 @@ namespace Umbraco.Extensions preferFocalPoint, useCropDimensions, cacheBusterValue, - furtherOptions, - ratioMode + furtherOptions ); /// @@ -188,9 +182,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: /// - /// Use a dimension as a ratio /// /// The URL of the cropped image. /// @@ -206,8 +199,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, string cacheBusterValue = null, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) => imageUrl.GetCropUrl( ImageUrlGenerator, cropDataSet, @@ -219,8 +211,7 @@ namespace Umbraco.Extensions preferFocalPoint, useCropDimensions, cacheBusterValue, - furtherOptions, - ratioMode + furtherOptions ); diff --git a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs index a6d82e3352..ae367b1cf9 100644 --- a/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensions.cs @@ -103,9 +103,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: /// - /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -124,8 +123,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, bool cacheBuster = true, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); + string furtherOptions = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions); public static string GetCropUrl( this MediaWithCrops mediaWithCrops, @@ -142,15 +140,14 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, bool cacheBuster = true, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) { if (mediaWithCrops == null) { throw new ArgumentNullException(nameof(mediaWithCrops)); } - return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); + return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions); } private static string GetCropUrl( @@ -170,8 +167,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, bool cacheBuster = true, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) { if (mediaItem == null) { @@ -219,7 +215,7 @@ namespace Umbraco.Extensions return GetCropUrl( mediaItemUrl, imageUrlGenerator, localCrops, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, - cacheBusterValue, furtherOptions, ratioMode); + cacheBusterValue, furtherOptions); } /// @@ -239,9 +235,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: /// - /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -258,8 +253,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, string cacheBusterValue = null, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) { if (string.IsNullOrWhiteSpace(imageUrl)) { @@ -274,7 +268,7 @@ namespace Umbraco.Extensions return GetCropUrl( imageUrl, imageUrlGenerator, cropDataSet, width, height, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions); } /// @@ -294,9 +288,8 @@ namespace Umbraco.Extensions /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated. /// These are any query string parameters (formatted as query strings) that the underlying image processing service supports. For example: /// - /// Use a dimension as a ratio. /// /// The URL of the cropped image. /// @@ -313,8 +306,7 @@ namespace Umbraco.Extensions bool preferFocalPoint = false, bool useCropDimensions = false, string cacheBusterValue = null, - string furtherOptions = null, - ImageCropRatioMode? ratioMode = null) + string furtherOptions = null) { if (string.IsNullOrWhiteSpace(imageUrl)) { @@ -334,14 +326,14 @@ namespace Umbraco.Extensions options = cropDataSet.GetCropBaseOptions(imageUrl, crop, preferFocalPoint || string.IsNullOrWhiteSpace(cropAlias)); - if (crop != null & useCropDimensions) + if (crop != null && useCropDimensions) { width = crop.Width; height = crop.Height; } - // Calculate missing dimension if a predefined crop has been specified, there are no coordinates and no ratio mode - if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null && ratioMode == null) + // Calculate missing dimension if a predefined crop has been specified, but has no coordinates + if (crop != null && string.IsNullOrEmpty(cropAlias) == false && crop.Coordinates == null) { if (width != null && height == null) { @@ -357,41 +349,14 @@ namespace Umbraco.Extensions { options = new ImageUrlGenerationOptions(imageUrl) { - ImageCropMode = (imageCropMode ?? ImageCropMode.Pad), + ImageCropMode = (imageCropMode ?? ImageCropMode.Pad), // Not sure why we default to Pad ImageCropAnchor = imageCropAnchor }; } options.Quality = quality; - options.Width = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Width ? null : width; - options.Height = ratioMode != null && ratioMode.Value == ImageCropRatioMode.Height ? null : height; - - if (ratioMode == ImageCropRatioMode.Width && height != null) - { - // If only height specified then assume a square - if (width == null) - { - options.Width = height; - } - else - { - options.Width = (int)MathF.Round(height.Value * ((float)width.Value / height.Value)); - } - } - - if (ratioMode == ImageCropRatioMode.Height && width != null) - { - // If only width specified then assume a square - if (height == null) - { - options.Height = width; - } - else - { - options.Height = (int)MathF.Round(width.Value * ((float)height.Value / width.Value)); - } - } - + options.Width = width; + options.Height = height; options.FurtherOptions = furtherOptions; options.CacheBusterValue = cacheBusterValue; diff --git a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs index a6dd4ac1ca..aefd102725 100644 --- a/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/UrlHelperExtensions.cs @@ -246,7 +246,6 @@ namespace Umbraco.Extensions bool useCropDimensions = false, bool cacheBuster = true, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, bool htmlEncode = true) { if (mediaItem == null) @@ -255,7 +254,7 @@ namespace Umbraco.Extensions } var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions); return CreateHtmlString(url, htmlEncode); } @@ -272,14 +271,13 @@ namespace Umbraco.Extensions bool useCropDimensions = true, string cacheBusterValue = null, string furtherOptions = null, - ImageCropRatioMode? ratioMode = null, bool htmlEncode = true) { if (imageCropperValue == null) return HtmlString.Empty; var imageUrl = imageCropperValue.Src; var url = imageUrl.GetCropUrl(imageCropperValue, width, height, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions); return CreateHtmlString(url, htmlEncode); } From 8ca35f598b0ebc0525bc45cb0d85303e5eb0480f Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 12 Aug 2021 10:08:41 +0200 Subject: [PATCH 31/54] Removed two effectively duplicate tests. --- ...ImageCropperTemplateCoreExtensionsTests.cs | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs index 2d16805a89..b3b1e64f94 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Extensions/ImageCropperTemplateCoreExtensionsTests.cs @@ -101,44 +101,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Extensions y.Height == 50))); } - [Test] - public void GetCropUrl_WithCropSpecifiedAndWidthRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() - { - var imageUrl = "/test.jpg"; - Mock imageUrlGenerator = CreateMockImageUrlGenerator(); - var result = imageUrl.GetCropUrl( - imageUrlGenerator.Object, - CreateImageCropperValueWithCrops(), - imageCropMode: ImageCropMode.Crop, - cropAlias: "TestCrop", - width: 35, - height: 50); - - imageUrlGenerator - .Verify(x => x.GetImageUrl( - It.Is(y => y.Width == 35 && - y.Height == 50))); - } - - [Test] - public void GetCropUrl_WithCropSpecifiedAndHeightRatioModeProvidedWithWidthAndHeight_CallsImageGeneratorWithCorrectParameters() - { - var imageUrl = "/test.jpg"; - Mock imageUrlGenerator = CreateMockImageUrlGenerator(); - var result = imageUrl.GetCropUrl( - imageUrlGenerator.Object, - CreateImageCropperValueWithCrops(), - imageCropMode: ImageCropMode.Crop, - cropAlias: "TestCrop", - width: 60, - height: 40); - - imageUrlGenerator - .Verify(x => x.GetImageUrl( - It.Is(y => y.Width == 60 && - y.Height == 40))); - } - private static Mock CreateMockImageUrlGenerator() => new Mock(); private static ImageCropperValue CreateImageCropperValueWithCrops() => new ImageCropperValue From 74ea311e3c2f6a9aca5b56c4a8886ba869d4fdc6 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 13 Aug 2021 14:23:27 +0200 Subject: [PATCH 32/54] Remove width/height when both are above the configured maximums, add comments/documentation --- src/Umbraco.Core/Media/IImageUrlGenerator.cs | 18 ++++++++++- .../Media/ImageSharpImageUrlGenerator.cs | 11 +++++-- .../UmbracoBuilder.ImageSharp.cs | 31 +++++++++---------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Core/Media/IImageUrlGenerator.cs b/src/Umbraco.Core/Media/IImageUrlGenerator.cs index c8628f3147..bf61127561 100644 --- a/src/Umbraco.Core/Media/IImageUrlGenerator.cs +++ b/src/Umbraco.Core/Media/IImageUrlGenerator.cs @@ -1,12 +1,28 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Media { + /// + /// Exposes a method that generates an image URL based on the specified options. + /// public interface IImageUrlGenerator { + /// + /// Gets the supported image file types/extensions. + /// + /// + /// The supported image file types/extensions. + /// IEnumerable SupportedImageFileTypes { get; } + /// + /// Gets the image URL based on the specified . + /// + /// The image URL generation options. + /// + /// The generated image URL. + /// string GetImageUrl(ImageUrlGenerationOptions options); } } diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 6331ad24e1..6fc5e46f2c 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -9,12 +9,21 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Infrastructure.Media { + /// + /// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp. + /// + /// public class ImageSharpImageUrlGenerator : IImageUrlGenerator { private static readonly string[] s_supportedImageFileTypes = Configuration.Default.ImageFormats.SelectMany(f => f.FileExtensions).ToArray(); + /// + /// + /// This uses the default instance of the ImageSharp configuration, so we need to ensure we don't new up a different instance when configuring the middleware. + /// public IEnumerable SupportedImageFileTypes { get; } = s_supportedImageFileTypes; + /// public string GetImageUrl(ImageUrlGenerationOptions options) { if (options == null) @@ -82,7 +91,5 @@ namespace Umbraco.Cms.Infrastructure.Media return imageUrl.ToString(); } - - } } diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index ec14091092..6a6231471e 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -27,14 +27,27 @@ namespace Umbraco.Extensions services.AddImageSharp(options => { + // We use the same default configuration instance in ImageSharpImageUrlGenerator, so we don't want to create a new instance here options.Configuration = SixLabors.ImageSharp.Configuration.Default; options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge; options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge; options.CachedNameLength = imagingSettings.Cache.CachedNameLength; + + // Use configurable maximum width and height (overwrite ImageSharps default) options.OnParseCommandsAsync = context => { - RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Width, imagingSettings.Resize.MaxWidth); - RemoveIntParamenterIfValueGreatherThen(context.Commands, ResizeWebProcessor.Height, imagingSettings.Resize.MaxHeight); + if (context.Commands.Count == 0) + { + return Task.CompletedTask; + } + + uint width = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture); + uint height = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture); + if (width > imagingSettings.Resize.MaxWidth && height > imagingSettings.Resize.MaxHeight) + { + context.Commands.Remove(ResizeWebProcessor.Width); + context.Commands.Remove(ResizeWebProcessor.Height); + } return Task.CompletedTask; }; @@ -55,19 +68,5 @@ namespace Umbraco.Extensions return services; } - - private static void RemoveIntParamenterIfValueGreatherThen(IDictionary commands, string parameter, int maxValue) - { - if (commands.TryGetValue(parameter, out var command)) - { - if (int.TryParse(command, out var i)) - { - if (i > maxValue) - { - commands.Remove(parameter); - } - } - } - } } } From 36568a0fdc173136cf36210a69d1d87975e3a5ed Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 13 Aug 2021 15:53:36 +0200 Subject: [PATCH 33/54] Move ImageSharpImageUrlGenerator to Web.Common and add NoopImageUrlGenerator --- .../UmbracoBuilder.CoreServices.cs | 2 +- .../Media/NoopImageUrlGenerator.cs | 16 ++++++++ .../Media/ImageSharpImageUrlGeneratorTests.cs | 6 +-- .../UmbracoBuilder.ImageSharp.cs | 5 ++- .../Media/ImageSharpImageUrlGenerator.cs | 41 +++++++++++++------ 5 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs rename src/Umbraco.Tests.UnitTests/{Umbraco.Infrastructure => Umbraco.Web.Common}/Media/ImageSharpImageUrlGeneratorTests.cs (98%) rename src/{Umbraco.Infrastructure => Umbraco.Web.Common}/Media/ImageSharpImageUrlGenerator.cs (56%) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 5b7e4a336c..d8e3625591 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -144,7 +144,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.PropertyValueConverters() .Remove(); - builder.Services.AddUnique(); + builder.Services.AddUnique(); // register *all* checks, except those marked [HideFromTypeFinder] of course builder.Services.AddUnique(); diff --git a/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs new file mode 100644 index 0000000000..158f7d6c04 --- /dev/null +++ b/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; +using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Core.Models; + +namespace Umbraco.Cms.Infrastructure.Media +{ + public class NoopImageUrlGenerator : IImageUrlGenerator + { + /// + public IEnumerable SupportedImageFileTypes => Enumerable.Empty(); + + /// + public string GetImageUrl(ImageUrlGenerationOptions options) => options?.ImageUrl; + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs similarity index 98% rename from src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs index 91dd1a8c53..d113f144a3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs @@ -3,9 +3,9 @@ using NUnit.Framework; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Infrastructure.Media; +using Umbraco.Cms.Web.Common.Media; -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Media { [TestFixture] public class ImageSharpImageUrlGeneratorTests @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media private static readonly ImageUrlGenerationOptions.CropCoordinates s_crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m); private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.96m, 0.80827067669172936m); private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.4275m, 0.41m); - private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator(); + private static readonly ImageSharpImageUrlGenerator s_generator = new ImageSharpImageUrlGenerator(new string[0]); [Test] public void GetCropUrl_CropAliasTest() diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 6a6231471e..72fe74bbe6 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -8,7 +8,9 @@ using SixLabors.ImageSharp.Web.DependencyInjection; using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Media; using Umbraco.Cms.Web.Common.ImageProcessors; +using Umbraco.Cms.Web.Common.Media; namespace Umbraco.Extensions { @@ -27,7 +29,6 @@ namespace Umbraco.Extensions services.AddImageSharp(options => { - // We use the same default configuration instance in ImageSharpImageUrlGenerator, so we don't want to create a new instance here options.Configuration = SixLabors.ImageSharp.Configuration.Default; options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge; options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge; @@ -66,6 +67,8 @@ namespace Umbraco.Extensions .AddProcessor() .AddProcessor(); + builder.Services.AddUnique(); + return services; } } diff --git a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs similarity index 56% rename from src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs rename to src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs index 6fc5e46f2c..ead8d3916b 100644 --- a/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs @@ -3,11 +3,14 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; -using SixLabors.ImageSharp; +using Microsoft.Extensions.Options; +using SixLabors.ImageSharp.Web.Middleware; +using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Web.Common.ImageProcessors; -namespace Umbraco.Cms.Infrastructure.Media +namespace Umbraco.Cms.Web.Common.Media { /// /// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp. @@ -15,13 +18,25 @@ namespace Umbraco.Cms.Infrastructure.Media /// public class ImageSharpImageUrlGenerator : IImageUrlGenerator { - private static readonly string[] s_supportedImageFileTypes = Configuration.Default.ImageFormats.SelectMany(f => f.FileExtensions).ToArray(); - /// + public IEnumerable SupportedImageFileTypes { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The options. + public ImageSharpImageUrlGenerator(IOptions options) + : this(options.Value.Configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray()) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The supported image file types/extensions. /// - /// This uses the default instance of the ImageSharp configuration, so we need to ensure we don't new up a different instance when configuring the middleware. + /// This constructor is only used for testing. /// - public IEnumerable SupportedImageFileTypes { get; } = s_supportedImageFileTypes; + internal ImageSharpImageUrlGenerator(IEnumerable supportedImageFileTypes) => SupportedImageFileTypes = supportedImageFileTypes; /// public string GetImageUrl(ImageUrlGenerationOptions options) @@ -46,37 +61,37 @@ namespace Umbraco.Cms.Infrastructure.Media if (options.FocalPoint != null) { - AddQueryString("rxy", options.FocalPoint.Left, options.FocalPoint.Top); + AddQueryString(ResizeWebProcessor.Xy, options.FocalPoint.Left, options.FocalPoint.Top); } if (options.Crop != null) { - AddQueryString("cc", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); + AddQueryString(CropWebProcessor.Coordinates, options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); } if (options.ImageCropMode.HasValue) { - AddQueryString("rmode", options.ImageCropMode.Value.ToString().ToLowerInvariant()); + AddQueryString(ResizeWebProcessor.Mode, options.ImageCropMode.Value.ToString().ToLowerInvariant()); } if (options.ImageCropAnchor.HasValue) { - AddQueryString("ranchor", options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); + AddQueryString(ResizeWebProcessor.Anchor, options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); } if (options.Width.HasValue) { - AddQueryString("width", options.Width.Value); + AddQueryString(ResizeWebProcessor.Width, options.Width.Value); } if (options.Height.HasValue) { - AddQueryString("height", options.Height.Value); + AddQueryString(ResizeWebProcessor.Height, options.Height.Value); } if (options.Quality.HasValue) { - AddQueryString("quality", options.Quality.Value); + AddQueryString(JpegQualityWebProcessor.Quality, options.Quality.Value); } if (string.IsNullOrWhiteSpace(options.FurtherOptions) == false) From a0184d555678dee7b27eeb3faec91c58da645388 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Fri, 13 Aug 2021 16:04:44 +0200 Subject: [PATCH 34/54] Remove repeated ImageSharp default configuration --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 72fe74bbe6..df27b5c442 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -29,7 +29,6 @@ namespace Umbraco.Extensions services.AddImageSharp(options => { - options.Configuration = SixLabors.ImageSharp.Configuration.Default; options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge; options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge; options.CachedNameLength = imagingSettings.Cache.CachedNameLength; @@ -52,17 +51,12 @@ namespace Umbraco.Extensions return Task.CompletedTask; }; - options.OnBeforeSaveAsync = _ => Task.CompletedTask; - options.OnProcessedAsync = _ => Task.CompletedTask; - options.OnPrepareResponseAsync = _ => Task.CompletedTask; }) - .SetRequestParser() .Configure(options => { options.CacheFolder = imagingSettings.Cache.CacheFolder; }) - .SetCache() - .SetCacheHash() + // We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed) .RemoveProcessor() .AddProcessor() .AddProcessor(); From 96cbf1622f5c7620f542c8a97e602944107b11b1 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Fri, 13 Aug 2021 19:46:37 +0200 Subject: [PATCH 35/54] Fix expansion panel header causing horizontal overflow --- .../src/less/components/html/umb-expansion-panel.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less index ea08794c23..a686935c67 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/html/umb-expansion-panel.less @@ -6,6 +6,7 @@ } .umb-expansion-panel__header { + box-sizing: border-box; padding: 10px 20px; font-weight: bold; display: flex; From a79aec85d76c5e626855e126c1261e8259f987c9 Mon Sep 17 00:00:00 2001 From: Gunnar Date: Fri, 13 Aug 2021 22:03:15 +0000 Subject: [PATCH 36/54] SystemLock - Update link to msdn blog --- src/Umbraco.Core/SystemLock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/SystemLock.cs b/src/Umbraco.Core/SystemLock.cs index a4224560c1..a8647f6cc8 100644 --- a/src/Umbraco.Core/SystemLock.cs +++ b/src/Umbraco.Core/SystemLock.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Umbraco.Cms.Core { - // http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/10266988.aspx + // https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-6-asynclock/ // // notes: // - this is NOT a reader/writer lock From f9347803003d50ec11f6fc4d3e85f7f77f147c2d Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sun, 15 Aug 2021 18:38:17 +1200 Subject: [PATCH 37/54] Process requires disposal --- src/Umbraco.Core/Diagnostics/MiniDump.cs | 38 ++++++++++--------- .../Persistence/LocalDb.cs | 16 ++++---- .../Sync/DatabaseServerMessenger.cs | 18 +++++---- 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Core/Diagnostics/MiniDump.cs b/src/Umbraco.Core/Diagnostics/MiniDump.cs index da5a729148..25f6e530e1 100644 --- a/src/Umbraco.Core/Diagnostics/MiniDump.cs +++ b/src/Umbraco.Core/Diagnostics/MiniDump.cs @@ -79,26 +79,28 @@ namespace Umbraco.Cms.Core.Diagnostics private static bool Write(IMarchal marchal, SafeHandle fileHandle, Option options, bool withException = false) { - var currentProcess = Process.GetCurrentProcess(); - var currentProcessHandle = currentProcess.Handle; - var currentProcessId = (uint)currentProcess.Id; - - MiniDumpExceptionInformation exp; - - exp.ThreadId = GetCurrentThreadId(); - exp.ClientPointers = false; - exp.ExceptionPointers = IntPtr.Zero; - - if (withException) + using (var currentProcess = Process.GetCurrentProcess()) { - exp.ExceptionPointers = marchal.GetExceptionPointers(); + var currentProcessHandle = currentProcess.Handle; + var currentProcessId = (uint)currentProcess.Id; + + MiniDumpExceptionInformation exp; + + exp.ThreadId = GetCurrentThreadId(); + exp.ClientPointers = false; + exp.ExceptionPointers = IntPtr.Zero; + + if (withException) + { + exp.ExceptionPointers = marchal.GetExceptionPointers(); + } + + var bRet = exp.ExceptionPointers == IntPtr.Zero + ? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) + : MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero); + + return bRet; } - - var bRet = exp.ExceptionPointers == IntPtr.Zero - ? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) - : MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero); - - return bRet; } public static bool Dump(IMarchal marchal, IHostingEnvironment hostingEnvironment, Option options = Option.WithFullMemory, bool withException = false) diff --git a/src/Umbraco.Infrastructure/Persistence/LocalDb.cs b/src/Umbraco.Infrastructure/Persistence/LocalDb.cs index 1a24a30066..0ca98d2b14 100644 --- a/src/Umbraco.Infrastructure/Persistence/LocalDb.cs +++ b/src/Umbraco.Infrastructure/Persistence/LocalDb.cs @@ -906,7 +906,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence return -1; } - var p = new Process + using (var p = new Process { StartInfo = { @@ -918,13 +918,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden } - }; - p.Start(); - output = p.StandardOutput.ReadToEnd(); - error = p.StandardError.ReadToEnd(); - p.WaitForExit(); + }) + { + p.Start(); + output = p.StandardOutput.ReadToEnd(); + error = p.StandardError.ReadToEnd(); + p.WaitForExit(); - return p.ExitCode; + return p.ExitCode; + } } /// diff --git a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs index ee8793f5c9..10556b7fe6 100644 --- a/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Infrastructure/Sync/DatabaseServerMessenger.cs @@ -72,15 +72,17 @@ namespace Umbraco.Cms.Infrastructure.Sync GlobalSettings = globalSettings.Value; _lastPruned = _lastSync = DateTime.UtcNow; _syncIdle = new ManualResetEvent(true); - - // See notes on _localIdentity - LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER - + "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT - + " [P" + Process.GetCurrentProcess().Id // eg 1234 - + "/D" + AppDomain.CurrentDomain.Id // eg 22 - + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique - + using (var process = Process.GetCurrentProcess()) + { + // See notes on _localIdentity + LocalIdentity = Environment.MachineName // eg DOMAIN\SERVER + + "/" + hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT + + " [P" + process.Id // eg 1234 + + "/D" + AppDomain.CurrentDomain.Id // eg 22 + + "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique + } _initialized = new Lazy(InitializeWithMainDom); + } public GlobalSettings GlobalSettings { get; } From 9457d734997bc39c2ae563e5d36d3d9c5a192f59 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sun, 15 Aug 2021 18:51:26 +1200 Subject: [PATCH 38/54] Avoid mixing using of string interpolation and string formatting. MessageFormat parameter is cached and should be a constant string. --- .../Examine/ExamineIndexRebuilder.cs | 2 +- src/Umbraco.PublishedCache.NuCache/ContentStore.cs | 12 ++++++------ .../PublishedSnapshotService.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs index 472321ac9e..ef6d361970 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexRebuilder.cs @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Infrastructure.Examine if (useBackgroundThread) { - _logger.LogInformation($"Starting async background thread for rebuilding index {indexName}."); + _logger.LogInformation("Starting async background thread for rebuilding index {indexName}.",indexName); _backgroundTaskQueue.QueueBackgroundWorkItem( cancellationToken => Task.Run(() => RebuildIndex(indexName, delay.Value, cancellationToken))); diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs index f7c3b987e4..d83d91c0db 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -524,7 +524,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache parent = GetParentLink(kit.Node, null); if (parent == null) { - _logger.LogWarning($"Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}."); + _logger.LogWarning("Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}.", kit.Node.Id, kit.Node.ParentContentId); return false; } @@ -533,21 +533,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // because the data sort operation is by path. if (parent.Value == null) { - _logger.LogWarning($"Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues."); + _logger.LogWarning("Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.ParentContentId); return false; } // make sure the kit is valid if (kit.DraftData == null && kit.PublishedData == null) { - _logger.LogWarning($"Skip item id={kit.Node.Id}, both draft and published data are null."); + _logger.LogWarning("Skip item id={kit.Node.Id}, both draft and published data are null.", kit.Node.Id); return false; } // unknown = bad if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null) { - _logger.LogWarning($"Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}."); + _logger.LogWarning("Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}.", kit.Node.Id, kit.ContentTypeId); return false; } @@ -723,7 +723,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache previousNode = null; // there is no previous sibling } - _logger.LogDebug($"Set {thisNode.Id} with parent {thisNode.ParentContentId}"); + _logger.LogDebug("Set {thisNode.Id} with parent {thisNode.ParentContentId}", thisNode.Id, thisNode.ParentContentId); SetValueLocked(_contentNodes, thisNode.Id, thisNode); // if we are initializing from the database source ensure the local db is updated @@ -780,7 +780,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache ok = false; continue; // skip that one } - _logger.LogDebug($"Set {kit.Node.Id} with parent {kit.Node.ParentContentId}"); + _logger.LogDebug("Set {kit.Node.Id} with parent {kit.Node.ParentContentId}", kit.Node.Id, kit.Node.ParentContentId); SetValueLocked(_contentNodes, kit.Node.Id, kit.Node); if (_localDb != null) RegisterChange(kit.Node.Id, kit); diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index f73b5b505e..3fcab95ef2 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -456,7 +456,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache // Update: We will still return false here even though the above mentioned race condition has been fixed since we now // lock the entire operation of creating/populating the cache file with the same lock as releasing/closing the cache file - _logger.LogInformation($"Tried to load {entityType} from the local cache file but it was empty."); + _logger.LogInformation("Tried to load {entityType} from the local cache file but it was empty.", entityType); return false; } From c72e4f224a666c77628ea52cd4accbf6168d4648 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sun, 15 Aug 2021 19:02:05 +1200 Subject: [PATCH 39/54] use existing CharArrays for TrimStart() to reduce array allocations --- src/Umbraco.Core/Constants-CharArrays.cs | 8 +++++++- src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs | 2 +- src/Umbraco.Core/Routing/UmbracoRequestPaths.cs | 6 +++--- .../AspNetCore/AspNetCoreHostingEnvironment.cs | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Constants-CharArrays.cs b/src/Umbraco.Core/Constants-CharArrays.cs index 0d1722f7eb..e1318d9ae9 100644 --- a/src/Umbraco.Core/Constants-CharArrays.cs +++ b/src/Umbraco.Core/Constants-CharArrays.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core +namespace Umbraco.Cms.Core { public static partial class Constants { @@ -78,6 +78,12 @@ /// public static readonly char[] TildeForwardSlash = new char[] { '~', '/' }; + + /// + /// Char array containing ~ / \ + /// + public static readonly char[] TildeForwardSlashBackSlash = new char[] { '~', '/', "\\" }; + /// /// Char array containing only ? /// diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index 8a48625cfe..b94ad6d496 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -86,7 +86,7 @@ namespace Umbraco.Cms.Core.Routing // and then the comparisons in IsMatch can be way faster - and allocate way less strings const string propertyAlias = Constants.Conventions.Content.UrlAlias; - var test1 = alias.TrimStart('/') + ","; + var test1 = alias.TrimStart(Constants.CharArrays.ForwardSlash) + ","; var test2 = ",/" + test1; // test2 is ",/alias," test1 = "," + test1; // test1 is ",alias," diff --git a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs index 084b9b47e3..5d298b811a 100644 --- a/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs +++ b/src/Umbraco.Core/Routing/UmbracoRequestPaths.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Core.Routing public UmbracoRequestPaths(IOptions globalSettings, IHostingEnvironment hostingEnvironment) { var applicationPath = hostingEnvironment.ApplicationVirtualPath; - _appPath = applicationPath.TrimStart('/'); + _appPath = applicationPath.TrimStart(Constants.CharArrays.ForwardSlash); _backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment) .EnsureStartsWith('/').TrimStart(_appPath.EnsureStartsWith('/')).EnsureStartsWith('/'); @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Core.Routing /// public bool IsBackOfficeRequest(string absPath) { - var fullUrlPath = absPath.TrimStart('/'); + var fullUrlPath = absPath.TrimStart(Constants.CharArrays.ForwardSlash); var urlPath = fullUrlPath.TrimStart(_appPath).EnsureStartsWith('/'); // check if this is in the umbraco back office @@ -108,7 +108,7 @@ namespace Umbraco.Cms.Core.Routing // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a // plugin controller for the front-end. - if (urlPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Length >= 3) + if (urlPath.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries).Length >= 3) { return false; } diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs index 0b9801f871..bb87ea62bf 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs @@ -141,7 +141,7 @@ namespace Umbraco.Cms.Web.Common.AspNetCore throw new ArgumentException("The path appears to already be fully qualified. Please remove the call to MapPath"); } - return Path.Combine(root, newPath.TrimStart('~', '/', '\\')); + return Path.Combine(root, newPath.TrimStart(Core.Constants.CharArrays.TildeForwardSlashBackSlash)); } /// From c8777c25fffb8fee0a599f84b1a26e535b1ccc93 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sun, 15 Aug 2021 19:04:36 +1200 Subject: [PATCH 40/54] Remove _inMemoryModelFactory.ModelsChanged event handler on dispose --- .../ModelsBuilder/RefreshingRazorViewEngine.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs b/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs index a541d61368..46dba59aba 100644 --- a/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs +++ b/src/Umbraco.Web.Common/ModelsBuilder/RefreshingRazorViewEngine.cs @@ -180,6 +180,7 @@ namespace Umbraco.Cms.Web.Common.ModelsBuilder { if (disposing) { + _inMemoryModelFactory.ModelsChanged -= InMemoryModelFactoryModelsChanged; _locker.Dispose(); } From 2b6b62037018699960fd9541025f42a094f1a173 Mon Sep 17 00:00:00 2001 From: Chad Currie Date: Sun, 15 Aug 2021 19:36:41 +1200 Subject: [PATCH 41/54] fix backslash --- src/Umbraco.Core/Constants-CharArrays.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Constants-CharArrays.cs b/src/Umbraco.Core/Constants-CharArrays.cs index e1318d9ae9..4be5ecba04 100644 --- a/src/Umbraco.Core/Constants-CharArrays.cs +++ b/src/Umbraco.Core/Constants-CharArrays.cs @@ -82,7 +82,7 @@ namespace Umbraco.Cms.Core /// /// Char array containing ~ / \ /// - public static readonly char[] TildeForwardSlashBackSlash = new char[] { '~', '/', "\\" }; + public static readonly char[] TildeForwardSlashBackSlash = new char[] { '~', '/', '\\' }; /// /// Char array containing only ? From 42c6aa8d565966a9c7af269e1cf1bdc90bb0c9c5 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 16 Aug 2021 10:35:37 +0200 Subject: [PATCH 42/54] Register default ImageSharp configuration for application-wide use --- .../UmbracoBuilder.CoreServices.cs | 2 ++ .../Media/ImageDimensionExtractor.cs | 13 +++++++- .../ImageSharpConfigurationOptions.cs | 30 +++++++++++++++++++ .../UmbracoBuilder.ImageSharp.cs | 10 ++++--- .../Media/ImageSharpImageUrlGenerator.cs | 9 +++--- 5 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index d8e3625591..393fb60df4 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -180,6 +180,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); + // Add default ImageSharp configuration + builder.Services.AddUnique(SixLabors.ImageSharp.Configuration.Default); builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs index f8dc089a0d..380704c26c 100644 --- a/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs +++ b/src/Umbraco.Infrastructure/Media/ImageDimensionExtractor.cs @@ -8,6 +8,17 @@ namespace Umbraco.Cms.Infrastructure.Media { internal class ImageDimensionExtractor : IImageDimensionExtractor { + /// + /// The ImageSharp configuration. + /// + private readonly Configuration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The ImageSharp configuration. + public ImageDimensionExtractor(Configuration configuration) => _configuration = configuration; + /// /// Gets the dimensions of an image. /// @@ -39,7 +50,7 @@ namespace Umbraco.Cms.Infrastructure.Media stream.Seek(0, SeekOrigin.Begin); } - using (var image = Image.Load(stream)) + using (var image = Image.Load(_configuration, stream)) { var fileWidth = image.Width; var fileHeight = image.Height; diff --git a/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs new file mode 100644 index 0000000000..86b1dd8a5c --- /dev/null +++ b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Options; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Web.Middleware; + +namespace Umbraco.Cms.Web.Common.DependencyInjection +{ + /// + /// Configures the ImageSharp middleware options to use the registered configuration. + /// + /// + internal class ImageSharpConfigurationOptions : IConfigureOptions + { + /// + /// The ImageSharp configuration. + /// + private readonly Configuration _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The ImageSharp configuration. + public ImageSharpConfigurationOptions(Configuration configuration) => _configuration = configuration; + + /// + /// Invoked to configure a instance. + /// + /// The options instance to configure. + public void Configure(ImageSharpMiddlewareOptions options) => options.Configuration = _configuration; + } +} diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index df27b5c442..5661fd1487 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -2,13 +2,16 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using SixLabors.ImageSharp.Web.Caching; using SixLabors.ImageSharp.Web.Commands; using SixLabors.ImageSharp.Web.DependencyInjection; +using SixLabors.ImageSharp.Web.Middleware; using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.ImageProcessors; using Umbraco.Cms.Web.Common.Media; @@ -29,6 +32,7 @@ namespace Umbraco.Extensions services.AddImageSharp(options => { + // The configuration is set using ImageSharpConfigurationOptions options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge; options.CacheMaxAge = imagingSettings.Cache.CacheMaxAge; options.CachedNameLength = imagingSettings.Cache.CachedNameLength; @@ -52,15 +56,13 @@ namespace Umbraco.Extensions return Task.CompletedTask; }; }) - .Configure(options => - { - options.CacheFolder = imagingSettings.Cache.CacheFolder; - }) + .Configure(options => options.CacheFolder = imagingSettings.Cache.CacheFolder) // We need to add CropWebProcessor before ResizeWebProcessor (until https://github.com/SixLabors/ImageSharp.Web/issues/182 is fixed) .RemoveProcessor() .AddProcessor() .AddProcessor(); + builder.Services.AddTransient, ImageSharpConfigurationOptions>(); builder.Services.AddUnique(); return services; diff --git a/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs index ead8d3916b..276a3ab492 100644 --- a/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; -using Microsoft.Extensions.Options; -using SixLabors.ImageSharp.Web.Middleware; +using SixLabors.ImageSharp; using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; @@ -24,9 +23,9 @@ namespace Umbraco.Cms.Web.Common.Media /// /// Initializes a new instance of the class. /// - /// The options. - public ImageSharpImageUrlGenerator(IOptions options) - : this(options.Value.Configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray()) + /// The ImageSharp configuration. + public ImageSharpImageUrlGenerator(Configuration configuration) + : this(configuration.ImageFormats.SelectMany(f => f.FileExtensions).ToArray()) { } /// From 992eb5802326a1282d277d73e7e4d073b35fc522 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 16 Aug 2021 11:08:09 +0200 Subject: [PATCH 43/54] Move ImageSharpImageUrlGenerator back to Infrastructure and remove NoopImageUrlGenerator --- .../UmbracoBuilder.CoreServices.cs | 5 ++--- .../Media/ImageSharpImageUrlGenerator.cs | 18 ++++++++---------- .../Media/NoopImageUrlGenerator.cs | 16 ---------------- .../Media/ImageSharpImageUrlGeneratorTests.cs | 5 ++--- .../UmbracoBuilder.ImageSharp.cs | 12 +++--------- 5 files changed, 15 insertions(+), 41 deletions(-) rename src/{Umbraco.Web.Common => Umbraco.Infrastructure}/Media/ImageSharpImageUrlGenerator.cs (78%) delete mode 100644 src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs rename src/Umbraco.Tests.UnitTests/{Umbraco.Web.Common => Umbraco.Infrastructure}/Media/ImageSharpImageUrlGeneratorTests.cs (98%) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 393fb60df4..8388841e17 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -144,8 +144,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.PropertyValueConverters() .Remove(); - builder.Services.AddUnique(); - // register *all* checks, except those marked [HideFromTypeFinder] of course builder.Services.AddUnique(); @@ -180,9 +178,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection builder.Services.AddUnique(); - // Add default ImageSharp configuration + // Add default ImageSharp configuration and service implementations builder.Services.AddUnique(SixLabors.ImageSharp.Configuration.Default); builder.Services.AddUnique(); + builder.Services.AddUnique(); builder.Services.AddUnique(); diff --git a/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs similarity index 78% rename from src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs rename to src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs index 276a3ab492..ecde6790d2 100644 --- a/src/Umbraco.Web.Common/Media/ImageSharpImageUrlGenerator.cs +++ b/src/Umbraco.Infrastructure/Media/ImageSharpImageUrlGenerator.cs @@ -4,12 +4,10 @@ using System.Globalization; using System.Linq; using System.Text; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Web.Common.ImageProcessors; -namespace Umbraco.Cms.Web.Common.Media +namespace Umbraco.Cms.Infrastructure.Media { /// /// Exposes a method that generates an image URL based on the specified options that can be processed by ImageSharp. @@ -60,37 +58,37 @@ namespace Umbraco.Cms.Web.Common.Media if (options.FocalPoint != null) { - AddQueryString(ResizeWebProcessor.Xy, options.FocalPoint.Left, options.FocalPoint.Top); + AddQueryString("rxy", options.FocalPoint.Left, options.FocalPoint.Top); } if (options.Crop != null) { - AddQueryString(CropWebProcessor.Coordinates, options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); + AddQueryString("cc", options.Crop.Left, options.Crop.Top, options.Crop.Right, options.Crop.Bottom); } if (options.ImageCropMode.HasValue) { - AddQueryString(ResizeWebProcessor.Mode, options.ImageCropMode.Value.ToString().ToLowerInvariant()); + AddQueryString("rmode", options.ImageCropMode.Value.ToString().ToLowerInvariant()); } if (options.ImageCropAnchor.HasValue) { - AddQueryString(ResizeWebProcessor.Anchor, options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); + AddQueryString("ranchor", options.ImageCropAnchor.Value.ToString().ToLowerInvariant()); } if (options.Width.HasValue) { - AddQueryString(ResizeWebProcessor.Width, options.Width.Value); + AddQueryString("width", options.Width.Value); } if (options.Height.HasValue) { - AddQueryString(ResizeWebProcessor.Height, options.Height.Value); + AddQueryString("height", options.Height.Value); } if (options.Quality.HasValue) { - AddQueryString(JpegQualityWebProcessor.Quality, options.Quality.Value); + AddQueryString("quality", options.Quality.Value); } if (string.IsNullOrWhiteSpace(options.FurtherOptions) == false) diff --git a/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs b/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs deleted file mode 100644 index 158f7d6c04..0000000000 --- a/src/Umbraco.Infrastructure/Media/NoopImageUrlGenerator.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Cms.Core.Media; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Infrastructure.Media -{ - public class NoopImageUrlGenerator : IImageUrlGenerator - { - /// - public IEnumerable SupportedImageFileTypes => Enumerable.Empty(); - - /// - public string GetImageUrl(ImageUrlGenerationOptions options) => options?.ImageUrl; - } -} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs similarity index 98% rename from src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs rename to src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs index d113f144a3..a531aa6bbd 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Media/ImageSharpImageUrlGeneratorTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Media/ImageSharpImageUrlGeneratorTests.cs @@ -3,9 +3,9 @@ using NUnit.Framework; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Web.Common.Media; +using Umbraco.Cms.Infrastructure.Media; -namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Media +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Media { [TestFixture] public class ImageSharpImageUrlGeneratorTests @@ -74,7 +74,6 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Media Assert.AreEqual("?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100", urlString); } - /// /// Test that if Crop mode is specified as anything other than Crop the image doesn't use the crop /// diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 5661fd1487..5fb8fe2260 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -10,10 +10,8 @@ using SixLabors.ImageSharp.Web.Middleware; using SixLabors.ImageSharp.Web.Processors; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Media; using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Cms.Web.Common.ImageProcessors; -using Umbraco.Cms.Web.Common.Media; namespace Umbraco.Extensions { @@ -24,13 +22,10 @@ namespace Umbraco.Extensions /// public static IServiceCollection AddUmbracoImageSharp(this IUmbracoBuilder builder) { - IConfiguration configuration = builder.Config; - IServiceCollection services = builder.Services; - - ImagingSettings imagingSettings = configuration.GetSection(Cms.Core.Constants.Configuration.ConfigImaging) + ImagingSettings imagingSettings = builder.Config.GetSection(Cms.Core.Constants.Configuration.ConfigImaging) .Get() ?? new ImagingSettings(); - services.AddImageSharp(options => + builder.Services.AddImageSharp(options => { // The configuration is set using ImageSharpConfigurationOptions options.BrowserMaxAge = imagingSettings.Cache.BrowserMaxAge; @@ -63,9 +58,8 @@ namespace Umbraco.Extensions .AddProcessor(); builder.Services.AddTransient, ImageSharpConfigurationOptions>(); - builder.Services.AddUnique(); - return services; + return builder.Services; } } } From 43704da869651967cd8bc636267adb1a9306be3c Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 16 Aug 2021 11:12:54 +0200 Subject: [PATCH 44/54] Remove dimensions if either one is above the configured maximum --- .../DependencyInjection/UmbracoBuilder.ImageSharp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs index 5fb8fe2260..280d48f64b 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilder.ImageSharp.cs @@ -42,7 +42,7 @@ namespace Umbraco.Extensions uint width = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Width), context.Culture); uint height = context.Parser.ParseValue(context.Commands.GetValueOrDefault(ResizeWebProcessor.Height), context.Culture); - if (width > imagingSettings.Resize.MaxWidth && height > imagingSettings.Resize.MaxHeight) + if (width > imagingSettings.Resize.MaxWidth || height > imagingSettings.Resize.MaxHeight) { context.Commands.Remove(ResizeWebProcessor.Width); context.Commands.Remove(ResizeWebProcessor.Height); From 0b53249d5742404e7511d129ae55503ce8673c85 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 16 Aug 2021 12:11:30 +0200 Subject: [PATCH 45/54] Make ImageSharpConfigurationOptions public --- .../DependencyInjection/ImageSharpConfigurationOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs index 86b1dd8a5c..628345dcd6 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/ImageSharpConfigurationOptions.cs @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Web.Common.DependencyInjection /// Configures the ImageSharp middleware options to use the registered configuration. /// /// - internal class ImageSharpConfigurationOptions : IConfigureOptions + public sealed class ImageSharpConfigurationOptions : IConfigureOptions { /// /// The ImageSharp configuration. From b82320c9f3ca3904137e098f9221067b44cf72cc Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 16 Aug 2021 13:26:42 +0200 Subject: [PATCH 46/54] Modifying the cmsLanguageText unique index to be just across the languageId and unqiueId fields --- .../Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs | 4 ++-- .../Persistence/Dtos/LanguageTextDto.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs index 91a8b29bbb..b8f657c6bd 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/DictionaryTablesIndexes.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 public class DictionaryTablesIndexes : MigrationBase { private const string IndexedDictionaryColumn = "key"; - private const string IndexedLanguageTextColumn = "value"; + private const string IndexedLanguageTextColumn = "languageId"; public DictionaryTablesIndexes(IMigrationContext context) : base(context) @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 // Delete existing DeleteIndex(indexLanguageTextDto); - var langTextcolumns = new[] { "languageId", "UniqueId", IndexedLanguageTextColumn }; + var langTextcolumns = new[] { IndexedLanguageTextColumn, "UniqueId" }; // Re-create/Add AddUniqueConstraint(langTextcolumns, indexLanguageTextDto); diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs index 79c2fa088d..0928e828f4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/LanguageTextDto.cs @@ -17,6 +17,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [Column("languageId")] [ForeignKey(typeof(LanguageDto), Column = "id")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_languageId", ForColumns = "languageId,UniqueId")] public int LanguageId { get; set; } [Column("UniqueId")] @@ -25,7 +26,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [Column("value")] [Length(1000)] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_value", ForColumns = "languageId,UniqueId,value")] public string Value { get; set; } } } From 600319cf93bd7dc8458269dfc6d53a839f60e6e8 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 16 Aug 2021 14:14:41 +0200 Subject: [PATCH 47/54] Added missing key value for a test dictionary item --- .../Packaging/PackageDataInstallationTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs index 2f809812d3..2caa8515d1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -766,7 +767,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging new DictionaryItem("Parent") { // This matches what is in the package.xml file - Key = new System.Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"), + Key = new Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"), Translations = new List { new DictionaryTranslation(englishLanguage, expectedEnglishParentValue), @@ -782,6 +783,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging LocalizationService.Save( new DictionaryItem("Parent") { + // This matches what is in the package.xml file + Key = new Guid("28f2e02a-8c66-4fcd-85e3-8524d551c0d3"), Translations = new List { new DictionaryTranslation(englishLanguage, expectedEnglishParentValue), From fba02ea2d64683a5b830f87637e72eeb848dcaf6 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska Date: Mon, 16 Aug 2021 14:21:37 +0200 Subject: [PATCH 48/54] Fix merge conflict --- src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs index fe7724f41d..8fca7bed48 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs @@ -230,10 +230,9 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade // - 8.15 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8} // - 8.15 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901} // - 9.0 RC1 - Current state: {5060F3D2-88BE-4D30-8755-CF51F28EAD12} - To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); + To("{622E5172-42E1-4662-AD80-9504AF5A4E53}"); To("{10F7BB61-C550-426B-830B-7F954F689CDF}"); To("{12DCDE7F-9AB7-4617-804F-AB66BF360980}"); - } } } From 7a431a7184142503f7a61b4ea01b7d3f97e1f148 Mon Sep 17 00:00:00 2001 From: Zeegaan <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 16 Aug 2021 14:57:27 +0200 Subject: [PATCH 49/54] Renamed appsettings to appsettings.template --- .../Umbraco.Web.UI.NetCore.csproj | 15 ++++ .../appsettings.Development.template.json | 59 ++++++++++++++ .../appsettings.template.json | 76 +++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 src/Umbraco.Web.UI.NetCore/appsettings.Development.template.json create mode 100644 src/Umbraco.Web.UI.NetCore/appsettings.template.json diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj index b14848bae5..7979338f47 100644 --- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj +++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj @@ -84,6 +84,7 @@ $(ProjectDir)wwwroot/umbraco $(ProjectDir)umbraco/config/appsettings-schema.json + @@ -100,6 +101,11 @@ + + + + + @@ -107,6 +113,15 @@ + + + + + + + + + diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.Development.template.json b/src/Umbraco.Web.UI.NetCore/appsettings.Development.template.json new file mode 100644 index 0000000000..9cac2c3dd9 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/appsettings.Development.template.json @@ -0,0 +1,59 @@ +{ + "$schema" : "./umbraco/config/appsettings-schema.json", + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Examine.Lucene.Providers.LuceneIndex": "Debug", + "Examine.BaseIndexProvider": "Debug", + "Examine.Lucene.LoggingReplicationClient": "Debug", + "Examine.Lucene.ExamineReplicator": "Debug" + } + }, + "WriteTo": [ + { + "Name": "Async", + "Args": { + "configure": [ + { + "Name": "Console" + } + ] + } + } + ] + }, + "Umbraco": { + "CMS": { + "Examine": { + "LuceneDirectoryFactory": "TempFileSystemDirectoryFactory" + }, + "Global": { + "Smtp": { + //"From": "your@email.here", + //"Host": "localhost", + // "Port": "25" + } + }, + "Hosting": { + "Debug": true + }, + "RuntimeMinification": { + "useInMemoryCache": true, + "cacheBuster": "Timestamp" + }, + "RichTextEditor": { + "Commands": [ + { + "Alias": "fullscreen", + "Name": "Full Screen", + "Mode": "All" + } + ], + "Plugins": [ + "fullscreen" + ] + } + } + } +} diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.template.json b/src/Umbraco.Web.UI.NetCore/appsettings.template.json new file mode 100644 index 0000000000..cef926fad2 --- /dev/null +++ b/src/Umbraco.Web.UI.NetCore/appsettings.template.json @@ -0,0 +1,76 @@ +{ + "$schema": "./umbraco/config/appsettings-schema.json", + "ConnectionStrings": { + "umbracoDbDSN": "" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information", + "System": "Warning" + } + } + }, + "Umbraco": { + "CMS": { + "Content": { + "Notifications": { + "Email": "your@email.here" + }, + "MacroErrors": "Throw" + }, + "Global": { + "DefaultUILanguage": "en-us", + "HideTopLevelNodeFromPath": true, + "UmbracoPath": "~/umbraco", + "TimeOutInMinutes": 20, + "UseHttps": false + }, + "Hosting": { + "Debug": false + }, + "KeepAlive": { + "DisableKeepAliveTask": false, + "KeepAlivePingUrl": "{umbracoApplicationUrl}/api/keepalive/ping" + }, + "RequestHandler": { + "ConvertUrlsToAscii": "try" + }, + "RuntimeMinification": { + "dataFolder": "umbraco/Data/TEMP/Smidge", + "version": "637642136775050602" + }, + "Security": { + "KeepUserLoggedIn": false, + "UsernameIsEmail": true, + "HideDisabledUsersInBackoffice": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\", + "UserPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 + }, + "MemberPassword": { + "RequiredLength": 10, + "RequireNonLetterOrDigit": false, + "RequireDigit": false, + "RequireLowercase": false, + "RequireUppercase": false, + "MaxFailedAccessAttemptsBeforeLockout": 5 + } + }, + "Tours": { + "EnableTours": true + }, + "ModelsBuilder": { + "ModelsMode": "InMemoryAuto", + "Enable": true + } + } + } +} From c39053d7c19a1c8307e57d4c550e2ace6a28b335 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 17 Aug 2021 08:26:32 +0200 Subject: [PATCH 50/54] Bumb version --- .../.template.config/template.json | 2 +- .../.template.config/template.json | 2 +- src/Directory.Build.props | 2 +- src/Umbraco.Web.UI.Client/package-lock.json | 756 +++++++++--------- 4 files changed, 381 insertions(+), 381 deletions(-) diff --git a/build/templates/UmbracoPackage/.template.config/template.json b/build/templates/UmbracoPackage/.template.config/template.json index b2bc86399a..ef32835152 100644 --- a/build/templates/UmbracoPackage/.template.config/template.json +++ b/build/templates/UmbracoPackage/.template.config/template.json @@ -24,7 +24,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-rc001", + "defaultValue": "9.0.0-rc002", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/build/templates/UmbracoProject/.template.config/template.json b/build/templates/UmbracoProject/.template.config/template.json index ba75721bc4..3b34f77130 100644 --- a/build/templates/UmbracoProject/.template.config/template.json +++ b/build/templates/UmbracoProject/.template.config/template.json @@ -57,7 +57,7 @@ "version": { "type": "parameter", "datatype": "string", - "defaultValue": "9.0.0-rc001", + "defaultValue": "9.0.0-rc002", "description": "The version of Umbraco to load using NuGet", "replaces": "UMBRACO_VERSION_FROM_TEMPLATE" }, diff --git a/src/Directory.Build.props b/src/Directory.Build.props index b45e31a4a6..6fdc91dbde 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ 9.0.0 9.0.0 - 9.0.0-rc001 + 9.0.0-rc002 9.0.0 9.0 en-US diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 3b55d1fc91..3fa54b46d3 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -20,7 +20,7 @@ "@babel/core": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.4.tgz", - "integrity": "sha1-br2f4Akl9sPhd7tyahiLX1eAiP8=", + "integrity": "sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", @@ -831,7 +831,7 @@ "@babel/preset-env": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.6.3.tgz", - "integrity": "sha1-nhvwWi4taHA20kxA5GOdxGzvInE=", + "integrity": "sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.0.0", @@ -957,7 +957,7 @@ "@gulp-sourcemaps/identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", - "integrity": "sha1-Hm/l2AJ7HyhdwNMXYvVmvM1z1ak=", + "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", "dev": true, "requires": { "acorn": "^5.0.3", @@ -976,7 +976,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -1086,7 +1086,7 @@ "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { "mime-types": "~2.1.24", @@ -1096,7 +1096,7 @@ "accord": { "version": "0.29.0", "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", - "integrity": "sha1-t0HBdtAENcWSnUZt/oz2vukzseQ=", + "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", "dev": true, "requires": { "convert-source-map": "^1.5.0", @@ -1143,7 +1143,7 @@ "ace-builds": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.2.tgz", - "integrity": "sha1-avwuQ6e17/3ETYQHQ2EShSVo6A0=" + "integrity": "sha512-M1JtZctO2Zg+1qeGUFZXtYKsyaRptqQtqpVzlj80I0NzGW9MF3um0DBuizIvQlrPYUlTdm+wcOPZpZoerkxQdA==" }, "acorn": { "version": "7.1.0", @@ -1240,12 +1240,12 @@ "angular-animate": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.7.5.tgz", - "integrity": "sha1-H/xsKpze4ieiunnMbNj3HsRNtdw=" + "integrity": "sha512-kU/fHIGf2a4a3bH7E1tzALTHk+QfoUSCK9fEcMFisd6ZWvNDwPzXWAilItqOC3EDiAXPmGHaNc9/aXiD9xrAxQ==" }, "angular-aria": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/angular-aria/-/angular-aria-1.7.9.tgz", - "integrity": "sha1-kMYYlf+9h26VkVIisyp70AcK9+M=" + "integrity": "sha512-luI3Jemd1AbOQW0krdzfEG3fM0IFtLY0bSSqIDEx3POE0XjKIC1MkrO8Csyq9PPgueLphyAPofzUwZ8YeZ88SA==" }, "angular-chart.js": { "version": "1.1.1", @@ -1270,12 +1270,12 @@ "angular-cookies": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-cookies/-/angular-cookies-1.7.5.tgz", - "integrity": "sha1-HFqzwFzcQ/F3e+lQbmRYfLNUNjQ=" + "integrity": "sha512-/8xvvSl/Z9Vwu8ChRm+OQE3vmli8Icwl8uTYkHqD7j7cknJP9kNaf7SgsENlsLVtOqLE/I7TCFYrSx3bmSeNQA==" }, "angular-dynamic-locale": { "version": "0.1.37", "resolved": "https://registry.npmjs.org/angular-dynamic-locale/-/angular-dynamic-locale-0.1.37.tgz", - "integrity": "sha1-fon70uxFvdaryJ82zaiJODjkk1Q=", + "integrity": "sha512-m5Kyk8W8/mOZSqRxuByOwHBjv8labLBAgvl0Z3iQx2xT/tWCqb94imKUPwumudszdPDjxeopwyucQvm8Sw7ogw==", "requires": { "@types/angular": "^1.6.25" } @@ -1283,7 +1283,7 @@ "angular-i18n": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-i18n/-/angular-i18n-1.7.5.tgz", - "integrity": "sha1-Lie2Thl3qMa2sFHFHQF1xtTcglI=" + "integrity": "sha512-52+Jpt8HRJV2bqSbSU6fWkwOvGzj/DxbNpKXxnTuCS9heuJrlm77BS/lhrF4BA8+Uudnh7npr5/yRELobP+8Yw==" }, "angular-local-storage": { "version": "0.7.1", @@ -1293,32 +1293,32 @@ "angular-messages": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-messages/-/angular-messages-1.7.5.tgz", - "integrity": "sha1-fC/XgTFaQ6GYOLEX2gFCqYhFThQ=" + "integrity": "sha512-YDpJpFLyrIgZjE/sIAjgww1y6r3QqXBJbNDI0QjftD37vHXLkwvAOo3A4bxPw8BikyGLcJrFrgf6hRAzntJIWA==" }, "angular-mocks": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-mocks/-/angular-mocks-1.7.5.tgz", - "integrity": "sha1-yLq6WgbtYLk0aXAmtJIWliavOEs=" + "integrity": "sha512-I+Ue2Bkx6R9W5178DYrNvzjIdGh4wKKoCWsgz8dc7ysH4mA70Q3M9v5xRF0RUu7r+2CZj+nDeUecvh2paxcYvg==" }, "angular-route": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-route/-/angular-route-1.7.5.tgz", - "integrity": "sha1-NKNkjEB6FKAw0HXPSFMY4zuiPw4=" + "integrity": "sha512-7KfyEVVOWTI+jTY/j5rUNCIHGRyeCOx7YqZI/Ci3IbDK7GIsy6xH+hS5ai0Xi0sLjzDZ0PUDO4gBn+K0dVtlOg==" }, "angular-sanitize": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.7.5.tgz", - "integrity": "sha1-ddSeFQccqccFgedtIJQPJjcuJNI=" + "integrity": "sha512-wjKCJOIwrkEvfD0keTnKGi6We13gtoCAQIHcdoqyoo3gwvcgNfYymVQIS3+iCGVcjfWz0jHuS3KgB4ysRWsTTA==" }, "angular-touch": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/angular-touch/-/angular-touch-1.7.5.tgz", - "integrity": "sha1-7SYyKmhfApmyPLauqYNMEZQk2kY=" + "integrity": "sha512-XNAZNG0RA1mtdwBJheViCF1H/7wOygp4MLIfs5y1K+rne6AeaYKZcV6EJs9fvgfLKLO6ecm1+3J8hoCkdhhxQw==" }, "angular-ui-sortable": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/angular-ui-sortable/-/angular-ui-sortable-0.19.0.tgz", - "integrity": "sha1-SsQ5H8TU3lcRDbS10xp8GY0xT9A=", + "integrity": "sha512-u/uc981Nzg4XN1bMU9qKleMTSt7F1XjMWnyGw6gxPLIeQeLZm8jWNy7tj8y2r2HmvzXFbQVq2z6rObznFKAekQ==", "requires": { "angular": ">=1.2.x", "jquery": ">=3.1.x", @@ -1333,7 +1333,7 @@ "ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha1-Y3S03V1HGP884npnGjscrQdxMqk=", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { "ansi-wrap": "^0.1.0" @@ -1381,7 +1381,7 @@ "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { "color-convert": "^1.9.0" @@ -1561,7 +1561,7 @@ "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { "sprintf-js": "~1.0.2" @@ -1585,7 +1585,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", "dev": true }, "arr-map": { @@ -1635,7 +1635,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -1643,7 +1643,7 @@ "array-last": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha1-eqdwc/7FZd2rJJP1+IGF9ASp0zY=", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", "dev": true, "requires": { "is-number": "^4.0.0" @@ -1652,7 +1652,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -1660,13 +1660,13 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, "array-sort": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha1-5MBTVkU/VvU1EqfR1hI/LFTAqIo=", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, "requires": { "default-compare": "^1.0.0", @@ -1677,7 +1677,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -1703,7 +1703,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, "asap": { @@ -1716,7 +1716,7 @@ "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha1-jSR136tVO7M+d7VOWeiAu4ziMTY=", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { "safer-buffer": "~2.1.0" } @@ -1735,13 +1735,13 @@ "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -1758,7 +1758,7 @@ "async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha1-XhWqcplipLB0FPUoqIzfGOCykKI=", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -1770,13 +1770,13 @@ "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha1-tyfb+H12UWAvBvTUrDh/R9kbDL8=", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", "dev": true }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=" + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "async-settle": { "version": "1.0.0", @@ -1795,13 +1795,13 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "autoprefixer": { "version": "9.6.5", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.5.tgz", - "integrity": "sha1-mPSv5+k8zPMjKHUV1CYBlhl3Xl4=", + "integrity": "sha512-rGd50YV8LgwFQ2WQp4XzOTG69u1qQsXn0amww7tjqV5jJuNazgFKYEVItEBngyyvVITKOg20zr2V+9VsrXJQ2g==", "dev": true, "requires": { "browserslist": "^4.7.0", @@ -1834,7 +1834,7 @@ "babel-plugin-dynamic-import-node": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha1-8A9Qe9qjw+P/bn5emNkKesq5b38=", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", "dev": true, "requires": { "object.assign": "^4.1.0" @@ -1872,7 +1872,7 @@ "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { "cache-base": "^1.0.1", @@ -1896,7 +1896,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1905,7 +1905,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -1914,7 +1914,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -2257,7 +2257,7 @@ "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha1-WYr+VHVbKGilMw0q/51Ou1Mgm2U=", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, "bl": { @@ -2309,7 +2309,7 @@ "blob": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha1-1oDu7yX4zZGtUz9bAe7UjmTK9oM=", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", "dev": true }, "bluebird": { @@ -2321,7 +2321,7 @@ "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { "bytes": "3.1.0", @@ -2339,7 +2339,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -2354,7 +2354,7 @@ "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true } } @@ -2368,7 +2368,7 @@ "bootstrap": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", - "integrity": "sha1-w6NH1Bniia0R9AM+PEEyuHwIHXI=" + "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==" }, "bootstrap-social": { "version": "5.1.1", @@ -2382,7 +2382,7 @@ "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -2392,7 +2392,7 @@ "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { "arr-flatten": "^1.1.0", @@ -2449,7 +2449,7 @@ "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", @@ -2459,7 +2459,7 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", "dev": true }, "buffer-crc32": { @@ -2484,7 +2484,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "bufferstreams": { @@ -2499,13 +2499,13 @@ "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { "collection-visit": "^1.0.0", @@ -2606,7 +2606,7 @@ }, "callsites": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true }, @@ -2639,7 +2639,7 @@ "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha1-Xk2Q4idJYdRikZl99Znj7QCO5MA=", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -2685,7 +2685,7 @@ "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "requires": { "ansi-styles": "^3.2.1", @@ -2696,7 +2696,7 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "chart.js": { @@ -2720,7 +2720,7 @@ "chartjs-color-string": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", - "integrity": "sha1-HfCWYhwOcHIKZPQTXqFx0FFAL3E=", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", "requires": { "color-name": "^1.0.0" } @@ -2728,7 +2728,7 @@ "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha1-gEs6e2qZNYw8XGHnHYco8EHP+Rc=", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -2778,7 +2778,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true } } @@ -2786,7 +2786,7 @@ "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -2809,7 +2809,7 @@ "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha1-LUEe92uFabbQyEBo2r6FsKpeXBc=", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "dev": true, "requires": { "source-map": "~0.6.0" @@ -2818,7 +2818,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -2826,7 +2826,7 @@ "cli-color": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha1-fRBzj0hSaCT4/n2lGFfLD1cv4B8=", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", "dev": true, "requires": { "ansi-regex": "^2.1.1", @@ -2855,7 +2855,7 @@ "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha1-g22v1mzw/qXXHOXVsL9ulYAJES0=", + "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", "requires": { "good-listener": "^1.2.2", "select": "^1.1.2", @@ -2904,7 +2904,7 @@ "cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha1-EgoAywU7+2OiIucJ+Wg+ouEdjOw=", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, "requires": { "inherits": "^2.0.1", @@ -2947,7 +2947,7 @@ "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha1-Q/bCEVG07yv1cYfbDXPeIp4+fsM=", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", "dev": true, "requires": { "@types/q": "^1.5.1", @@ -2985,7 +2985,7 @@ "color": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", - "integrity": "sha1-aBSOf4XUGtdknF+oyBBvCY0inhA=", + "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -3017,12 +3017,12 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "dev": true, "requires": { "color-name": "^1.0.0", @@ -3032,7 +3032,7 @@ "color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha1-k4NDeaHMmgxh+C9S8NBDIiUb1aI=", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, "colorette": { @@ -3050,13 +3050,13 @@ "colors": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha1-xQSRR51MG9rtLJztMs98fcI2D3g=", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha1-4BKJUNCCuGohaFgHlqCqXWxo2MU=", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", "dev": true, "requires": { "color": "3.0.x", @@ -3066,7 +3066,7 @@ "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha1-2SC0Mo1TSjrIKV1o971LpsQnvpo=", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", "dev": true, "requires": { "color-convert": "^1.9.1", @@ -3093,7 +3093,7 @@ "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } @@ -3113,7 +3113,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha1-FuQHD7qK4ptnnyIVhT7hgasuq8A=", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "component-inherit": { @@ -3131,7 +3131,7 @@ "concat-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ=", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -3164,7 +3164,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -3175,7 +3175,7 @@ "concat-with-sourcemaps": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", - "integrity": "sha1-1OqT8FriV5CVG5nns7CeOQikCC4=", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, "requires": { "source-map": "^0.6.1" @@ -3184,7 +3184,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -3203,7 +3203,7 @@ "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha1-XUk0iRDKpeB6AYALAw0MNfIEhPg=", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", @@ -3215,7 +3215,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -3239,7 +3239,7 @@ "consolidate": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz", - "integrity": "sha1-IasEMjXHGgfUXZqtmFk7DbpWurc=", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", "dev": true, "requires": { "bluebird": "^3.1.1" @@ -3258,7 +3258,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "convert-source-map": { @@ -3285,7 +3285,7 @@ "copy-props": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", - "integrity": "sha1-k7scrfr9MdpbuKnUtB9HHsOnLf4=", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", "dev": true, "requires": { "each-props": "^1.3.0", @@ -3344,7 +3344,7 @@ "cosmiconfig": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha1-BA9yaAnFked6F8CjYmykW08Wixo=", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "dev": true, "requires": { "import-fresh": "^2.0.0", @@ -3356,7 +3356,7 @@ "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { "nice-try": "^1.0.4", @@ -3369,7 +3369,7 @@ "css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha1-xkZ1XHOXHyu6amAeLPL9cbEpiSk=", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -3381,7 +3381,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -3395,7 +3395,7 @@ "css-declaration-sorter": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha1-wZiUD2OnbX42wecQGLABchBUyyI=", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", "dev": true, "requires": { "postcss": "^7.0.1", @@ -3417,7 +3417,7 @@ "css-select-base-adapter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha1-Oy/0lyzDYquIVhUHqVQIoUMhNdc=", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, "css-tree": { @@ -3451,7 +3451,7 @@ "cssnano": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha1-CsQfCxPRPUZUh+ERt3jULaYxuLI=", + "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -3463,7 +3463,7 @@ "cssnano-preset-default": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha1-UexmLM/KD4izltzZZ5zbkxvhf3Y=", + "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", "dev": true, "requires": { "css-declaration-sorter": "^4.0.1", @@ -3513,7 +3513,7 @@ "cssnano-util-raw-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha1-sm1f1fcqEd/np4RvtMZyYPlr8oI=", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -3522,7 +3522,7 @@ "cssnano-util-same-parent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha1-V0CC+yhZ0ttDOFWDXZqEVuoYu/M=", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", "dev": true }, "csso": { @@ -3594,7 +3594,7 @@ "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha1-hpgJU3LVjb7jRv/Qxwk/mfj561o=", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", "dev": true, "requires": { "es5-ext": "^0.10.50", @@ -3635,7 +3635,7 @@ "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { "ms": "^2.1.1" @@ -3644,7 +3644,7 @@ "debug-fabulous": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha1-r4oIYyRlIk70F0qfBjCMPCoevI4=", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", "dev": true, "requires": { "debug": "3.X", @@ -3849,7 +3849,7 @@ "default-compare": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha1-y2ETGESthNhHiPto/QFoHKd4Gi8=", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { "kind-of": "^5.0.2" @@ -3858,7 +3858,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -3872,7 +3872,7 @@ "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { "object-keys": "^1.0.12" @@ -3881,7 +3881,7 @@ "define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { "is-descriptor": "^1.0.2", @@ -3891,7 +3891,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3900,7 +3900,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -3909,7 +3909,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -3927,7 +3927,7 @@ "delegate": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", - "integrity": "sha1-tmtxwxWFIuirV0T3INjKDCr1kWY=" + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, "depd": { "version": "1.1.2", @@ -3956,7 +3956,7 @@ "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", - "integrity": "sha1-yrasM99wydmnJ0kK5DrJladpsio=", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", "dev": true, "requires": { "colorspace": "1.1.x", @@ -3967,7 +3967,7 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=" + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, "dir-glob": { "version": "3.0.1", @@ -3989,7 +3989,7 @@ "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { "esutils": "^2.0.2" @@ -4020,7 +4020,7 @@ "domelementtype": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha1-H4vf6R9aeAYydOgDtL3O326U+U0=", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", "dev": true } } @@ -4028,7 +4028,7 @@ "domelementtype": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "dev": true }, "domexception": { @@ -4051,7 +4051,7 @@ "domhandler": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", "dev": true, "requires": { "domelementtype": "1" @@ -4060,7 +4060,7 @@ "domutils": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "requires": { "dom-serializer": "0", @@ -4148,7 +4148,7 @@ "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha1-Kk31MX9sz9kfhtb9JdjYoQO4gwk=", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -4192,7 +4192,7 @@ "each-props": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha1-6kWkFNFt1c+kGbGoFyDVygaJIzM=", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { "is-plain-object": "^2.0.1", @@ -4250,7 +4250,7 @@ "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { "once": "^1.4.0" @@ -4362,7 +4362,7 @@ "entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha1-aNYITKsbB5dnVA2A5Wo5tCPkq/Q=", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", "dev": true }, "env-variable": { @@ -4374,7 +4374,7 @@ "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "optional": true, "requires": { @@ -4384,7 +4384,7 @@ "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha1-tKxAZIEH/c3PriQvQovqihTU8b8=", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { "is-arrayish": "^0.2.1" @@ -4444,7 +4444,7 @@ "es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha1-TrIVlMlyvEBVPSduUQU5FD21Pgo=", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", "dev": true }, "es6-symbol": { @@ -4460,7 +4460,7 @@ "es6-weak-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha1-ttofFswswNm+Q+a9v8Xn383zHVM=", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, "requires": { "d": "1", @@ -4509,7 +4509,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } @@ -4563,7 +4563,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "glob-parent": { @@ -4597,19 +4597,19 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -4630,7 +4630,7 @@ "eslint-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha1-dP7HxU0Hdrb2fgJRBAtYBlZOmB8=", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -4656,7 +4656,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { @@ -4698,13 +4698,13 @@ "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, "event-emitter": { @@ -4833,7 +4833,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -4877,7 +4877,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { "is-number": "^2.1.0", @@ -4955,7 +4955,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -4970,7 +4970,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -4981,7 +4981,7 @@ "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { "chardet": "^0.7.0", @@ -4992,7 +4992,7 @@ "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { "array-unique": "^0.3.2", @@ -5026,7 +5026,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5035,7 +5035,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -5044,7 +5044,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -5062,7 +5062,7 @@ "fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha1-28GRVPVYaQFQojlToK29A1vkX8c=", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { "ansi-gray": "^0.1.1", @@ -5201,7 +5201,7 @@ "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha1-yg9u+m3T1WEzP7FFFQZcL6/fQ5w=", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { "flat-cache": "^2.0.1" @@ -5264,7 +5264,7 @@ "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", @@ -5279,7 +5279,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -5339,7 +5339,7 @@ "fined": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha1-0AvszxqitHXRbUI7Aji3E6LEo3s=", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", "dev": true, "requires": { "expand-tilde": "^2.0.2", @@ -5393,13 +5393,13 @@ "flagged-respawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha1-595vEnnd2cqarIpZcdYYYGs6q0E=", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha1-XSltbwS9pEpGMKMBQTvbwuwIXsA=", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { "flatted": "^2.0.0", @@ -5415,13 +5415,13 @@ "flatted": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha1-aeV8qo8OrLwoHS4stFjUb9tEngg=", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha1-jdfYc6G6vCB9lOrQwuDkQnbr8ug=", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "requires": { "inherits": "^2.0.3", @@ -5508,7 +5508,7 @@ "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha1-3M5SwF9kTymManq5Nr1yTO/786Y=", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -5596,7 +5596,7 @@ "fs-readfile-promise": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-readfile-promise/-/fs-readfile-promise-3.0.1.tgz", - "integrity": "sha1-0NMHt/au38kgwx+m5XEu+qKXyVg=", + "integrity": "sha512-LsSxMeaJdYH27XrW7Dmq0Gx63mioULCRel63B5VeELYLavi1wF5s0XfsIdKDFdCL9hsfQ2qBvXJszQtQJ9h17A==", "dev": true, "requires": { "graceful-fs": "^4.1.11" @@ -6159,7 +6159,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, "functional-red-black-tree": { @@ -6171,7 +6171,7 @@ "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, "get-proxy": { @@ -6475,7 +6475,7 @@ "glob-watcher": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", - "integrity": "sha1-iKir8cTRMeuTkomUvEpZPC5d1iY=", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", "dev": true, "requires": { "anymatch": "^2.0.0", @@ -6501,7 +6501,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6525,7 +6525,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "globby": { @@ -6561,7 +6561,7 @@ "glogg": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha1-LX3XAr7aIus7/634gGltpthGMT8=", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", "dev": true, "requires": { "sparkles": "^1.0.0" @@ -6622,7 +6622,7 @@ "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha1-VDZRBw/Q9qsKBlDGo+b/WnywnKo=", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", "dev": true, "requires": { "glob-watcher": "^5.0.3", @@ -6655,7 +6655,7 @@ "gulp-babel": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-babel/-/gulp-babel-8.0.0.tgz", - "integrity": "sha1-4NqW9PLsSojdOjAw9HbjirISbYc=", + "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", "dev": true, "requires": { "plugin-error": "^1.0.1", @@ -6675,7 +6675,7 @@ "gulp-clean-css": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz", - "integrity": "sha1-kV7CWNxtPmpQBD9hAGbVwurE9U4=", + "integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==", "dev": true, "requires": { "clean-css": "4.2.1", @@ -6713,7 +6713,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -6795,7 +6795,7 @@ "gulp-eslint": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-6.0.0.tgz", - "integrity": "sha1-fUArtF+KZ2UrhoJ3ARgSBXNwqDI=", + "integrity": "sha512-dCVPSh1sA+UVhn7JSQt7KEb4An2sQNbOdB3PA8UCfxsoPlAKjJHxYHGXdXC7eb+V1FAnilSFFqslPrq037l1ig==", "dev": true, "requires": { "eslint": "^6.0.0", @@ -6870,7 +6870,7 @@ "gulp-less": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", - "integrity": "sha1-NIwzpd3nogfFdxsdgmHRrBAhzu0=", + "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", "dev": true, "requires": { "accord": "^0.29.0", @@ -6915,7 +6915,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -7047,7 +7047,7 @@ "gulp-notify": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/gulp-notify/-/gulp-notify-3.2.0.tgz", - "integrity": "sha1-KugiUAnfiB7vWb5d1aLxM3OHdk4=", + "integrity": "sha512-qEocs1UVoDKKUjfsxJNMNwkRla0PbsyJwsqNNXpzYWsLQ29LhxRMY3wnTGZcc4hMHtalnvah/Dwlwb4NijH/0A==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -7099,7 +7099,7 @@ "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", - "integrity": "sha1-+XYZXPPzR9DV9SSDVp/oAxzM6Ks=", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0", @@ -7109,7 +7109,7 @@ "lodash.templatesettings": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", - "integrity": "sha1-5IExDwSdPPbUfpEq0JMTsVTw+zM=", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", "dev": true, "requires": { "lodash._reinterpolate": "^3.0.0" @@ -7133,7 +7133,7 @@ "gulp-postcss": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", - "integrity": "sha1-jTdyzU0nvKVeyMtMjlduO95NxVA=", + "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", "dev": true, "requires": { "fancy-log": "^1.3.2", @@ -7146,7 +7146,7 @@ "gulp-rename": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", - "integrity": "sha1-3hxxjnxAla6GH3KW708ySGSCQL0=", + "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", "dev": true }, "gulp-sort": { @@ -7161,7 +7161,7 @@ "gulp-sourcemaps": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", - "integrity": "sha1-o/AC2HNG0sDzrsNq9+uHPyPeiuY=", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", "dev": true, "requires": { "@gulp-sourcemaps/identity-map": "1.X", @@ -7186,7 +7186,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -7225,7 +7225,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -7247,7 +7247,7 @@ "gulp-watch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", - "integrity": "sha1-g9N4dS9b+0baAj5zwX7R2nBmIV0=", + "integrity": "sha512-HnTSBdzAOFIT4wmXYPDUn783TaYAq9bpaN05vuZNP5eni3z3aRx0NAKbjhhMYtcq76x4R1wf4oORDGdlrEjuog==", "dev": true, "requires": { "ansi-colors": "1.1.0", @@ -7348,7 +7348,7 @@ "gulp-wrap": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/gulp-wrap/-/gulp-wrap-0.15.0.tgz", - "integrity": "sha1-6QFMm7hkOrMQ6TjURpuFaFUaVS8=", + "integrity": "sha512-f17zkGObA+hE/FThlg55gfA0nsXbdmHK1WqzjjB2Ytq1TuhLR7JiCBJ3K4AlMzCyoFaCjfowos+VkToUNE0WTQ==", "dev": true, "requires": { "consolidate": "^0.15.1", @@ -7392,7 +7392,7 @@ "through2": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", - "integrity": "sha1-OSducTwzAu3544jdnIEt07glvVo=", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "dev": true, "requires": { "readable-stream": "2 || 3" @@ -7457,7 +7457,7 @@ "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha1-HvievT5JllV2de7ZiTEQ3DUPoIA=", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -7466,7 +7466,7 @@ "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { "function-bind": "^1.1.1" @@ -7484,7 +7484,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", "dev": true, "requires": { "isarray": "2.0.1" @@ -7577,13 +7577,13 @@ "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha1-TAb8y0YC/iYCs8k9+C1+fb8aio4=", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", "dev": true }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "requires": { "parse-passwd": "^1.0.0" @@ -7610,7 +7610,7 @@ "html-comment-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha1-l9RoiutcgYhqNk+qDK0d2hTUM6c=", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "dev": true }, "html-encoding-sniffer": { @@ -7639,7 +7639,7 @@ "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, "isarray": { @@ -7684,7 +7684,7 @@ "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { "depd": "~1.1.2", @@ -7733,7 +7733,7 @@ "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -7749,7 +7749,7 @@ "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, "image-size": { @@ -7993,13 +7993,13 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, "ini": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, "inquirer": { @@ -8095,7 +8095,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -8122,13 +8122,13 @@ "is": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", - "integrity": "sha1-Yc/23TxBk9uUo9YlggcrROVkXXk=", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", "dev": true }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -8179,13 +8179,13 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true }, "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", "dev": true }, "is-color-stop": { @@ -8231,7 +8231,7 @@ "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", @@ -8242,7 +8242,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", "dev": true } } @@ -8387,7 +8387,7 @@ "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "^3.0.1" @@ -8436,7 +8436,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -8445,7 +8445,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, "is-retry-allowed": { @@ -8465,7 +8465,7 @@ "is-svg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha1-kyHb0pwhLlypnE+peUxxS8r6L3U=", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", "dev": true, "requires": { "html-comment-regex": "^1.1.0" @@ -8488,7 +8488,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -8509,7 +8509,7 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true }, "is-wsl": { @@ -8564,7 +8564,7 @@ "jasmine-core": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha1-Eywj5kWvlthci8oTyHWLGEKfweQ=", + "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", "dev": true }, "jquery": { @@ -8585,19 +8585,19 @@ "js-levenshtein": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha1-xs7ljrNVA3LfjeuF+tXOZs4B1Z0=", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", "dev": true }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha1-r/FRswv9+o5J4F2iLnQV6d+jeEc=", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -8731,7 +8731,7 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, "json-buffer": { @@ -8744,7 +8744,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha1-u4Z8+zRQ5pEHwTHRxRS6s9yLyqk=", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, "json-schema": { @@ -8755,7 +8755,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8771,7 +8771,7 @@ "json5": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha1-gbbLBOm6SW8ccAXQe0NoomOPkLY=", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -8837,7 +8837,7 @@ "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha1-xV7PAhheJGklk5kxDBc84xIzsUI=", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -8853,7 +8853,7 @@ "braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha1-NFThpGLujVmeI23zNs2epPiv4Qc=", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { "fill-range": "^7.0.1" @@ -8878,7 +8878,7 @@ "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha1-GRmmp8df44ssfHflGYU12prN2kA=", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -8903,7 +8903,7 @@ "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha1-6h9/O4DwZCNug0cPhsCcJU+0Wwk=", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { "binary-extensions": "^2.0.0" @@ -8921,7 +8921,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha1-dTU0W4lnNNX4DE0GxQlVUnoU8Ss=", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "mime": { @@ -8933,7 +8933,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, "readdirp": { @@ -8954,7 +8954,7 @@ "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha1-FkjESq58jZiKMmAY7XL1tN0DkuQ=", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { "is-number": "^7.0.0" @@ -8965,7 +8965,7 @@ "karma-jasmine": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz", - "integrity": "sha1-JuPjHy+vJy3YDrsOGJiRTMOhl2M=", + "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", "dev": true, "requires": { "jasmine-core": "^3.3" @@ -8980,7 +8980,7 @@ "karma-junit-reporter": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz", - "integrity": "sha1-007vfwsv0GTgiWlU6IUakM8UyPM=", + "integrity": "sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw==", "dev": true, "requires": { "path-is-absolute": "^1.0.0", @@ -9009,13 +9009,13 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", "dev": true }, "kuler": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", - "integrity": "sha1-73x4TzbJ+24W3TFQ0VJneysCKKY=", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", "dev": true, "requires": { "colornames": "^1.1.1" @@ -9104,7 +9104,7 @@ "less": { "version": "3.10.3", "resolved": "https://registry.npmjs.org/less/-/less-3.10.3.tgz", - "integrity": "sha1-QXoJddXu7MUs/0vPo8CdNXgeZ5I=", + "integrity": "sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow==", "dev": true, "requires": { "clone": "^2.1.2", @@ -9127,7 +9127,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } @@ -9317,7 +9317,7 @@ "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, "lodash.partialright": { @@ -9430,7 +9430,7 @@ "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha1-ce5R+nvkyuwaY4OffmgtgTLTDK8=", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -9470,7 +9470,7 @@ "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha1-i75Q6oW+1ZvJ4z3KuCNe6bz0Q80=", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { "pseudomap": "^1.0.2", @@ -9506,7 +9506,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9537,7 +9537,7 @@ "marked": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha1-tkIB8FHScbHtwQoE0a6bdLuOXA4=", + "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==", "dev": true }, "matchdep": { @@ -9569,13 +9569,13 @@ "math-random": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha1-XdaUPJOFSCZwFtTjTwV1gwgMUUw=", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", "dev": true }, "mdn-data": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha1-aZs8OKxvHXKAkaZGULZdOIUC/Vs=", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", "dev": true }, "media-typer": { @@ -9587,7 +9587,7 @@ "memoizee": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", - "integrity": "sha1-B6APIEaZ+alcLZ53IYJxx81hDVc=", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", "dev": true, "requires": { "d": "1", @@ -9631,7 +9631,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "merge2": { @@ -9643,7 +9643,7 @@ "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9664,7 +9664,7 @@ "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, "optional": true }, @@ -9697,7 +9697,7 @@ "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -9705,14 +9705,14 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, "minimize": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/minimize/-/minimize-2.2.0.tgz", - "integrity": "sha1-ixZ28wBR2FmNdDZGvRJpCwdNpMM=", + "integrity": "sha512-IxR2XMbw9pXCxApkdD9BTcH2U4XlXhbeySUrv71rmMS9XDA8BVXEsIuFu24LtwCfBgfbL7Fuh8/ZzkO5DaTLlQ==", "dev": true, "requires": { "argh": "^0.1.4", @@ -9727,7 +9727,7 @@ "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -9737,7 +9737,7 @@ "is-extendable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { "is-plain-object": "^2.0.4" @@ -9756,7 +9756,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true } @@ -9782,7 +9782,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "multipipe": { @@ -9797,7 +9797,7 @@ "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha1-rLAwDrTeI6fd7sAU4+lgRLNHIzE=", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", "dev": true }, "mute-stream": { @@ -9816,7 +9816,7 @@ "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { "arr-diff": "^4.0.0", @@ -9841,12 +9841,12 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "next-tick": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, @@ -9858,13 +9858,13 @@ "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "node-notifier": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", - "integrity": "sha1-y3La+UyTkECY4oucWQ/YZuRkvVA=", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", "dev": true, "requires": { "growly": "^1.3.0", @@ -9886,7 +9886,7 @@ "node.extend": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.2.tgz", - "integrity": "sha1-tEBFJUlKzJl0DzcDxJa31Rgsxsw=", + "integrity": "sha512-pDT4Dchl94/+kkgdwyS2PauDFjZG0Hk0IcHIB+LkW27HLDtdoeMxHTxZh39DYbPP8UflWXWj9JcdDozF+YDOpQ==", "dev": true, "requires": { "has": "^1.0.3", @@ -9896,7 +9896,7 @@ "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, "requires": { "hosted-git-info": "^2.1.4", @@ -9923,7 +9923,7 @@ "normalize-url": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", "dev": true }, "nouislider": { @@ -9934,7 +9934,7 @@ "now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", - "integrity": "sha1-jlechoV2SnzALLaAOA6U9DzLH3w=", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", "dev": true, "requires": { "once": "^1.3.2" @@ -13067,7 +13067,7 @@ "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { "boolbase": "~1.0.0" @@ -13094,7 +13094,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU=" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "3.0.0", @@ -13148,7 +13148,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, "object-visit": { @@ -13163,7 +13163,7 @@ "object.assign": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -13394,7 +13394,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -13474,7 +13474,7 @@ "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "requires": { "callsites": "^3.0.0" @@ -13483,7 +13483,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true } } @@ -13541,7 +13541,7 @@ "parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha1-4rXb7eAOf6m8NjYH9TMn6LBzGJs=", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true }, "parse-passwd": { @@ -13577,7 +13577,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, "pascalcase": { @@ -13616,7 +13616,7 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-root": { @@ -13673,7 +13673,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -13695,7 +13695,7 @@ "plugin-error": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha1-dwFr2JGdCsN3/c3QMiMolTyleBw=", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, "requires": { "ansi-colors": "^1.0.1", @@ -13733,13 +13733,13 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha1-B2Srxpxj1ayELdSGfo0CXogN+PM=", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -13762,7 +13762,7 @@ "postcss-colormin": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha1-rgYLzpPteUrHEmTwgTLVUJVr04E=", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13775,7 +13775,7 @@ "postcss-convert-values": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha1-yjgT7U2g+BL51DcDWE5Enr4Ymn8=", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13785,7 +13785,7 @@ "postcss-discard-comments": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha1-H7q9LCRr/2qq15l7KwkY9NevQDM=", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13794,7 +13794,7 @@ "postcss-discard-duplicates": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha1-P+EzzTyCKC5VD8myORdqkge3hOs=", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13803,7 +13803,7 @@ "postcss-discard-empty": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha1-yMlR6fc+2UKAGUWERKAq2Qu592U=", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13812,7 +13812,7 @@ "postcss-discard-overridden": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha1-ZSrvipZybwKfXj4AFG7npOdV/1c=", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13821,7 +13821,7 @@ "postcss-load-config": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", - "integrity": "sha1-yE1pK3u3tB3c7ZTuYuirMbQXsAM=", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", "dev": true, "requires": { "cosmiconfig": "^5.0.0", @@ -13831,7 +13831,7 @@ "postcss-merge-longhand": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha1-YvSaE+Sg7gTnuY9CuxYGLKJUniQ=", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", "dev": true, "requires": { "css-color-names": "0.0.4", @@ -13843,7 +13843,7 @@ "postcss-merge-rules": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha1-NivqT/Wh+Y5AdacTxsslrv75plA=", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -13870,7 +13870,7 @@ "postcss-minify-font-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha1-zUw0TM5HQ0P6xdgiBqssvLiv1aY=", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -13880,7 +13880,7 @@ "postcss-minify-gradients": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha1-k7KcL/UJnFNe7NpWxKpuZlpmNHE=", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13892,7 +13892,7 @@ "postcss-minify-params": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha1-a5zvAwwR41Jh+V9hjJADbWgNuHQ=", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13906,7 +13906,7 @@ "postcss-minify-selectors": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha1-4uXrQL/uUA0M2SQ1APX46kJi+9g=", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -13931,7 +13931,7 @@ "postcss-normalize-charset": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha1-izWt067oOhNrBHHg1ZvlilAoXdQ=", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "requires": { "postcss": "^7.0.0" @@ -13940,7 +13940,7 @@ "postcss-normalize-display-values": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha1-Db4EpM6QY9RmftK+R2u4MMglk1o=", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13951,7 +13951,7 @@ "postcss-normalize-positions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha1-BfdX+E8mBDc3g2ipH4ky1LECkX8=", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13963,7 +13963,7 @@ "postcss-normalize-repeat-style": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha1-xOu8KJ85kaAo1EdRy90RkYsXkQw=", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -13975,7 +13975,7 @@ "postcss-normalize-string": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha1-zUTECrB6DHo23F6Zqs4eyk7CaQw=", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", "dev": true, "requires": { "has": "^1.0.0", @@ -13986,7 +13986,7 @@ "postcss-normalize-timing-functions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha1-jgCcoqOUnNr4rSPmtquZy159KNk=", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -13997,7 +13997,7 @@ "postcss-normalize-unicode": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha1-hBvUj9zzAZrUuqdJOj02O1KuHPs=", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -14008,7 +14008,7 @@ "postcss-normalize-url": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha1-EOQ3+GvHx+WPe5ZS7YeNqqlfquE=", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "dev": true, "requires": { "is-absolute-url": "^2.0.0", @@ -14020,7 +14020,7 @@ "postcss-normalize-whitespace": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha1-vx1AcP5Pzqh9E0joJdjMDF+qfYI=", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", "dev": true, "requires": { "postcss": "^7.0.0", @@ -14030,7 +14030,7 @@ "postcss-ordered-values": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha1-DPdcgg7H1cTSgBiVWeC1ceusDu4=", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", "dev": true, "requires": { "cssnano-util-get-arguments": "^4.0.0", @@ -14041,7 +14041,7 @@ "postcss-reduce-initial": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha1-f9QuvqXpyBRgljniwuhK4nC6SN8=", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -14053,7 +14053,7 @@ "postcss-reduce-transforms": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha1-F++kBerMbge+NBSlyi0QdGgdTik=", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", "dev": true, "requires": { "cssnano-util-get-match": "^4.0.0", @@ -14076,7 +14076,7 @@ "postcss-svgo": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha1-F7mXvHEbMzurFDqu07jT1uPTglg=", + "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", "dev": true, "requires": { "is-svg": "^3.0.0", @@ -14088,7 +14088,7 @@ "postcss-unique-selectors": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha1-lEaRHzKJv9ZMbWgPBzwDsfnuS6w=", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "dev": true, "requires": { "alphanum-sort": "^1.0.0", @@ -14129,32 +14129,32 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8=", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", "dev": true }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "optional": true, "requires": { @@ -14189,7 +14189,7 @@ "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha1-Ejma3W5M91Jtlzy8i1zi4pCLOQk=", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -14199,7 +14199,7 @@ "pumpify": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha1-NlE74karJ1cLGjdKXOJ4v9dDcM4=", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { "duplexify": "^3.6.0", @@ -14210,7 +14210,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "q": { "version": "1.5.1", @@ -14221,13 +14221,13 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha1-yzroBuh0BERYTvFUzo7pjUA/PjY=" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "5.1.1", @@ -14258,7 +14258,7 @@ "randomatic": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha1-t3bvxZN1mE42xTey9RofCv8Noe0=", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", "dev": true, "requires": { "is-number": "^4.0.0", @@ -14269,7 +14269,7 @@ "is-number": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha1-ACbjf1RU1z41bf5lZGmYZ8an8P8=", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", "dev": true } } @@ -14277,13 +14277,13 @@ "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { "bytes": "3.1.0", @@ -14328,7 +14328,7 @@ "readdirp": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { "graceful-fs": "^4.1.11", @@ -14366,7 +14366,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -14397,7 +14397,7 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha1-SoVuxLVuQHfFV1icroXnpMiGmhE=", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "dev": true }, "regenerate-unicode-properties": { @@ -14421,7 +14421,7 @@ "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { "is-equal-shallow": "^0.1.3" @@ -14430,7 +14430,7 @@ "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { "extend-shallow": "^3.0.2", @@ -14440,7 +14440,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "regexpu-core": { @@ -14460,7 +14460,7 @@ "regjsgen": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha1-SPC/Gl6iBRlpKcDZeYtC0e2YRDw=", + "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", "dev": true }, "regjsparser": { @@ -14483,7 +14483,7 @@ "remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", - "integrity": "sha1-wr8eN3Ug0yT2I4kuM8EMrCwlK1M=", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "requires": { "is-buffer": "^1.1.5", @@ -14510,7 +14510,7 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -14683,7 +14683,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, "reusify": { @@ -14695,7 +14695,7 @@ "rfdc": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha1-unLME2egzNnPgahws7WL060H+MI=", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "dev": true }, "rgb-regex": { @@ -14722,7 +14722,7 @@ "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha1-stEE/g2Psnz54KHNqCYt04M8bKs=", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "requires": { "glob": "^7.1.3" @@ -14746,7 +14746,7 @@ "run-sequence": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/run-sequence/-/run-sequence-2.2.1.tgz", - "integrity": "sha1-HOZD2jb9jH6n4akynaM/wriJhJU=", + "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", "dev": true, "requires": { "chalk": "^1.1.3", @@ -14784,7 +14784,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -14806,7 +14806,7 @@ }, "kind-of": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", "dev": true }, @@ -14843,7 +14843,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -14857,12 +14857,12 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, "saxes": { @@ -14892,7 +14892,7 @@ "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "semver-greatest-satisfied-range": { @@ -14930,7 +14930,7 @@ "set-value": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha1-oY1AUw5vB95CKMfe/kInr4ytAFs=", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -14953,7 +14953,7 @@ "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, "shebang-command": { @@ -14974,7 +14974,7 @@ "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs=", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, "signal-exit": { @@ -14995,7 +14995,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "dev": true } } @@ -15009,7 +15009,7 @@ "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha1-ys12k0YaY3pXiNkqfdT7oGjoFjY=", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -15028,7 +15028,7 @@ "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { "base": "^0.11.1", @@ -15044,7 +15044,7 @@ "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -15079,7 +15079,7 @@ "snapdragon-node": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { "define-property": "^1.0.0", @@ -15099,7 +15099,7 @@ "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -15108,7 +15108,7 @@ "is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { "kind-of": "^6.0.0" @@ -15117,7 +15117,7 @@ "is-descriptor": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", @@ -15130,7 +15130,7 @@ "snapdragon-util": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { "kind-of": "^3.2.0" @@ -15335,13 +15335,13 @@ "sparkles": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha1-AI22XtzmxQ7sDF4ijhlFBh3QQ3w=", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", "dev": true }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha1-+4PlBERSaPFUsHTiGMh8ADzTHfQ=", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -15351,7 +15351,7 @@ "spdx-exceptions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha1-LqRQrudPKom/uUUZwH/Nb0EyKXc=", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", "dev": true }, "spdx-expression-parse": { @@ -15367,7 +15367,7 @@ "spdx-license-ids": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha1-NpS1gEVnpFjTyARYQqY1hjL2JlQ=", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "spectrum-colorpicker2": { @@ -15378,7 +15378,7 @@ "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { "extend-shallow": "^3.0.0" @@ -15411,7 +15411,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "optional": true, @@ -15435,7 +15435,7 @@ "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha1-+2YcC+8ps520B2nuOfpwCT1vaHc=", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -15451,7 +15451,7 @@ "stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha1-g26zyDgv4pNv6vVEYxAXzn1Ho88=", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, "stack-trace": { @@ -15496,7 +15496,7 @@ "stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", - "integrity": "sha1-rNrI2lnvK8HheiwMz2wyDRIOVV0=", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true }, "stream-shift": { @@ -15680,7 +15680,7 @@ "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha1-Zxj8r00eB9ihMYaQiB6NlnJqcdU=", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", "dev": true, "requires": { "browserslist": "^4.0.0", @@ -15704,7 +15704,7 @@ "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -15750,7 +15750,7 @@ "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha1-EpLRlQDOP4YFOwXw6Ofko7shB54=", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { "ajv": "^6.10.2", @@ -15762,7 +15762,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, "is-fullwidth-code-point": { @@ -15774,7 +15774,7 @@ "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -15785,7 +15785,7 @@ "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -15884,7 +15884,7 @@ "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha1-adycGxdEbueakr9biEu0uRJ1BvU=", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "dev": true }, "text-table": { @@ -15902,7 +15902,7 @@ "through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha1-AcHjnrMdB8t9A6lqcIIyYLIxMs0=", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { "readable-stream": "~2.3.6", @@ -15953,7 +15953,7 @@ "through2-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", - "integrity": "sha1-cA54bfI2fCyIzYqlvkz5weeDElQ=", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", "dev": true, "requires": { "through2": "~2.0.0", @@ -15976,7 +15976,7 @@ "timers-ext": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha1-b1ethXjgej+5+R2Th9ZWR1VeJcY=", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", "dev": true, "requires": { "es5-ext": "~0.10.46", @@ -15992,7 +15992,7 @@ "tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", - "integrity": "sha1-HRpW7fxRxD6GPLtTgqcjMONVVCM=" + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "tinymce": { "version": "4.9.11", @@ -16002,7 +16002,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -16060,7 +16060,7 @@ "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { "define-property": "^2.0.2", @@ -16091,7 +16091,7 @@ "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "dev": true }, "tough-cookie": { @@ -16165,7 +16165,7 @@ "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha1-hI3XaY2vo+VKbEeedZxLw/GIR6A=", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, "type-check": { @@ -16180,7 +16180,7 @@ "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", @@ -16287,7 +16287,7 @@ "undertaker": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", - "integrity": "sha1-cBZi/4zjWHFTJN/UkqTwNgVd/ks=", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", "dev": true, "requires": { "arr-flatten": "^1.0.1", @@ -16310,13 +16310,13 @@ "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha1-JhmADEyCWADv3YNDr33Zkzy+KBg=", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", "dev": true }, "unicode-match-property-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha1-jtKjJWmWG86SJ9Cc0/+7j+1fAgw=", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, "requires": { "unicode-canonical-property-names-ecmascript": "^1.0.4", @@ -16338,7 +16338,7 @@ "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha1-C2/nuDWuzaYcbqTU8CwUIh4QmEc=", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", @@ -16362,7 +16362,7 @@ "unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", - "integrity": "sha1-xl0RDppK35psWUiygFPZqNBMvqw=", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", "dev": true, "requires": { "json-stable-stringify-without-jsonify": "^1.0.1", @@ -16372,7 +16372,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha1-tkb2m+OULavOzJ1mOcgNwQXvqmY=", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, "unpipe": { @@ -16436,13 +16436,13 @@ "upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha1-j2bbzVWog6za5ECK+LA1pQRMGJQ=", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { "punycode": "^2.1.0" } @@ -16482,7 +16482,7 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, "useragent": { @@ -16504,7 +16504,7 @@ "util.promisify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "integrity": "sha1-RA9xZaRZyaFtwUXrjnLzVocJcDA=", "dev": true, "requires": { "define-properties": "^1.1.2", @@ -16540,7 +16540,7 @@ "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha1-/JH2uce6FchX9MssXe/uw51PQQo=", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -16619,7 +16619,7 @@ "vinyl-fs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", - "integrity": "sha1-yFhJQF9nQo/qu71cXb3WT0fTG8c=", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, "requires": { "fs-mkdirp-stream": "^1.0.0", @@ -16828,7 +16828,7 @@ "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -16859,7 +16859,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -16876,7 +16876,7 @@ "write": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha1-CADhRSO5I6OH5BUSPIZWFqrg9cM=", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -16902,7 +16902,7 @@ "xmlbuilder": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-12.0.0.tgz", - "integrity": "sha1-4u1nXgaDSgid37hNuW4sKwP3jBo=", + "integrity": "sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ==", "dev": true }, "xmlchars": { @@ -16920,7 +16920,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, "y18n": { From 45bbaafde943cb5d263d0debfa294c47c9d4f7f5 Mon Sep 17 00:00:00 2001 From: Zeegaan <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 17 Aug 2021 09:55:57 +0200 Subject: [PATCH 51/54] fixed todo in AspNetCore Backoffice --- .../Implementations/TestHelper.cs | 2 +- .../AspNetCore/AspNetCoreBackOfficeInfo.cs | 30 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 90c5e0eb02..46ebd6ab9c 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -136,7 +136,7 @@ namespace Umbraco.Cms.Tests.Integration.Implementations { var globalSettings = new GlobalSettings(); IOptionsMonitor mockedOptionsMonitorOfGlobalSettings = Mock.Of>(x => x.CurrentValue == globalSettings); - _backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings); + _backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings, null); } return _backOfficeInfo; diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs index caaac9dfeb..8f21594435 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs @@ -1,17 +1,39 @@ using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Routing; +using static Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.Common.AspNetCore { public class AspNetCoreBackOfficeInfo : IBackOfficeInfo { - public AspNetCoreBackOfficeInfo(IOptionsMonitor globalSettings) + private readonly IOptionsMonitor _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; + private string _getAbsoluteUrl; + public AspNetCoreBackOfficeInfo(IOptionsMonitor globalSettings, IHostingEnvironment hostingEnviroment) { - GetAbsoluteUrl = globalSettings.CurrentValue.UmbracoPath; + _globalSettings = globalSettings; + _hostingEnvironment = hostingEnviroment; + //GetAbsoluteUrl= WebPath.Combine(hostingEnviroment.ApplicationMainUrl.ToString(), globalSettings.CurrentValue.UmbracoPath.TrimStart(CharArrays.TildeForwardSlash)); + } - public string GetAbsoluteUrl { get; } // TODO make absolute - + public string GetAbsoluteUrl + { + get + { + if (_getAbsoluteUrl is null) + { + if(_hostingEnvironment.ApplicationMainUrl is null) + { + return ""; + } + _getAbsoluteUrl = WebPath.Combine(_hostingEnvironment.ApplicationMainUrl.ToString(), _globalSettings.CurrentValue.UmbracoPath.TrimStart(CharArrays.TildeForwardSlash)); + } + return _getAbsoluteUrl; + } + } } } From efac943e110d3dff2df266b33db1aed9674aa8f5 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:09:49 +0200 Subject: [PATCH 52/54] Update src/Umbraco.Tests.Integration/Implementations/TestHelper.cs Co-authored-by: Bjarke Berg --- src/Umbraco.Tests.Integration/Implementations/TestHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs index 46ebd6ab9c..832cf6257b 100644 --- a/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs +++ b/src/Umbraco.Tests.Integration/Implementations/TestHelper.cs @@ -136,7 +136,7 @@ namespace Umbraco.Cms.Tests.Integration.Implementations { var globalSettings = new GlobalSettings(); IOptionsMonitor mockedOptionsMonitorOfGlobalSettings = Mock.Of>(x => x.CurrentValue == globalSettings); - _backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings, null); + _backOfficeInfo = new AspNetCoreBackOfficeInfo(mockedOptionsMonitorOfGlobalSettings, GetHostingEnvironment()); } return _backOfficeInfo; From 9fe0a40b6300528b1606837998be070966791de1 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:09:53 +0200 Subject: [PATCH 53/54] Update src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs Co-authored-by: Bjarke Berg --- src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs index 8f21594435..f006fb03f4 100644 --- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs +++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreBackOfficeInfo.cs @@ -16,7 +16,6 @@ namespace Umbraco.Cms.Web.Common.AspNetCore { _globalSettings = globalSettings; _hostingEnvironment = hostingEnviroment; - //GetAbsoluteUrl= WebPath.Combine(hostingEnviroment.ApplicationMainUrl.ToString(), globalSettings.CurrentValue.UmbracoPath.TrimStart(CharArrays.TildeForwardSlash)); } From ea530e786effcefb7b55c254b324264de7b3fd48 Mon Sep 17 00:00:00 2001 From: Zeegaan <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 17 Aug 2021 12:14:46 +0200 Subject: [PATCH 54/54] Changed gitignore to ignore appsettings.json --- .gitignore | 2 + .../appsettings.Development.json | 59 -------------- src/Umbraco.Web.UI.NetCore/appsettings.json | 76 ------------------- 3 files changed, 2 insertions(+), 135 deletions(-) delete mode 100644 src/Umbraco.Web.UI.NetCore/appsettings.Development.json delete mode 100644 src/Umbraco.Web.UI.NetCore/appsettings.json diff --git a/.gitignore b/.gitignore index f5249bb70b..fb5e701d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -193,6 +193,8 @@ src/Umbraco.Web.UI/Umbraco/telemetrics-id.umb /src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Ll]ogs /src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Dd]ata/* /src/Umbraco.Web.UI.NetCore/[Uu]mbraco/[Mm]odels/* +/src/Umbraco.Web.UI.NetCore/appsettings.json +/src/Umbraco.Web.UI.NetCore/appsettings.Development.json src/Umbraco.Tests.Integration/umbraco/Data/ diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.Development.json b/src/Umbraco.Web.UI.NetCore/appsettings.Development.json deleted file mode 100644 index 9cac2c3dd9..0000000000 --- a/src/Umbraco.Web.UI.NetCore/appsettings.Development.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "$schema" : "./umbraco/config/appsettings-schema.json", - "Serilog": { - "MinimumLevel": { - "Default": "Information", - "Override": { - "Examine.Lucene.Providers.LuceneIndex": "Debug", - "Examine.BaseIndexProvider": "Debug", - "Examine.Lucene.LoggingReplicationClient": "Debug", - "Examine.Lucene.ExamineReplicator": "Debug" - } - }, - "WriteTo": [ - { - "Name": "Async", - "Args": { - "configure": [ - { - "Name": "Console" - } - ] - } - } - ] - }, - "Umbraco": { - "CMS": { - "Examine": { - "LuceneDirectoryFactory": "TempFileSystemDirectoryFactory" - }, - "Global": { - "Smtp": { - //"From": "your@email.here", - //"Host": "localhost", - // "Port": "25" - } - }, - "Hosting": { - "Debug": true - }, - "RuntimeMinification": { - "useInMemoryCache": true, - "cacheBuster": "Timestamp" - }, - "RichTextEditor": { - "Commands": [ - { - "Alias": "fullscreen", - "Name": "Full Screen", - "Mode": "All" - } - ], - "Plugins": [ - "fullscreen" - ] - } - } - } -} diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json deleted file mode 100644 index cef926fad2..0000000000 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "./umbraco/config/appsettings-schema.json", - "ConnectionStrings": { - "umbracoDbDSN": "" - }, - "Serilog": { - "MinimumLevel": { - "Default": "Information", - "Override": { - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information", - "System": "Warning" - } - } - }, - "Umbraco": { - "CMS": { - "Content": { - "Notifications": { - "Email": "your@email.here" - }, - "MacroErrors": "Throw" - }, - "Global": { - "DefaultUILanguage": "en-us", - "HideTopLevelNodeFromPath": true, - "UmbracoPath": "~/umbraco", - "TimeOutInMinutes": 20, - "UseHttps": false - }, - "Hosting": { - "Debug": false - }, - "KeepAlive": { - "DisableKeepAliveTask": false, - "KeepAlivePingUrl": "{umbracoApplicationUrl}/api/keepalive/ping" - }, - "RequestHandler": { - "ConvertUrlsToAscii": "try" - }, - "RuntimeMinification": { - "dataFolder": "umbraco/Data/TEMP/Smidge", - "version": "637642136775050602" - }, - "Security": { - "KeepUserLoggedIn": false, - "UsernameIsEmail": true, - "HideDisabledUsersInBackoffice": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\", - "UserPassword": { - "RequiredLength": 10, - "RequireNonLetterOrDigit": false, - "RequireDigit": false, - "RequireLowercase": false, - "RequireUppercase": false, - "MaxFailedAccessAttemptsBeforeLockout": 5 - }, - "MemberPassword": { - "RequiredLength": 10, - "RequireNonLetterOrDigit": false, - "RequireDigit": false, - "RequireLowercase": false, - "RequireUppercase": false, - "MaxFailedAccessAttemptsBeforeLockout": 5 - } - }, - "Tours": { - "EnableTours": true - }, - "ModelsBuilder": { - "ModelsMode": "InMemoryAuto", - "Enable": true - } - } - } -}