From 45bcfffe837f5a2bb759fb4f4e19e0e16ba22b64 Mon Sep 17 00:00:00 2001 From: Gornytskyi Maxim <50423072+MaxymGorn@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:58:27 +0300 Subject: [PATCH] Feature/update nuget to 1 6 0 (#212) ## Target Add a rounded option to the GetCroppedCanvas method #### Open Questions ## Checklist - [x] Documentation updated - [x] Tests cover new or modified code - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] New dependencies added - [ ] Includes breaking changes - [x] Version bumped ## Visuals --------- Co-authored-by: ColdForeign Co-authored-by: George Radchuk <38187349+ColdForeign@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug_report.md | 41 + .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/feature_request.md | 23 + .github/workflows/cd.yml | 2 +- README.md | 6 +- .../Components/AspectRatioSettings.razor | 38 +- .../Components/AspectRatioSettings.razor.cs | 26 +- .../CroppedDimensionsSettings.razor | 46 +- .../Components/CropperDataPreview.razor | 8 +- .../Client/Components/Docs/ApiLink.cs | 29 + .../Client/Components/Docs/ApiMethod.cs | 13 + .../Client/Components/Docs/ApiProperty.cs | 14 + .../Client/Components/Docs/DocsApi.razor | 442 ++++++++++ .../Client/Components/Docs/DocsPage.razor | 31 + .../Client/Components/Docs/DocsPage.razor.cs | 131 +++ .../Components/Docs/DocsPageContent.razor | 8 + .../Client/Components/Docs/DocsSectionLink.cs | 9 + .../Client/Components/Docs/DocsTypeInfo.razor | 35 + .../Client/Components/Docs/EnumSwitch.razor | 30 + .../Components/Docs/NavigationFooterLink.cs | 20 + .../Components/Docs/NavigationSection.cs | 12 + .../Components/Docs/QueuedContent.razor | 58 ++ .../Components/Docs/SectionContent.razor | 6 +- .../Components/Docs/SectionContent.razor.cs | 79 +- .../Components/Docs/SectionHeader.razor | 6 +- .../Components/Docs/SectionHeader.razor.cs | 34 + .../Client/Components/GetSetCropperData.razor | 361 +++++--- .../Components/GetSetCropperData.razor.cs | 5 + .../Client/Components/ZoomRatioSettings.razor | 47 +- .../Components/ZoomRatioSettings.razor.cs | 33 +- .../Client/Cropper.Blazor.Client.csproj | 22 +- .../Client/Extensions/DocsVeiewExtension.cs | 10 + .../Client/Models/DocStrings.cs | 36 + src/Cropper.Blazor/Client/Pages/About.razor | 9 + src/Cropper.Blazor/Client/Pages/Api.razor | 29 +- .../Client/Pages/CropperDemo.razor | 774 +++++++++--------- .../Client/Pages/CropperDemo.razor.cs | 48 +- src/Cropper.Blazor/Client/Pages/Index.razor | 26 +- .../Client/Services/RenderQueueService.cs | 100 +++ .../Client/Shared/SeoHeader.razor | 12 + .../Client/Shared/SeoHeader.razor.cs | 54 ++ .../Client/Styles/Cropper.Blazor.Client.scss | 15 + .../Client/Styles/components/docssection.scss | 33 +- .../Client/Styles/layout/_mainlayout.scss | 2 +- src/Cropper.Blazor/Client/Theme/Theme.cs | 1 - src/Cropper.Blazor/Client/wwwroot/index.html | 26 +- .../wwwroot/overrideCropperJsInteropModule.js | 8 +- .../wwwroot/resizeWindowEventListener.js | 19 + src/Cropper.Blazor/Client/wwwroot/sitemap.xml | 27 + .../CodeSnippetsCompiler.cs | 9 +- .../Cropper.Blazor.Client.Compiler.csproj | 7 +- .../DocStrings.cs | 139 ++++ .../Cropper.Blazor.Client.Compiler/Paths.cs | 9 + .../Cropper.Blazor.Client.Compiler/Program.cs | 4 +- .../Cropper.Blazor.Shared.csproj | 9 + .../Extensions/MethodInfoExtensions.cs | 185 +++++ .../Extensions/TypeNameHelper.cs | 216 +++++ .../Extensions/XmlDocumentationExtension.cs | 436 ++++++++++ .../Cropper.Blazor.UnitTests.csproj | 2 +- src/Cropper.Blazor/Cropper.Blazor.sln | 7 + .../Cropper.Blazor/Cropper.Blazor.csproj | 4 +- .../Models/GetCroppedCanvasOptions.cs | 5 + .../Cropper.Blazor/wwwroot/cropper.min.js | 6 +- .../Server/Cropper.Blazor.Server.csproj | 22 +- 64 files changed, 3242 insertions(+), 670 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 src/Cropper.Blazor/Client/Components/Docs/ApiLink.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/ApiMethod.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/ApiProperty.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsApi.razor create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsPageContent.razor create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsSectionLink.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/DocsTypeInfo.razor create mode 100644 src/Cropper.Blazor/Client/Components/Docs/EnumSwitch.razor create mode 100644 src/Cropper.Blazor/Client/Components/Docs/NavigationFooterLink.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/NavigationSection.cs create mode 100644 src/Cropper.Blazor/Client/Components/Docs/QueuedContent.razor create mode 100644 src/Cropper.Blazor/Client/Models/DocStrings.cs create mode 100644 src/Cropper.Blazor/Client/Services/RenderQueueService.cs create mode 100644 src/Cropper.Blazor/Client/Shared/SeoHeader.razor create mode 100644 src/Cropper.Blazor/Client/Shared/SeoHeader.razor.cs create mode 100644 src/Cropper.Blazor/Client/wwwroot/resizeWindowEventListener.js create mode 100644 src/Cropper.Blazor/Client/wwwroot/sitemap.xml create mode 100644 src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/DocStrings.cs create mode 100644 src/Cropper.Blazor/Cropper.Blazor.Shared/Cropper.Blazor.Shared.csproj create mode 100644 src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/MethodInfoExtensions.cs create mode 100644 src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/TypeNameHelper.cs create mode 100644 src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/XmlDocumentationExtension.cs diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..af120529 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. + +**Pull Request** +- [ ] I would like to do a Pull Request diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..d9eb5902 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Github Discussions + url: https://github.com/CropperBlazor/Cropper.Blazor/discussions + about: Please ask and answer questions here + - name: Telegram + url: https://t.me/+dLQD8Al6C9s0ZjMy + about: You can also ask and answer questions here \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..c59d488c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. + +**Pull Request** +- [ ] I would like to do a Pull Request diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f7ea99b3..8e7d442f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -148,4 +148,4 @@ jobs: - name: DotNet Build Blazor MAUI Demo Project for .net 6 run: dotnet build --no-restore - working-directory: examples\Cropper.Blazor.MAUI.Net6 \ No newline at end of file + working-directory: examples\Cropper.Blazor.MAUI.Net6 diff --git a/README.md b/README.md index bf6c64c4..6e51cc9e 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,13 @@ Cropper.Blazor

- Cropper.Blazor is a component element that wraps around Cropper.js + Cropper.Blazor is a component that wraps around Cropper.js version 1.6.0

[![Build and run test](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/ci.yml) [![Deploy to GitHub Pages](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/cd.yml/badge.svg?event=push)](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/cd.yml) +[![Deploy to NuGet](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/release.yml/badge.svg?event=push)](https://github.com/CropperBlazor/Cropper.Blazor/actions/workflows/release.yml) [![coverage](https://codecov.io/github/CropperBlazor/Cropper.Blazor/branch/dev/graph/badge.svg?token=39M66DO85T)](https://codecov.io/github/CropperBlazor/Cropper.Blazor) [![GitHub](https://img.shields.io/github/license/CropperBlazor/Cropper.Blazor?color=ff5c9b)](https://github.com/CropperBlazor/Cropper.Blazor/blob/dev/LICENSE) [![GitHub](https://img.shields.io/github/last-commit/CropperBlazor/Cropper.Blazor?color=009DEA)](https://github.com/CropperBlazor/Cropper.Blazor) @@ -20,6 +21,9 @@ ## Demo - [CropperBlazor.github.io/demo](https://CropperBlazor.github.io/demo) +## API +- [https://cropperblazor.github.io/api](https://cropperblazor.github.io/api) + ## Prerequisites - Supported .NET 7.0, .NET 6.0 versions for these web platforms: - Blazor WebAssembly diff --git a/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor b/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor index 6f039b66..a786b447 100644 --- a/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor +++ b/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor @@ -1,19 +1,29 @@ - - - Aspect Ratio Settings (only for FREE aspect ratio) - + + + +
+ Aspect Ratio Settings + + This option work's only for FREE aspect ratio mode! + +
+
+
+ + - - - Current aspect ratio: @AspectRatio - +
+ + Current aspect ratio: + + +
diff --git a/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor.cs b/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor.cs index 8d2535aa..39fc42a2 100644 --- a/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor.cs +++ b/src/Cropper.Blazor/Client/Components/AspectRatioSettings.razor.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations; -using Cropper.Blazor.Client.Pages; +using Cropper.Blazor.Components; using Cropper.Blazor.Models; using Microsoft.AspNetCore.Components; @@ -17,7 +17,7 @@ public decimal? MaxAspectRatio set { maxAspectRatio = value; - ApplyAspectRatioRulesForCropperAsync(); + InvokeAsync(ApplyAspectRatioRulesForCropperAsync); } } @@ -27,18 +27,18 @@ public decimal? MinAspectRatio set { minAspectRatio = value; - ApplyAspectRatioRulesForCropperAsync(); + InvokeAsync(ApplyAspectRatioRulesForCropperAsync); } } [CascadingParameter(Name = "AspectRatio"), Required] private decimal? AspectRatio { get; set; } - [CascadingParameter(Name = "CropperDemo"), Required] - private CropperDemo CropperDemo { get; set; } = null!; + [CascadingParameter(Name = "CropperComponent"), Required] + private CropperComponent CropperComponent { get; set; } = null!; - [CascadingParameter(Name = "IsEnableAspectRatioSettings"), Required] - private bool IsEnableAspectRatioSettings + [CascadingParameter(Name = "IsFreeAspectRatioEnabled"), Required] + private bool IsFreeAspectRatioEnabled { get => isEnableAspectRatioSettings; set @@ -57,8 +57,8 @@ public async Task ApplyAspectRatioRulesForCropperAsync() { if (minAspectRatio is not null || maxAspectRatio is not null) { - ContainerData containerData = await CropperDemo.CropperComponent!.GetContainerDataAsync(); - CropBoxData cropBoxData = await CropperDemo.CropperComponent!.GetCropBoxDataAsync(); + ContainerData containerData = await CropperComponent!.GetContainerDataAsync(); + CropBoxData cropBoxData = await CropperComponent!.GetCropBoxDataAsync(); if (cropBoxData.Height != 0) { @@ -67,12 +67,16 @@ public async Task ApplyAspectRatioRulesForCropperAsync() if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio) { decimal? newCropBoxWidth = cropBoxData.Height * ((minAspectRatio + maxAspectRatio) / 2); + decimal? left = (containerData.Width - newCropBoxWidth) / 2; - CropperDemo.CropperComponent!.SetCropBoxData(new SetCropBoxDataOptions + CropperComponent!.SetCropBoxData(new SetCropBoxDataOptions { - Left = (containerData.Width - newCropBoxWidth) / 2, + Left = left, Width = newCropBoxWidth, }); + + cropBoxData = await CropperComponent!.GetCropBoxDataAsync(); + aspectRatio = cropBoxData.Width / cropBoxData.Height; } SetUpAspectRatio(aspectRatio); diff --git a/src/Cropper.Blazor/Client/Components/CroppedDimensionsSettings.razor b/src/Cropper.Blazor/Client/Components/CroppedDimensionsSettings.razor index cebea3b0..bb16afa5 100644 --- a/src/Cropper.Blazor/Client/Components/CroppedDimensionsSettings.razor +++ b/src/Cropper.Blazor/Client/Components/CroppedDimensionsSettings.razor @@ -1,26 +1,32 @@ - - - - Dimensions Settings -
This option may not work with arbitrary 'Aspect Ratios' images, it is recommended to use a free Aspect Ratio for this option or calculate the allowed values yourself
-
- + + + +
+ Dimensions Settings + + This setting may not work with a specific aspect ratio. + It is recommended to use a FREE aspect ratio for this + option or calculate the valid values yourself. + +
+
+
+ +
+ - + - +
+
+ - + +
diff --git a/src/Cropper.Blazor/Client/Components/CropperDataPreview.razor b/src/Cropper.Blazor/Client/Components/CropperDataPreview.razor index 2ab25db6..ff8a490d 100644 --- a/src/Cropper.Blazor/Client/Components/CropperDataPreview.razor +++ b/src/Cropper.Blazor/Client/Components/CropperDataPreview.razor @@ -1,11 +1,11 @@  + Adornment="Adornment.End" AdornmentText="px" AdornmentColor="Color.Primary" Margin="Margin.Dense" Format="N4" /> + Adornment="Adornment.End" AdornmentText="px" AdornmentColor="Color.Primary" Margin="Margin.Dense" Format="N4" /> + Adornment="Adornment.End" AdornmentText="px" AdornmentColor="Color.Primary" Margin="Margin.Dense" Format="N4" /> + Adornment="Adornment.End" AdornmentText="px" AdornmentColor="Color.Primary" Margin="Margin.Dense" Format="N4" /> diff --git a/src/Cropper.Blazor/Client/Components/Docs/ApiLink.cs b/src/Cropper.Blazor/Client/Components/Docs/ApiLink.cs new file mode 100644 index 00000000..941f032a --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/ApiLink.cs @@ -0,0 +1,29 @@ +using Cropper.Blazor.Components; + +namespace Cropper.Blazor.Client.Components.Docs +{ + public static class ApiLink + { + private static Dictionary SpecialCaseComponents = + new() + { + [typeof(CropperComponent)] = "cropper-component" + }; + + public static string GetComponentLinkFor(Type type) + { + return $"components/{GetComponentName(type)}"; + } + + private static string GetComponentName(Type type) + { + if (!SpecialCaseComponents.TryGetValue(type, out var component)) + { + component = new string(type.ToString().Replace("Cropper.Blazor", "").TakeWhile(c => c != '`').ToArray()) + .ToLowerInvariant(); + } + + return component; + } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/ApiMethod.cs b/src/Cropper.Blazor/Client/Components/Docs/ApiMethod.cs new file mode 100644 index 00000000..627d5a6f --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/ApiMethod.cs @@ -0,0 +1,13 @@ +using System.Reflection; + +namespace Cropper.Blazor.Client.Components.Docs +{ + public class ApiMethod + { + public string Signature { get; set; } + public ParameterInfo Return { get; set; } + public string Documentation { get; set; } + public MethodInfo MethodInfo { get; set; } + public ParameterInfo[] Parameters { get; set; } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/ApiProperty.cs b/src/Cropper.Blazor/Client/Components/Docs/ApiProperty.cs new file mode 100644 index 00000000..0cc61b89 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/ApiProperty.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +namespace Cropper.Blazor.Client.Components.Docs +{ + public class ApiProperty + { + public string Name { get; set; } + public Type Type { get; set; } + public PropertyInfo PropertyInfo { get; set; } + public string Description { get; set; } + public object Default { get; set; } + public bool IsTwoWay { get; set; } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsApi.razor b/src/Cropper.Blazor/Client/Components/Docs/DocsApi.razor new file mode 100644 index 00000000..c07effdd --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsApi.razor @@ -0,0 +1,442 @@ +@using System.Reflection; +@using Cropper.Blazor.Client.Components.Docs +@using System.Text.RegularExpressions +@using System.Web +@using Cropper.Blazor.Client.Extensions; +@using Cropper.Blazor.Client.Models; +@using Microsoft.Extensions.DependencyInjection +@using System.Globalization +@using MudBlazor; +@using Cropper.Blazor.Shared.Extensions; + + + + + + + + + + @{ + // save as lists to speed up displaying the page + var properties = getProperties().ToList(); + var methods = getMethods().ToList(); + var eventCallbacks = getEventCallbacks().ToList(); + } + + @if (properties.Count() > 0) + { + + + + @* *@ + + + Name + Type + Default + Description + + + @if (_propertiesGrouping == Grouping.Inheritance && (Type)context.Key != Type) + { + + @($"Inherited from {((Type)context.Key).GetTypeDisplayName()}") + + } + else if (_propertiesGrouping == Grouping.Categories) + { + + @context.Key + + } + + + + @context.Name + @if (_propertiesGrouping == Grouping.Inheritance && IsOverridden(context.PropertyInfo)) + { + overridden + } + + +
+ + @if (context.IsTwoWay) + { + + + + } +
+
+ + @{ + var def = PresentDefaultValue(context.Default); + } + @if (def.Contains(" + } + else + { + @def + } + + + @(HttpUtility.HtmlDecode(context.Description)) + + + @(HttpUtility.HtmlDecode(context.Description)) + +
+ +
+
+
+ } + + @if (eventCallbacks.Count() > 0) + { + + + + + + Name + Type + Description + + + @context.Name + @context.Type.GetTypeDisplayName() + @(HttpUtility.HtmlDecode(context.Description)) + + + + + } + + @if (methods.Count() > 0) + { + + + + + + Name + Parameters + Return + Description + + + + @context.Signature + + @if (context.Parameters != null) + { + foreach (var parameterInfo in context.Parameters) + { +
+
@(new MarkupString($"
{parameterInfo.ParameterType.GetTypeDisplayName()} {parameterInfo.Name}
{AnalyseMethodDocumentation(context.Documentation, "param", parameterInfo.Name)}"))
+
+ } + } +
+ + @{ + string methodReturn = AnalyseMethodDocumentation(context.Documentation, "returns"); + if (!string.IsNullOrEmpty(methodReturn)) + { + methodReturn = " — " + methodReturn; + } + } + @if (@context.Return != null) + { +
@(new MarkupString($"{context.Return.ParameterType.GetTypeDisplayName()}{methodReturn}"))
+ } +
+ +
@(new MarkupString(HttpUtility.HtmlDecode(AnalyseMethodDocumentation(context.Documentation, "summary"))))
+
+
+ + @context.Signature + + @if (context.Parameters != null) + { + foreach (var parameterInfo in context.Parameters) + { +
+
@(new MarkupString($"
{parameterInfo.ParameterType.GetTypeDisplayName()} {parameterInfo.Name}
{AnalyseMethodDocumentation(context.Documentation, "param", parameterInfo.Name)}"))
+
+
+ } + } +
+ + @{ + string methodReturn = AnalyseMethodDocumentation(context.Documentation, "returns"); + if (!string.IsNullOrEmpty(methodReturn)) + { + methodReturn = " — " + methodReturn; + } + } + @if (@context.Return != null) + { +
@(new MarkupString($"{context.Return.ParameterType.GetTypeDisplayName()}{methodReturn}"))
+ } +
+ @(new MarkupString(HttpUtility.HtmlDecode(AnalyseMethodDocumentation(context.Documentation, "summary")))) +
+
+ + + +
+
+
+ } +
+
+ +
+ @RenderTheType() +
+ +@code { + private List hiddenMethods = new List() + { + "ToString", + "GetType", + "GetHashCode", + "Equals", + "SetParametersAsync", + "ReferenceEquals" + }; + [Parameter] public Type Type { get; set; } + + private IEnumerable getEventCallbacks() + { + string saveTypename = DocStrings.GetSaveTypename(Type); + + foreach (var info in Type.GetPropertyInfosWithAttribute().OrderBy(x => x.Name)) + { + if (info.GetCustomAttributes(typeof(System.ObsoleteAttribute), true).Length == 0 + && (info.PropertyType.Name.Contains("EventCallback") || info.PropertyType.Name.Contains("Action") || info.PropertyType.Name.Contains("Func"))) + { + yield return new ApiProperty() + { + Name = info.Name, + PropertyInfo = info, + Default = string.Empty, + Description = DocStrings.GetMemberDescription(saveTypename, info), + IsTwoWay = CheckIsTwoWayEventCallback(info), + Type = info.PropertyType, + }; + } + } + } + + private IEnumerable getMethods() + { + string saveTypename = DocStrings.GetSaveTypename(Type); + + foreach (var info in Type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Static).OrderBy(x => x.Name)) + { + if (!hiddenMethods.Any(x => x.Contains(info.Name)) && !info.Name.StartsWith("get_") && !info.Name.StartsWith("set_")) + { + if (info.GetCustomAttributes(typeof(System.ObsoleteAttribute), true).Length == 0) + { + yield return new ApiMethod() + { + MethodInfo = info, + Return = info.ReturnParameter, + Signature = info.GetSignature(), + Parameters = info.GetParameters(), + Documentation = DocStrings.GetMemberDescription(saveTypename, info) + }; + } + } + } + } + + private IEnumerable getProperties() + { + string saveTypename = DocStrings.GetSaveTypename(Type); + + foreach (var info in Type.GetPropertyInfosWithAttribute() + .OrderBy(x => _propertiesGrouping switch + { + Grouping.Categories => x.GetCustomAttribute()?.Order ?? int.MaxValue - 1, + Grouping.Inheritance => -NumberOfAncestorClasses(BaseDefinitionClass(x)), + _ => 0 + }) + .ThenBy(x => x.Name)) + { + if (info.GetCustomAttributes(typeof(System.ObsoleteAttribute), true).Length == 0 && !(info.PropertyType.Name.Contains("EventCallback") || info.PropertyType.Name.Contains("Action") || info.PropertyType.Name.Contains("Func"))) + { + yield return new ApiProperty + { + Name = info.Name, + PropertyInfo = info, + Default = GetDefaultValue(info), + IsTwoWay = CheckIsTwoWayProperty(info), + Description = DocStrings.GetMemberDescription(saveTypename, info), + Type = info.PropertyType + }; + } + } + } + + private string AnalyseMethodDocumentation(string documentation, string occurrence, string parameter = "") + { + try + { + // Define local variable + string doublequotes = @""""; + + // Define the start tag and the end tag + string endTag = $""; + string startTag = $"<{occurrence}{(parameter == string.Empty ? "" : " name=" + doublequotes + parameter + doublequotes)}>"; + + // Check if the documentation is valid and contains the start tag + if (documentation != null && documentation.Contains(startTag)) + { + // Remove the beginning of the documentation until the start tag + documentation = documentation.Substring(documentation.IndexOf(startTag), documentation.Length - documentation.IndexOf(startTag)); + + // Check if the documentation contains the end tag + if (documentation.Contains(endTag)) + { + // Return the extracted information + // If the information is not for summary, ' : ' is only added if there is a non-empty information to be returned + return ((occurrence != "summary" && documentation.Substring(startTag.Length, documentation.IndexOf(endTag) - startTag.Length).Trim() != "" ? "" : "") + + documentation.Substring(startTag.Length, documentation.IndexOf(endTag) - startTag.Length).Trim()) + .Replace(">", ">") + .Replace("<", "<"); + } + } + } + catch + { + // ignored + } + + return string.Empty; + } + + private bool CheckIsTwoWayEventCallback(PropertyInfo propertyInfo) => propertyInfo.Name.EndsWith("Changed") ? true : false; + + private bool CheckIsTwoWayProperty(PropertyInfo propertyInfo) + { + PropertyInfo eventCallbackInfo = Type.GetProperty(propertyInfo.Name + "Changed"); + + return eventCallbackInfo != null && + eventCallbackInfo.PropertyType.Name.Contains("EventCallback") && + eventCallbackInfo.GetCustomAttribute() != null && + eventCallbackInfo.GetCustomAttribute() == null; + } + + // used for default value getting + object _comp_instance; + + RenderFragment RenderTheType() + { + if (!Type.IsAssignableTo(typeof(IComponent))) + return null; + return new RenderFragment(builder => + { + builder.OpenComponent(0, Type); + builder.AddComponentReferenceCapture(1, inst => { _comp_instance = inst; }); + builder.CloseComponent(); + }); + } + + private object GetDefaultValue(PropertyInfo info) + { + try + { + if (_comp_instance == null) + { + return null; + //RenderTheType(); + } + return info.GetValue(_comp_instance); + } + catch (Exception) { } + return null; + } + + DefaultConverter _converter = new DefaultConverter() { Culture= CultureInfo.InvariantCulture }; + + private string PresentDefaultValue(object @default) + { + if (@default == null) + return "null"; + if (@default.GetType() == typeof(string)) + { + if (@default.ToString() == string.Empty) + { + return ""; + } + else + { + return $"\"{@default}\""; + } + } + if (@default.GetType().IsEnum) + return $"{@default.GetType().Name}.{@default}"; + if (Nullable.GetUnderlyingType(@default.GetType())!=null) + return _converter.Set(@default); + if (@default.GetType().IsGenericType) // for instance event callbacks + return ""; + if (@default.GetType().IsValueType) + return _converter.Set(@default); + return ""; + } + + + #region Grouping properties + + private enum Grouping { Categories, Inheritance, None } + + private Grouping _propertiesGrouping = Grouping.None; + + private TableGroupDefinition PropertiesGroupDefinition => _propertiesGrouping switch + { + Grouping.Categories => new() { Selector = (p) => p.PropertyInfo.GetCustomAttribute()?.Name ?? "Misc" }, + Grouping.Inheritance => new() { Selector = (p) => BaseDefinitionClass(p.PropertyInfo) }, + _ => null + }; + + // -- Grouping properties by inheritance ------------------------------------------------------------------------------------------ + + private static Type BaseDefinitionClass(MethodInfo m) => m.GetBaseDefinition().DeclaringType; + + private static Type BaseDefinitionClass(PropertyInfo p) => BaseDefinitionClass(p.GetMethod ?? p.SetMethod); // used for grouping properties + + private static bool IsOverridden(MethodInfo m) => m.GetBaseDefinition().DeclaringType != m.DeclaringType; + + private static bool IsOverridden(PropertyInfo p) => IsOverridden(p.GetMethod ?? p.SetMethod); // used for the "overridden" chip + + // used for ordering groups of properties + private static int NumberOfAncestorClasses(Type type) + { + int n = 0; + while ((type = type.BaseType) != null) + n++; + return n; + } + + #endregion +} \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor b/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor new file mode 100644 index 00000000..4efc5dd1 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor @@ -0,0 +1,31 @@ +@using MudBlazor; + +
+ +
+ + @ChildContent + +
+ +
+ @if (DisplayFooter) + { + + + } + + @if (_displayView) + { + + Mode + +
+ Overview + API +
+ } + +
+
\ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor.cs b/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor.cs new file mode 100644 index 00000000..a5a49ad1 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsPage.razor.cs @@ -0,0 +1,131 @@ +using System.Diagnostics; +using Cropper.Blazor.Client.Services; +using Microsoft.AspNetCore.Components; +using MudBlazor; +using MudBlazor.Interfaces; + +namespace Cropper.Blazor.Client.Components.Docs +{ + public partial class DocsPage : ComponentBase + { + [Parameter] public bool DisplayFooter { get; set; } + + private Queue _bufferedSections = new(); + private MudPageContentNavigation _contentNavigation; + private Stopwatch _stopwatch = Stopwatch.StartNew(); + private string _anchor = null; + private bool _displayView; + private string _componentName; + private bool _renderAds; + [Inject] NavigationManager NavigationManager { get; set; } + [Inject] private IRenderQueueService RenderQueue { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } + + private bool _contentDrawerOpen = true; + public event Action Rendered; + private Dictionary _sectionMapper = new(); + + int _sectionCount; + + public int SectionCount + { + get + { + lock (this) + return _sectionCount; + } + } + + public int IncrementSectionCount() + { + lock (this) + return _sectionCount++; + } + + protected override void OnInitialized() + { + base.OnInitialized(); + RenderQueue.Clear(); + var relativePath = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + if (relativePath.Contains("#") == true) + { + _anchor = relativePath.Split(new[] { "#" }, StringSplitOptions.RemoveEmptyEntries)[1]; + } + } + + protected override void OnParametersSet() + { + _stopwatch = Stopwatch.StartNew(); + _sectionCount = 0; + + /*for after this release is done*/ + _displayView = false; + _componentName = "temp"; + } + + protected override void OnAfterRender(bool firstRender) + { + if (_stopwatch.IsRunning) + { + _stopwatch.Stop(); + Rendered?.Invoke(_stopwatch); + } + if (firstRender) + { + _renderAds = true; + StateHasChanged(); + } + } + + public string GetParentTitle(DocsPageSection section) + { + if (section == null) { return string.Empty; } + + if (section == null || section.ParentSection == null || + _sectionMapper.ContainsKey(section.ParentSection) == false) { return string.Empty; } + + var item = _sectionMapper[section.ParentSection]; + + return item.Title; + } + + internal async void AddSection(DocsSectionLink sectionLinkInfo, DocsPageSection section) + { + _bufferedSections.Enqueue(sectionLinkInfo); + + if (_contentNavigation != null) + { + while (_bufferedSections.Count > 0) + { + var item = _bufferedSections.Dequeue(); + + if (_contentNavigation.Sections.FirstOrDefault(x => x.Id == sectionLinkInfo.Id) == default) + { + MudPageContentSection parentInfo = null; + if (section.ParentSection != null && _sectionMapper.ContainsKey(section.ParentSection) == true) + { + parentInfo = _sectionMapper[section.ParentSection]; + } + + var info = + new MudPageContentSection(sectionLinkInfo.Title, sectionLinkInfo.Id, section.Level, + parentInfo); + _sectionMapper.Add(section, info); + _contentNavigation.AddSection(info, false); + } + } + + ((IMudStateHasChanged)_contentNavigation).StateHasChanged(); + + if (_anchor != null) + { + if (sectionLinkInfo.Id == _anchor) + { + await _contentNavigation.ScrollToSection(new Uri(NavigationManager.Uri)); + _anchor = null; + } + } + } + } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsPageContent.razor b/src/Cropper.Blazor/Client/Components/Docs/DocsPageContent.razor new file mode 100644 index 00000000..35783dc3 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsPageContent.razor @@ -0,0 +1,8 @@ + +
+ @ChildContent +
+ +@code { + [Parameter] public RenderFragment ChildContent { get; set; } +} \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsSectionLink.cs b/src/Cropper.Blazor/Client/Components/Docs/DocsSectionLink.cs new file mode 100644 index 00000000..61333f3b --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsSectionLink.cs @@ -0,0 +1,9 @@ +namespace Cropper.Blazor.Client.Components.Docs +{ + public class DocsSectionLink + { + public string Id { get; set; } + public string Title { get; set; } + public bool Active { get; set; } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/DocsTypeInfo.razor b/src/Cropper.Blazor/Client/Components/Docs/DocsTypeInfo.razor new file mode 100644 index 00000000..dfa47c88 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/DocsTypeInfo.razor @@ -0,0 +1,35 @@ +@using Cropper.Blazor.Client.Extensions; +@using Cropper.Blazor.Shared.Extensions; + +@if (_nonnullableType.IsEnum) +{ + + @Type.GetTypeDisplayName() + + enumeration type + + @foreach (var name in Enum.GetNames(_nonnullableType)) + { + @(_nonnullableType.Name + "." + name) + +
+ } +
+
+
+} +else +{ + @Type.GetTypeDisplayName() +} + +@code { + [Parameter] public Type Type { get; set; } + + private Type _nonnullableType; + + protected override void OnInitialized() + { + _nonnullableType = Nullable.GetUnderlyingType(Type) ?? Type; + } +} \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/Docs/EnumSwitch.razor b/src/Cropper.Blazor/Client/Components/Docs/EnumSwitch.razor new file mode 100644 index 00000000..ea44aa2b --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/EnumSwitch.razor @@ -0,0 +1,30 @@ +@typeparam T where T: Enum + +
+ + @foreach (int value in Enum.GetValues(Type)) + { + @Enum.GetName(Type, value) + } + +
+ +@code { + private T _value; + + [Parameter] + public T Value + { + get => _value; + set + { + if (_value.Equals(value)) return; + _value = value; + ValueChanged.InvokeAsync(value); + } + } + + [Parameter] public EventCallback ValueChanged { get; set; } + + private Type Type => typeof(T); +} \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/Docs/NavigationFooterLink.cs b/src/Cropper.Blazor/Client/Components/Docs/NavigationFooterLink.cs new file mode 100644 index 00000000..a8184d5f --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/NavigationFooterLink.cs @@ -0,0 +1,20 @@ +namespace Cropper.Blazor.Client.Components.Docs +{ + public class NavigationFooterLink + { + public string Name { get; set; } + + public string Link { get; set; } + + public NavigationFooterLink() + { + + } + + public NavigationFooterLink(string name, string link) + { + Name = name; + Link = link; + } + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/NavigationSection.cs b/src/Cropper.Blazor/Client/Components/Docs/NavigationSection.cs new file mode 100644 index 00000000..01395f38 --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/NavigationSection.cs @@ -0,0 +1,12 @@ +namespace Cropper.Blazor.Client.Components.Docs +{ + public enum NavigationSection + { + Unspecified, + Api, + Components, + Features, + Customization, + Utilities + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/QueuedContent.razor b/src/Cropper.Blazor/Client/Components/Docs/QueuedContent.razor new file mode 100644 index 00000000..5fad369b --- /dev/null +++ b/src/Cropper.Blazor/Client/Components/Docs/QueuedContent.razor @@ -0,0 +1,58 @@ +@using Cropper.Blazor.Client.Services; +@implements IDisposable +@inject IRenderQueueService RenderQueue + +@if (_showContent || RenderImmediately) +{ + @ChildContent +} + +@code { + + [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter] public bool RenderImmediately { get; set; } + + private bool _showContent = false; + private bool _disposed = false; + public event Action Rendered; + public event Action Disposed; + + protected override void OnInitialized() + { + base.OnInitialized(); + if (!RenderImmediately) + RenderQueue.Enqueue(this); + } + + protected override void OnAfterRender(bool firstRender) + { + base.OnAfterRender(firstRender); + if (_disposed) + return; + if (_showContent && Rendered != null) + { + Rendered?.Invoke(this); + Rendered = null; + } + } + + public void Render() + { + if (_disposed) + return; + _showContent = true; + InvokeAsync(StateHasChanged); + } + + public bool IsDisposed => _disposed; + public bool IsRendered => _showContent; + + public void Dispose() + { + _disposed = true; + Rendered = null; + if (!_showContent) + Disposed?.Invoke(this); + Disposed = null; + } +} diff --git a/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor b/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor index 31be8b04..7255781d 100644 --- a/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor +++ b/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor @@ -1,5 +1,4 @@ -@using MudBlazor.Utilities -@using System; +@using System; @using System.IO; @using System.Linq; @using System.Text.RegularExpressions; @@ -7,6 +6,7 @@ @using Microsoft.AspNetCore.Components; @using Cropper.Blazor.Client.Extensions; @using Cropper.Blazor.Client.Models; +@using MudBlazor; @if (Codes != null || ChildContent != null) { @@ -58,7 +58,7 @@ diff --git a/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor.cs b/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor.cs index 6d927df3..51cf10ec 100644 --- a/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor.cs +++ b/src/Cropper.Blazor/Client/Components/Docs/SectionContent.razor.cs @@ -2,15 +2,14 @@ using Cropper.Blazor.Client.Models; using Microsoft.AspNetCore.Components; using MudBlazor; -using MudBlazor.Services; using MudBlazor.Utilities; namespace Cropper.Blazor.Client.Components.Docs; -public partial class SectionContent +public partial class SectionContent : IBrowserViewportObserver { [Inject] protected IJsApiService? JsApiService { get; set; } - [Inject] IBreakpointService BreakpointService { get; set; } = null!; + [Inject] IBrowserViewportService BreakpointService { get; set; } = null!; protected string Classname => new CssBuilder("docs-section-content") @@ -41,28 +40,30 @@ public partial class SectionContent .AddClass("show-code", HasCode && ShowCode) .Build(); - [Parameter] public string Class { get; set; } + [Parameter] public string Class { get; set; } = string.Empty; [Parameter] public bool DarkenBackground { get; set; } [Parameter] public bool Outlined { get; set; } = true; [Parameter] public bool ShowCode { get; set; } = true; [Parameter] public bool Block { get; set; } [Parameter] public bool FullWidth { get; set; } - [Parameter] public string Code { get; set; } - [Parameter] public string HighLight { get; set; } - [Parameter] public IEnumerable Codes { get; set; } - [Parameter] public RenderFragment ChildContent { get; set; } + [Parameter] public string Code { get; set; } = string.Empty; + [Parameter] public string HighLight { get; set; } = string.Empty; + [Parameter] public IEnumerable? Codes { get; set; } = null; + [Parameter] public RenderFragment ChildContent { get; set; } = null!; private bool HasCode; - public string ActiveCode; + public string ActiveCode = string.Empty; private bool IsVerticalAlign = false; + Guid IBrowserViewportObserver.Id { get; } = Guid.NewGuid(); + protected override void OnParametersSet() { if (Codes != null) { HasCode = true; - ActiveCode = Codes.FirstOrDefault().code; + ActiveCode = Codes.First().code; } else if (!string.IsNullOrWhiteSpace(Code)) { @@ -75,11 +76,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - await BreakpointService!.SubscribeAsync((br) => - { - IsVerticalAlign = BreakpointService!.IsMediaSize(br, Breakpoint.Xs); - InvokeAsync(StateHasChanged); - }); + await BreakpointService!.SubscribeAsync(this, fireImmediately: true); } await base.OnAfterRenderAsync(firstRender); @@ -116,35 +113,49 @@ RenderFragment CodeComponent(string code) => builder => { try { - var key = typeof(SectionContent).Assembly.GetManifestResourceNames().FirstOrDefault(x => x.Contains($".{code}Code.html")); - using (var stream = typeof(SectionContent).Assembly.GetManifestResourceStream(key)) - using (var reader = new StreamReader(stream)) - { - var read = reader.ReadToEnd(); + string? key = typeof(SectionContent).Assembly.GetManifestResourceNames().FirstOrDefault(x => x.Contains($".{code}Code.html")); + using var stream = typeof(SectionContent).Assembly.GetManifestResourceStream(key!); + using var reader = new StreamReader(stream!); + var read = reader.ReadToEnd(); - if (!string.IsNullOrEmpty(HighLight)) + if (!string.IsNullOrEmpty(HighLight)) + { + if (HighLight.Contains(',')) { - if (HighLight.Contains(",")) - { - var highlights = HighLight.Split(","); + var highlights = HighLight.Split(","); - foreach (var value in highlights) - { - read = Regex.Replace(read, $"{value}(?=\\s|\")", $"$&"); - } - } - else + foreach (var value in highlights) { - read = Regex.Replace(read, $"{HighLight}(?=\\s|\")", $"$&"); + read = Regex.Replace(read, $"{value}(?=\\s|\")", $"$&"); } } - - builder.AddMarkupContent(0, read); + else + { + read = Regex.Replace(read, $"{HighLight}(?=\\s|\")", $"$&"); + } } + + builder.AddMarkupContent(0, read); } catch (Exception ex) { - Console.WriteLine(ex.Message); + Console.WriteLine(ex.StackTrace); } }; + + public async Task NotifyBrowserViewportChangeAsync(BrowserViewportEventArgs browserViewportEventArgs) + { + if (browserViewportEventArgs.BrowserWindowSize.Width < 600) + { + IsVerticalAlign = true; + } + else + { + IsVerticalAlign = false; + } + + await InvokeAsync(StateHasChanged); + } + + public async ValueTask DisposeAsync() => await BreakpointService.UnsubscribeAsync(this); } diff --git a/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor b/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor index 90550c69..520b3437 100644 --- a/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor +++ b/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor @@ -1,7 +1,7 @@ -
- @if (!String.IsNullOrWhiteSpace(Title) && HideTitle == false) +
+ @if (!string.IsNullOrWhiteSpace(Title) && HideTitle == false) { - + @Title } diff --git a/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor.cs b/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor.cs index 351e6f76..f7554e60 100644 --- a/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor.cs +++ b/src/Cropper.Blazor/Client/Components/Docs/SectionHeader.razor.cs @@ -12,19 +12,53 @@ public partial class SectionHeader .Build(); [CascadingParameter] private DocsPageSection Section { get; set; } + [CascadingParameter] private DocsPage DocsPage { get; set; } [Parameter] public string Class { get; set; } [Parameter] public string Title { get; set; } + [Parameter] public string TitleClass { get; set; } [Parameter] public bool HideTitle { get; set; } [Parameter] public RenderFragment SubTitle { get; set; } [Parameter] public RenderFragment Description { get; set; } + public DocsSectionLink SectionInfo { get; set; } public ElementReference SectionReference; + protected override void OnInitialized() + { + base.OnInitialized(); + + if (DocsPage == null || string.IsNullOrWhiteSpace(Title)) + { + return; + } + + var parentTitle = DocsPage.GetParentTitle(Section) ?? string.Empty; + if (string.IsNullOrEmpty(parentTitle) == false) + { + parentTitle += '-'; + } + + var id = (parentTitle + Title).Replace(" ", "-").ToLowerInvariant(); + + SectionInfo = new DocsSectionLink { Id = id, Title = Title, }; + } + + protected override void OnAfterRender(bool firstRender) + { + base.OnAfterRender(firstRender); + if (firstRender == true && DocsPage != null && !string.IsNullOrWhiteSpace(Title)) + { + DocsPage.AddSection(SectionInfo, Section); + } + } + + private string GetSectionId() => SectionInfo?.Id ?? Guid.NewGuid().ToString(); + private Typo GetTitleTypo() { if (Section.Level >= 1) diff --git a/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor b/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor index e91f3b37..7a934f0f 100644 --- a/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor +++ b/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor @@ -1,127 +1,242 @@ @using Cropper.Blazor.Models - - @*//---Enable setup minimum and maximum cropped dimensions---//*@ - - @*//---Cropper Data---//*@ - - - Cropper Data - - - - - - - - - - - - Get Data - - - - - Set Data - - - - - -@*//---Container and Crop Box Data---//*@ - - - - Container Data - - - - - - - Get Container Data - - - - - @*//---Crop Box Data---//*@ - - - Crop Box Data - - - - - - - - - Get Data - - - - - Set Data - - - - + + + + +
+ + Event's settings +
+
+ + + + @*//---Enable setup minimum and maximum cropped dimensions---//*@ + + + + + @*//---Enable setup max/min zoom ratio---//*@ + + + + + @*//---Enable setup max/min aspect ratio---//*@ + + + + +
+
-@*//---Image Data---//*@ - - - - - Image Data - - - - - - - - - - - - - - - Get Image Data - - - - + + + + + + +
+ + Get or Set cropper component data +
+
+ + + + @*//---Cropper Data---//*@ + + + + Cropper Data + + + + + + + + + + + + + + + Get Data + + + + + Set Data + + + + + + + + @*//---Crop Box Data---//*@ + + + + Crop Box Data + + + + + + + + + + + + Get Data + + + + + Set Data + + + + + + + + + Container Dataa + + + + + + + + + + Get Container Data + + + + + + + + @*//---Image Data---//*@ + + + + Image Data + + + + + + + + + + + + + + + + + + Get Image Data + + + + + + + + @*//---Canvas Data---//*@ + + + + Canvas Data + + + + + + + + + + + + + + Get Data + + + + + Set Data + + + + + + + +
+
- - @*//---Enable setup max/min zoom ratio---//*@ - - @*//---Canvas Data---//*@ - - - Canvas Data - - - - - - - - - - - Get Data - - - - - Set Data - - - - - \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor.cs b/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor.cs index 35c3868b..52e3a861 100644 --- a/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor.cs +++ b/src/Cropper.Blazor/Client/Components/GetSetCropperData.razor.cs @@ -48,6 +48,11 @@ public void OnZoomEvent(ZoomEvent? zoomEvent) ZoomRatioSettings!.OnZoomEvent(zoomEvent); } + public void SetRatio(decimal? ratio) + { + ZoomRatioSettings!.SetRatio(ratio); + } + public void SetCropBoxData(SetCropBoxDataOptions cropBoxDataOptions) { SetCropBoxDataOptions.Invoke(cropBoxDataOptions); diff --git a/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor b/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor index cedda9e3..a274d088 100644 --- a/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor +++ b/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor @@ -1,20 +1,37 @@ - - - Zoom Ratio Settings - + + + +
+ Zoom Ratio Settings + + Make sure that the current Zoom Ratio value is less than the minimum value, + otherwise this setting may not work correctly. + +
+
+
+ +
+ - - - Zoom event old ratio: @OldRatio - - - Zoom event ratio: @Ratio - +
+
+ + Zoom event old ratio: + + +
+
+ + Zoom event ratio: + + +
diff --git a/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor.cs b/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor.cs index 120ad49d..ba6b6fa1 100644 --- a/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor.cs +++ b/src/Cropper.Blazor/Client/Components/ZoomRatioSettings.razor.cs @@ -1,4 +1,7 @@ -using Cropper.Blazor.Events.ZoomEvent; +using System.ComponentModel.DataAnnotations; +using Cropper.Blazor.Components; +using Cropper.Blazor.Events.ZoomEvent; +using Cropper.Blazor.Models; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; @@ -15,7 +18,7 @@ private decimal? MinZoomRatio set { minZoomRatio = value; - ApplyZoomRulesForCropperAsync(); + InvokeAsync(ApplyZoomRulesForCropperAsync); } } private decimal? MaxZoomRatio @@ -24,7 +27,7 @@ private decimal? MaxZoomRatio set { maxZoomRatio = value; - ApplyZoomRulesForCropperAsync(); + InvokeAsync(ApplyZoomRulesForCropperAsync); } } [Inject] private IJSRuntime? JSRuntime { get; set; } @@ -33,6 +36,9 @@ private decimal? MaxZoomRatio private decimal? Ratio { get; set; } = null; + [CascadingParameter(Name = "CropperComponent"), Required] + private CropperComponent CropperComponent { get; set; } = null!; + public void OnZoomEvent(ZoomEvent? zoomEvent) { OldRatio = zoomEvent?.OldRatio; @@ -41,8 +47,29 @@ public void OnZoomEvent(ZoomEvent? zoomEvent) StateHasChanged(); } + public void SetRatio(decimal? ratio) + { + Ratio = ratio; + + StateHasChanged(); + } + public async Task ApplyZoomRulesForCropperAsync() { + ImageData currentImageData = await CropperComponent!.GetImageDataAsync(); + decimal currentZoomRatio = currentImageData.Width / currentImageData.NaturalWidth; + + if ((MinZoomRatio is not null) && (MinZoomRatio > currentZoomRatio)) + { + ContainerData containerData = await CropperComponent.GetContainerDataAsync(); + CropperComponent.ZoomTo((decimal)MinZoomRatio, containerData.Width / 2, containerData.Height / 2); + } + else if ((MaxZoomRatio is not null) && (currentZoomRatio > MaxZoomRatio)) + { + ContainerData containerData = await CropperComponent.GetContainerDataAsync(); + CropperComponent.ZoomTo((decimal)MaxZoomRatio, containerData.Width / 2, containerData.Height / 2); + } + await JSRuntime!.InvokeVoidAsync("window.overrideOnZoomCropperEvent", MinZoomRatio, MaxZoomRatio); } } diff --git a/src/Cropper.Blazor/Client/Cropper.Blazor.Client.csproj b/src/Cropper.Blazor/Client/Cropper.Blazor.Client.csproj index ee7b3d0a..fa0015e2 100644 --- a/src/Cropper.Blazor/Client/Cropper.Blazor.Client.csproj +++ b/src/Cropper.Blazor/Client/Cropper.Blazor.Client.csproj @@ -27,13 +27,13 @@ - - - - - - - + + + + + + + @@ -81,6 +81,7 @@ + @@ -103,9 +104,11 @@ + + @@ -125,6 +128,7 @@ + @@ -135,6 +139,7 @@ + @@ -143,6 +148,7 @@ + @@ -160,8 +166,8 @@ - + diff --git a/src/Cropper.Blazor/Client/Extensions/DocsVeiewExtension.cs b/src/Cropper.Blazor/Client/Extensions/DocsVeiewExtension.cs index 3ff7de2b..fff12fd6 100644 --- a/src/Cropper.Blazor/Client/Extensions/DocsVeiewExtension.cs +++ b/src/Cropper.Blazor/Client/Extensions/DocsVeiewExtension.cs @@ -1,14 +1,18 @@ using Blazored.LocalStorage; using Cropper.Blazor.Client.Services; using Cropper.Blazor.Client.Services.UserPreferences; +using Cropper.Blazor.Extensions; using MudBlazor; using MudBlazor.Services; namespace Cropper.Blazor.Client.Extensions; + public static class DocsViewExtension { public static void TryAddDocsViewServices(this IServiceCollection services) { + services.AddCropper(); + services.AddMudServices(config => { config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomRight; @@ -24,5 +28,11 @@ public static void TryAddDocsViewServices(this IServiceCollection services) services.AddBlazoredLocalStorage(); services.AddScoped(); services.AddScoped(); + //set the capacity max so that content is not queue. Again this is for prerending to serve the entire page back to crawler + services.AddSingleton( + new RenderQueueService + { + Capacity = int.MaxValue + }); } } diff --git a/src/Cropper.Blazor/Client/Models/DocStrings.cs b/src/Cropper.Blazor/Client/Models/DocStrings.cs new file mode 100644 index 00000000..3ae7ed60 --- /dev/null +++ b/src/Cropper.Blazor/Client/Models/DocStrings.cs @@ -0,0 +1,36 @@ +using Cropper.Blazor.Shared.Extensions; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Cropper.Blazor.Client.Models +{ + // this is needed for the api docs + public static partial class DocStrings + { + /* To speed up the method, run it in this way: + * string saveTypename = DocStrings.GetSaveTypename(type); // calculate it only once + * DocStrings.GetMemberDescription(saveTypename, member); + */ + public static string GetMemberDescription(string saveTypename, MemberInfo member) + { + string name; + + if (member is PropertyInfo property) + name = saveTypename + "_" + property.Name; + else if (member is MethodInfo method) + name = saveTypename + "_method_" + GetSaveMethodIdentifier(method); + else + throw new Exception("Implemented only for properties and methods."); + + var field = typeof(DocStrings).GetField(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.GetField); + if (field == null) + return null; + return (string)field.GetValue(null); + } + + public static string GetSaveTypename(Type t) => Regex.Replace(t.ConvertToCSharpSource(), @"[\.]", "_").Replace("", "").TrimEnd('_'); + + private static string GetSaveMethodIdentifier(MethodInfo method) => Regex.Replace(method.ToString() + .Replace("Cropper.Blazor.Docs.Models.T", "T"), "[^A-Za-z0-9_]", "_"); // method signature + } +} diff --git a/src/Cropper.Blazor/Client/Pages/About.razor b/src/Cropper.Blazor/Client/Pages/About.razor index d7c5e258..5f00f403 100644 --- a/src/Cropper.Blazor/Client/Pages/About.razor +++ b/src/Cropper.Blazor/Client/Pages/About.razor @@ -1,4 +1,13 @@ @page "/about" + + + Sorry Not available now diff --git a/src/Cropper.Blazor/Client/Pages/Api.razor b/src/Cropper.Blazor/Client/Pages/Api.razor index 5f1b1a0d..0f3093b8 100644 --- a/src/Cropper.Blazor/Client/Pages/Api.razor +++ b/src/Cropper.Blazor/Client/Pages/Api.razor @@ -1,5 +1,26 @@ @page "/api" - - Sorry - Not available now - +@using Cropper.Blazor.Client.Components.Docs +@using Cropper.Blazor.Components; + + +@if (ComponentType == null) +{ + +} +else +{ + +} + + +@code { + [Parameter] public string component { get; set; } + + public Type ComponentType { get; set; } + + protected override void OnParametersSet() + { + ComponentType = typeof(CropperComponent); + StateHasChanged(); + } +} \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Pages/CropperDemo.razor b/src/Cropper.Blazor/Client/Pages/CropperDemo.razor index 9577a519..7be9b6b4 100644 --- a/src/Cropper.Blazor/Client/Pages/CropperDemo.razor +++ b/src/Cropper.Blazor/Client/Pages/CropperDemo.razor @@ -4,15 +4,23 @@ @using Cropper.Blazor.Extensions; @using Cropper.Blazor.Models + + @*//---Cropper Component---//*@ - +
@*//---Img preview and data---//*@ - +
@@ -48,393 +56,403 @@
- @*//---Buttons for test cropper functional---//*@ - - - - - - - - - - + + + + +
+ + Actions +
+
+ + + @*//---Buttons for test cropper functional---//*@ + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - Move to [0,0] - - - - - Zoom to 100% - - - - - 180° - - - - - Scale (-2, -1) - - - - - Get Cropped Canvas by URL - - - - - Get Cropped Canvas by element - - - - - - 320×180 - - - - - 640×360 - - - - - - @*//---Aspect Ratio | View Mode | Get Cropped Canvas---//*@ - - - - - - - - 16:9 - - - - - 4:3 - - - - - 1:1 - - - - - 2:3 - - - - - Free - - - - - - - VM0 - - - - - VM1 - - - - - VM2 - - - - - VM3 - - - - - - - - - - - - - - - - - - - - - - GET - - - - - - - - - 16:9 - - - - - 4:3 - - - - - 1:1 - - - - - 2:3 - - - - - Free - - - - - - - VM0 - - - - - VM1 - - - - - VM2 - - - - - VM3 - - - - - - - - - - - - - - - - - - - - - - GET - - - - - - - - Toggle Options - - - - - @foreach (var item in Options.GetType().GetProperties().Where(p => p.PropertyType == typeof(bool?))) - { - - - - } - - - - - + + + Move to [0,0] + + + + + Zoom to 100% + + + + + 180° + + + + + Scale (-2, -1) + + + + + Get Cropped Canvas by URL + + + + + Get Cropped Canvas by element + + + + + + 320×180 + + + + + 640×360 + + + +
+
+ @*//---Aspect Ratio | View Mode | Get Cropped Canvas---//*@ + + + + + + + + GET + + + + + + + Toggle Options + + + + + @foreach (var item in Options.GetType().GetProperties().Where(p => p.PropertyType == typeof(bool?))) + { + + + + } + + + + + + + + 16:9 + + + + + 4:3 + + + + + 1:1 + + + + + 2:3 + + + + + Free + + + + + + + VM0 + + + + + VM1 + + + + + VM2 + + + + + VM3 + + + + + + + + + + + + + + + + + + + + + + + + + + GET + + + + + + + Toggle Options + + + + + @foreach (var item in Options.GetType().GetProperties().Where(p => p.PropertyType == typeof(bool?))) + { + + + + } + + + + + + + + 16:9 + + + + + 4:3 + + + + + 1:1 + + + + + 2:3 + + + + + Free + + + + + + + VM0 + + + + + VM1 + + + + + VM2 + + + + + VM3 + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@*//+---// Get/Set all cropper data //---+//*@ - + + @ref="GetSetCropperData" /> diff --git a/src/Cropper.Blazor/Client/Pages/CropperDemo.razor.cs b/src/Cropper.Blazor/Client/Pages/CropperDemo.razor.cs index 208dfc1b..d9969711 100644 --- a/src/Cropper.Blazor/Client/Pages/CropperDemo.razor.cs +++ b/src/Cropper.Blazor/Client/Pages/CropperDemo.razor.cs @@ -34,7 +34,7 @@ public partial class CropperDemo : IDisposable private decimal? ScaleXValue; private decimal? ScaleYValue; private decimal AspectRatio = 1.7777777777777777m; - private bool IsEnableAspectRatioSettings; + private bool IsFreeAspectRatioEnabled; private string Src = "https://fengyuanchen.github.io/cropperjs/v2/picture.jpg"; private bool IsErrorLoadImage { get; set; } = false; @@ -94,15 +94,27 @@ public async void OnCropEvent(JSEventData cropJSEvent) || height > GetSetCropperData!.CroppedDimensionsSettings.MaximumHeight ) { - CropperComponent!.SetData(new SetDataOptions + decimal nWidth = Math.Max(GetSetCropperData!.CroppedDimensionsSettings.MinimumWidth ?? 0M, + Math.Min(GetSetCropperData!.CroppedDimensionsSettings.MaximumWidth ?? 0M, width)); + decimal nHeight = Math.Max(GetSetCropperData!.CroppedDimensionsSettings.MinimumHeight ?? 0M, + Math.Min(GetSetCropperData!.CroppedDimensionsSettings.MaximumHeight ?? 0M, height)); + + if (!IsFreeAspectRatioEnabled) { - Width = Math.Max( - GetSetCropperData!.CroppedDimensionsSettings.MinimumWidth ?? 0M, - Math.Min(GetSetCropperData!.CroppedDimensionsSettings.MaximumWidth ?? 0M, width)), - Height = Math.Max( - GetSetCropperData!.CroppedDimensionsSettings.MinimumHeight ?? 0M, - Math.Min(GetSetCropperData!.CroppedDimensionsSettings.MaximumHeight ?? 0M, height)), + if (nWidth == 0) + { + nWidth = nHeight * AspectRatio; + } + else if (nHeight == 0) + { + nHeight = nWidth * AspectRatio; + } + } + CropperComponent!.SetData(new SetDataOptions + { + Width = nWidth, + Height = nHeight }); } else @@ -215,6 +227,14 @@ public async void OnCropMoveEvent(JSEventData cropMoveJSEvent) public async void OnCropReadyEvent(JSEventData jSEventData) { await JSRuntime!.InvokeVoidAsync("console.log", $"CropReadyJSEvent, {JsonSerializer.Serialize(jSEventData)}"); + + await InvokeAsync(async () => + { + ImageData imageData = await CropperComponent!.GetImageDataAsync(); + decimal initZoomRatio = imageData.Width / imageData.NaturalWidth; + + GetSetCropperData!.SetRatio(initZoomRatio); + }); } public async void OnLoadImageEvent() @@ -305,14 +325,14 @@ private void Destroy() CropperComponent?.RevokeObjectUrlAsync(Src); } - public void SetAspectRatio(decimal aspectRatio, bool isEnableAspectRatioSettings = false) + public void SetAspectRatio(decimal aspectRatio, bool isFreeAspectRatioEnabled = false) { - IsEnableAspectRatioSettings = isEnableAspectRatioSettings; + IsFreeAspectRatioEnabled = isFreeAspectRatioEnabled; - if (aspectRatio != 0) - { - AspectRatio = aspectRatio; - } + //if (aspectRatio != 0) + //{ + // AspectRatio = aspectRatio; + //} CropperComponent?.SetAspectRatio(aspectRatio); } diff --git a/src/Cropper.Blazor/Client/Pages/Index.razor b/src/Cropper.Blazor/Client/Pages/Index.razor index 7610889f..9617fc48 100644 --- a/src/Cropper.Blazor/Client/Pages/Index.razor +++ b/src/Cropper.Blazor/Client/Pages/Index.razor @@ -5,6 +5,15 @@ @using Cropper.Blazor.Client.Pages.Examples.Installation @using Cropper.Blazor.Client.Pages.Examples.Uses.Preview + + @@ -13,7 +22,7 @@ - Cropper.Blazor + Cropper.Blazor @@ -24,13 +33,23 @@ Cropper.Blazor - is a component element that wraps around + is a component that wraps around Cropper.js + + + View Demo + + + Star on GitHub + +

Build and run test @@ -38,6 +57,9 @@ Deploy to GitHub Pages + + Deploy to NuGet + Code coverage diff --git a/src/Cropper.Blazor/Client/Services/RenderQueueService.cs b/src/Cropper.Blazor/Client/Services/RenderQueueService.cs new file mode 100644 index 00000000..6093df30 --- /dev/null +++ b/src/Cropper.Blazor/Client/Services/RenderQueueService.cs @@ -0,0 +1,100 @@ +using Cropper.Blazor.Client.Components.Docs; + +namespace Cropper.Blazor.Client.Services +{ + public interface IRenderQueueService + { + int Capacity { get; } + + void Enqueue(QueuedContent component); + Task WaitUntilEmpty(); + void Clear(); + } + + public class RenderQueueService : IRenderQueueService + { + private Queue _queue = new(); + private TaskCompletionSource _tcs; + + public int Capacity { get; init; } + + public RenderQueueService() + { + Capacity = 3; + } + + public void Clear() + { + lock (_queue) + { + _queue.Clear(); + _tcs?.TrySetResult(); + _tcs = null; + } + } + + void IRenderQueueService.Enqueue(QueuedContent component) + { + bool renderImmediately = false; + lock (_queue) + { + renderImmediately = _queue.Count == 0; + _queue.Enqueue(component); + component.Rendered += OnComponentRendered; + component.Disposed += OnComponentDisposed; + } + if (renderImmediately) + component.Render(); + } + + private async void RenderNext() + { + QueuedContent componentToRender = null; + lock (_queue) + { + while (_queue.Count > 0) + { + var component = _queue.Dequeue(); + if (component.IsDisposed || component.IsRendered) + { + component.Rendered -= OnComponentRendered; + component.Disposed -= OnComponentDisposed; + continue; + } + componentToRender = component; + break; + } + if (componentToRender == null) + { + _tcs?.TrySetResult(); + _tcs = null; + return; + } + } + await Task.Delay(1); + componentToRender.Render(); + } + + private void OnComponentRendered(QueuedContent component) + { + RenderNext(); + } + + private void OnComponentDisposed(QueuedContent component) + { + RenderNext(); + } + + public Task WaitUntilEmpty() + { + lock (_queue) + { + if (_queue.Count == 0) + return Task.CompletedTask; + if (_tcs == null) + _tcs = new TaskCompletionSource(); + return _tcs.Task; + } + } + } +} diff --git a/src/Cropper.Blazor/Client/Shared/SeoHeader.razor b/src/Cropper.Blazor/Client/Shared/SeoHeader.razor new file mode 100644 index 00000000..6999c9ab --- /dev/null +++ b/src/Cropper.Blazor/Client/Shared/SeoHeader.razor @@ -0,0 +1,12 @@ +@Title + + + + + @if (!string.IsNullOrEmpty(Overview)) + { + + + + } + \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/Shared/SeoHeader.razor.cs b/src/Cropper.Blazor/Client/Shared/SeoHeader.razor.cs new file mode 100644 index 00000000..91cf0d2b --- /dev/null +++ b/src/Cropper.Blazor/Client/Shared/SeoHeader.razor.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Components; + +namespace Cropper.Blazor.Client.Shared +{ + public partial class SeoHeader + { + [Parameter] + public string? Title { get; set; } + + [Parameter] + public string? Overview { get; set; } + + [Parameter] + public IEnumerable Keywords { get; set; } = new List(); + + string GetSubTitle() + { + if (string.IsNullOrEmpty(Overview)) + return ""; + return Overview.TrimEnd('.') + "."; + } + + string GetKeywords() + { + var keywords = new List(); + + keywords.AddRange(Keywords); + + keywords.AddRange(new[] + { + "Cropper.Blazor", + "blazor", + "component", + "crop-image", + "cropper", + "cropperjs", + "Cropper.js", + "Blazor Components", + "Blazor Library", + "Blazor Cropper", + "Cropper", + "Image", + "Crop", + "Resize", + "image-cropper", + "crop-image", + "csharp", + "blazor-cropper" + }); + + return string.Join(", ", keywords); + } + } +} diff --git a/src/Cropper.Blazor/Client/Styles/Cropper.Blazor.Client.scss b/src/Cropper.Blazor/Client/Styles/Cropper.Blazor.Client.scss index 6179f399..e107222b 100644 --- a/src/Cropper.Blazor/Client/Styles/Cropper.Blazor.Client.scss +++ b/src/Cropper.Blazor/Client/Styles/Cropper.Blazor.Client.scss @@ -96,6 +96,21 @@ html, body { height: 6px; } +.button-gradient { + position: relative; + animation-duration: 10s; + animation-delay: 1s; + animation-iteration-count: infinite; + animation-timing-function: ease; + animation-name: flow; + background-size: 400% 400%; + background-image: linear-gradient(82deg, #e73c7e 0%, #23a6d5 100%, #e73c7e 200%); +} + +.button-gradient:hover { + box-shadow: 0.3px 0.5px 0.4px hsla(200, 100%, 38%, 0.62), 0.3px 0.6px 0.5px -0.7px hsla(200, 100%, 38%, 0.54), 0.8px 1.6px 1.3px -1.4px hsla(200, 100%, 38%, 0.46), 2px 4.1px 3.4px -2.1px hsla(200, 100%, 38%, 0.38), 4.5px 9px 7.5px -2.9px hsla(200, 100%, 38%, 0.31), 8.5px 17.1px 14.3px -3.6px hsla(200, 100%, 38%, 0.23), 14.6px 29.2px 24.5px -4.3px hsla(200, 100%, 38%, 0.15), 23px 46px 38.6px -5px hsla(200, 100%, 38%, 0.08); +} + .cropper-error-load { max-height: inherit; max-width: inherit; diff --git a/src/Cropper.Blazor/Client/Styles/components/docssection.scss b/src/Cropper.Blazor/Client/Styles/components/docssection.scss index 0f33fde0..fc338b56 100644 --- a/src/Cropper.Blazor/Client/Styles/components/docssection.scss +++ b/src/Cropper.Blazor/Client/Styles/components/docssection.scss @@ -46,7 +46,7 @@ &:hover { .copy-code-button { - color: var(--mud-palette-text-secondary) !important; + color: var(--mud-palette-secondary-darken) !important; background-color: transparent; } } @@ -178,3 +178,34 @@ } } } + +@media (min-width: 1280px) { + .mud-drawer-open-responsive-lg-right .mud-main-content { + margin-right: 120px !important; + } +} + +.disabled { + pointer-events: none; + cursor: default; + opacity: 0.6; +} + +.docs-content-api-max-width { + max-width: 800px; +} + +.docs-content-return-api-max-width, .docs-content-description-api-max-width { + max-width: 400px; +} + +.docs-content-api-sm-max-width { + text-align: end; + padding: 10px; +} + +.docs-content-api-desc-sm-max-width { + display: inline-flex; + flex-direction: row; + white-space: pre; +} diff --git a/src/Cropper.Blazor/Client/Styles/layout/_mainlayout.scss b/src/Cropper.Blazor/Client/Styles/layout/_mainlayout.scss index 8baa4226..8b535ac7 100644 --- a/src/Cropper.Blazor/Client/Styles/layout/_mainlayout.scss +++ b/src/Cropper.Blazor/Client/Styles/layout/_mainlayout.scss @@ -1,7 +1,7 @@ .title { font-family: 'Public Sans', 'Roboto', 'Arial', sans-serif; font-weight: 600; - font-size: 3.75rem; + font-size: 1.75rem; } .title-description { diff --git a/src/Cropper.Blazor/Client/Theme/Theme.cs b/src/Cropper.Blazor/Client/Theme/Theme.cs index 5f5c41aa..4275f93f 100644 --- a/src/Cropper.Blazor/Client/Theme/Theme.cs +++ b/src/Cropper.Blazor/Client/Theme/Theme.cs @@ -71,7 +71,6 @@ public static MudTheme CropperBlazorDocsTheme() TableLines = "#33323e", Divider = "#292838", OverlayLight = "#1e1e2d80" - }; #endregion diff --git a/src/Cropper.Blazor/Client/wwwroot/index.html b/src/Cropper.Blazor/Client/wwwroot/index.html index 15d27955..149302ff 100644 --- a/src/Cropper.Blazor/Client/wwwroot/index.html +++ b/src/Cropper.Blazor/Client/wwwroot/index.html @@ -1,11 +1,29 @@ - - + + + + + + + + + + + + + + + + + + + + - Cropper.Blazor + Cropper Blazor component for cropping images. @@ -134,4 +152,4 @@ - + \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/wwwroot/overrideCropperJsInteropModule.js b/src/Cropper.Blazor/Client/wwwroot/overrideCropperJsInteropModule.js index 103ad7f7..087682ac 100644 --- a/src/Cropper.Blazor/Client/wwwroot/overrideCropperJsInteropModule.js +++ b/src/Cropper.Blazor/Client/wwwroot/overrideCropperJsInteropModule.js @@ -1,9 +1,11 @@ window.overrideOnZoomCropperEvent = (minZoomRatio, maxZoomRatio) => { window.cropper.onZoom = function (imageObject, event, correlationId) { - const jSEventData = this.getJSEventData(event, correlationId); - const isApplyPreventZoomRatio = minZoomRatio != null || maxZoomRatio != null; + const jSEventData = this.getJSEventData(event, correlationId); + + const isApplyPreventZoomMinRatio = (minZoomRatio != null) && (minZoomRatio > event.detail.ratio); + const isApplyPreventZoomMaxRatio = (maxZoomRatio != null) && (event.detail.ratio > maxZoomRatio); - if (isApplyPreventZoomRatio && (event.detail.ratio < minZoomRatio || event.detail.ratio > maxZoomRatio)) { + if (isApplyPreventZoomMinRatio || isApplyPreventZoomMaxRatio) { event.preventDefault(); } else { diff --git a/src/Cropper.Blazor/Client/wwwroot/resizeWindowEventListener.js b/src/Cropper.Blazor/Client/wwwroot/resizeWindowEventListener.js new file mode 100644 index 00000000..b81ddcd1 --- /dev/null +++ b/src/Cropper.Blazor/Client/wwwroot/resizeWindowEventListener.js @@ -0,0 +1,19 @@ +let timer +window.addEventListener('resize', () => { + if (!Object.hasOwn(this, 'cropper') || cropper == null || cropper.cropperInstances == null) { + return; + } + let keys = Object.keys(cropper.cropperInstances); + clearTimeout(timer); + if (keys.length > 0) { + keys.forEach((key) => { + cropper.cropperInstances[key].disable(); + }); + timer = setTimeout(() => { + let keys = Object.keys(cropper.cropperInstances); + keys.forEach((key) => { + cropper.cropperInstances[key].enable(); + }); + }, 100); + } +}) \ No newline at end of file diff --git a/src/Cropper.Blazor/Client/wwwroot/sitemap.xml b/src/Cropper.Blazor/Client/wwwroot/sitemap.xml new file mode 100644 index 00000000..03dff367 --- /dev/null +++ b/src/Cropper.Blazor/Client/wwwroot/sitemap.xml @@ -0,0 +1,27 @@ + + + + https://cropperblazor.github.io/ + 2023-08-02T21:02:02+03:00 + daily + 1.0 + + + https://cropperblazor.github.io/demo + 2023-08-02T21:02:02+03:00 + daily + 0.9 + + + https://cropperblazor.github.io/api + 2023-08-02T21:02:02+03:00 + daily + 0.8 + + + https://cropperblazor.github.io/about + 2023-08-02T21:02:02+03:00 + daily + 0.7 + + \ No newline at end of file diff --git a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/CodeSnippetsCompiler.cs b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/CodeSnippetsCompiler.cs index 71196864..ebc63027 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/CodeSnippetsCompiler.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/CodeSnippetsCompiler.cs @@ -6,9 +6,9 @@ namespace Cropper.Blazor.Client.Compiler; -public class CodeSnippetsCompiler +public static partial class CodeSnippetsCompiler { - public bool Execute() + public static bool Execute() { var paths = new Paths(); var success = true; @@ -61,7 +61,10 @@ public bool Execute() private static string EscapeComponentSource(string path) { var source = File.ReadAllText(path, Encoding.UTF8); - source = Regex.Replace(source, "@(namespace|layout|page) .+?\n", string.Empty); + source = EscapeComponentSourceRegex().Replace(source, string.Empty); return source.Replace("\"", "\"\"").Trim(); } + + [GeneratedRegex("@(namespace|layout|page) .+?\n")] + private static partial Regex EscapeComponentSourceRegex(); } diff --git a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Cropper.Blazor.Client.Compiler.csproj b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Cropper.Blazor.Client.Compiler.csproj index 120e38c3..6daed878 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Cropper.Blazor.Client.Compiler.csproj +++ b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Cropper.Blazor.Client.Compiler.csproj @@ -1,8 +1,13 @@ - + Exe net7.0 + + + + + diff --git a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/DocStrings.cs b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/DocStrings.cs new file mode 100644 index 00000000..69f92b60 --- /dev/null +++ b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/DocStrings.cs @@ -0,0 +1,139 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using Cropper.Blazor.Components; +using Cropper.Blazor.Shared.Extensions; +using Microsoft.AspNetCore.Components; + +namespace Cropper.Blazor.Client.Compiler +{ + public class DocStrings + { + private static string[] hiddenMethods = { "ToString", "GetType", "GetHashCode", "Equals", "SetParametersAsync", "ReferenceEquals" }; + + public bool Execute() + { + var paths = new Paths(); + var success = true; + try + { + var currentCode = string.Empty; + if (File.Exists(paths.DocStringsFilePath)) + { + currentCode = File.ReadAllText(paths.DocStringsFilePath); + } + + var cb = new CodeBuilder(); + cb.AddHeader(); + cb.AddLine("namespace Cropper.Blazor.Client.Models"); + cb.AddLine("{"); + cb.IndentLevel++; + cb.AddLine("public static partial class DocStrings"); + cb.AddLine("{"); + cb.IndentLevel++; + + var assembly = typeof(CropperComponent).Assembly; + foreach (var type in assembly.GetTypes().OrderBy(t => GetSaveTypename(t))) + { + foreach (var property in type.GetPropertyInfosWithAttribute()) + { + var doc = property.GetDocumentation() ?? ""; + doc = ConvertSeeTags(doc); + doc = Regex.Replace(doc, @"", ""); // remove all other XML tags + cb.AddLine($"public const string {GetSaveTypename(type)}_{property.Name} = @\"{EscapeDescription(doc).Trim()}\";\n"); + } + + // TableContext was causing conflicts due to the imperfect mapping from the name of class to the name of field in DocStrings + if (type.IsSubclassOf(typeof(Attribute)) || GetSaveTypename(type) == "TypeInference" + || GetSaveTypename(type).StartsWith("EventUtil_")) + continue; + + foreach (var method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy)) + { + if (!hiddenMethods.Any(x => x.Contains(method.Name)) && !method.Name.StartsWith("get_") && !method.Name.StartsWith("set_")) + { + // omit methods defined in System.Enum + if (GetBaseDefinitionClass(method) == typeof(Enum)) + continue; + + var doc = method.GetDocumentation() ?? ""; + doc = ConvertSeeTagsFormethod(doc); + doc = NormalizeWord(doc); + cb.AddLine($"public const string {GetSaveTypename(type)}_method_{GetSaveMethodIdentifier(method)} = @\"{EscapeDescription(doc)}\";\n"); + } + } + } + + cb.IndentLevel--; + cb.AddLine("}"); + cb.IndentLevel--; + cb.AddLine("}"); + + if (currentCode != cb.ToString()) + { + File.WriteAllText(paths.DocStringsFilePath, cb.ToString()); + } + } + catch (Exception e) + { + Console.WriteLine($"Error generating {paths.DocStringsFilePath} : {e.Message}"); + success = false; + } + + return success; + } + + private static string GetSaveTypename(Type t) => Regex.Replace(t.ConvertToCSharpSource(), @"[\.,<>]", "_").TrimEnd('_'); + + /* Methods can be overloaded so the method name doesn't identify it uniquely. Instead of method name we need the method signature. + * Currently the return type of a method is also used, but probably it can be removed. + * + * Alternatively we could use the format similar to this used in XML documentation - it will be even better because I think it is + * less likely to be changed in the future. See XmlDocumentation.cs for a method computing identifiers. + */ + private static string GetSaveMethodIdentifier(MethodInfo method) => Regex.Replace(method.ToString(), "[^A-Za-z0-9_]", "_"); + + private static Type GetBaseDefinitionClass(MethodInfo m) => m.GetBaseDefinition().DeclaringType; + + /* Replace tags by TYPE_OR_MEMBER_QUALIFIED_NAME without "Cropper.Blazor." at the beginning. + * It is a quick fix. It should be rather represented by ... but it is more difficult. + */ + private static string ConvertSeeTags(string doc) + { + return Regex.Replace(doc, "]+)\" */>", match => + { + string result = match.Groups[2].Value; // get the name of Type or type member (Field, Property, Method, or Event) + result = Regex.Replace(result, "`1", ""); // remove `1 from generic type name + return result; + }); + } + + private static string ConvertSeeTagsFormethod(string doc) + { + var result = doc + .Replace("
", "") + .Replace("", "scaleX") + .Replace("", "IBrowserFile") + .Replace("", "DotNetStreamReference") + .Replace("", "ValueTask") + .Replace("", "ValueTask<>") + .Replace("", "JSEventData<>") + .Replace("", "CancellationToken"); + + return ConvertSeeTags(result); + } + + private static string NormalizeWord(string doc) + { + //string.Join(" ", doc.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries)); + return Regex.Replace(doc, @"\s+", " "); + } + + private static string EscapeDescription(string doc) + { + return doc.Replace("\"", "\"\""); + } + } +} diff --git a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Paths.cs b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Paths.cs index af28e824..c7a9e353 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Paths.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Paths.cs @@ -6,6 +6,7 @@ namespace Cropper.Blazor.Client.Compiler; public class Paths { private const string DocsDirectory = "Client"; + private const string DocStringsFile = "DocStrings.generated.cs"; private const string SnippetsFile = "Snippets.generated.cs"; public const string ExampleDiscriminator = "Example"; // example components must contain this string @@ -48,4 +49,12 @@ public string SnippetsFilePath return Path.Join(DocsStringSnippetsDirPath, SnippetsFile); } } + + public string DocStringsFilePath + { + get + { + return Path.Join(DocsStringSnippetsDirPath, DocStringsFile); + } + } } diff --git a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Program.cs b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Program.cs index c84c9583..b0e05d31 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Program.cs +++ b/src/Cropper.Blazor/Cropper.Blazor.Client.Compiler/Program.cs @@ -9,9 +9,11 @@ public static int Main() { var stopWatch = Stopwatch.StartNew(); var success = - new CodeSnippetsCompiler().Execute(); + CodeSnippetsCompiler.Execute() + && new DocStrings().Execute(); Console.WriteLine($"Docs.Compiler completed in {stopWatch.ElapsedMilliseconds} msecs"); + return success ? 0 : 1; } } diff --git a/src/Cropper.Blazor/Cropper.Blazor.Shared/Cropper.Blazor.Shared.csproj b/src/Cropper.Blazor/Cropper.Blazor.Shared/Cropper.Blazor.Shared.csproj new file mode 100644 index 00000000..cfadb03d --- /dev/null +++ b/src/Cropper.Blazor/Cropper.Blazor.Shared/Cropper.Blazor.Shared.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/MethodInfoExtensions.cs b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/MethodInfoExtensions.cs new file mode 100644 index 00000000..595828f9 --- /dev/null +++ b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,185 @@ +using System.Reflection; +using System.Text; + +namespace Cropper.Blazor.Shared.Extensions +{ + // Adaptation from : https://stackoverflow.com/questions/1312166/print-full-signature-of-a-method-from-a-methodinfo/1312321 + public static class MethodInfoExtensions + { + ///

+ /// Return the method signature as a string. + /// + /// The Method + /// Return as an callable string(public void a(string b) would return a(b)) + /// Method signature + public static string GetSignature(this MethodInfo method, bool callable = false) + { + // Define local variables + var firstParameter = true; + var secondParameter = false; + var stringBuilder = new StringBuilder(); + + // Define the method access + if (callable == false) + { + // Append return type + stringBuilder.Append(RemoveNamespace(TypeName(method.ReturnType))); + stringBuilder.Append(' '); + } + + // Add the name of the method + stringBuilder.Append(method.Name); + + // Add generics method + if (method.IsGenericMethod) + { + stringBuilder.Append('<'); + + foreach (var genericArgument in method.GetGenericArguments()) + { + if (firstParameter) + { + firstParameter = false; + } + else + { + stringBuilder.Append(", "); + } + + stringBuilder.Append(TypeName(genericArgument)); + } + + stringBuilder.Append('>'); + } + + stringBuilder.Append('('); + firstParameter = true; + + foreach (var parameter in method.GetParameters()) + { + if (firstParameter) + { + firstParameter = false; + + if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false)) + { + if (callable) + { + secondParameter = true; + continue; + } + stringBuilder.Append("this "); + } + } + else if (secondParameter == true) + { + secondParameter = false; + } + else + { + stringBuilder.Append(", "); + } + + if (parameter.ParameterType.IsByRef) + { + stringBuilder.Append("ref "); + } + else if (parameter.IsOut) + { + stringBuilder.Append("out "); + } + + if (!callable) + { + stringBuilder.Append(TypeName(parameter.ParameterType)); + stringBuilder.Append(' '); + } + + stringBuilder.Append(parameter.Name); + } + + stringBuilder.Append(')'); + + // Return final result + return stringBuilder.ToString(); + } + + public static string GetAliases(string value, Type type = null) + { + switch (value.ToUpperInvariant()) + { + case "STRING": return "string"; + case "INT16": return "short"; + case "INT32": return "int"; + case "INT64": return "long"; + case "INTPTR": return "nint"; + case "UINT16": return "ushort"; + case "UINT32": return "uint"; + case "UINT64": return "ulong"; + case "UINTPTR": return "nuint"; + case "DOUBLE": return "double"; + case "DECIMAL": return "decimal"; + case "OBJECT": return "object"; + case "VOID": return string.Empty; + case "BOOLEAN": return "bool"; + case "SBYTE": return "sbyte"; + case "CHAR": return "char"; + case "FLOAT": return "float"; + default: + { + if (type != null) + { + return string.IsNullOrWhiteSpace(type.FullName) ? RemoveNamespace(type.Name) : RemoveNamespace(type.FullName); + } + else + { + return RemoveNamespace(value); + } + } + } + } + + /// + /// Get full type name with full namespace names + /// + /// Type. May be generic or nullable + /// Full type name, fully qualified namespaces + private static string TypeName(Type type) + { + var first = true; + var nullableType = Nullable.GetUnderlyingType(type); + + if (nullableType != null) + { + return RemoveNamespace(nullableType.Name + "?"); + } + + if (!(type.IsGenericType && type.Name.Contains('`'))) + { + return GetAliases(type.Name.ToUpperInvariant(), type); + } + + var stringBuilder = new StringBuilder(type.Name.Substring(0, type.Name.IndexOf('`'))); + stringBuilder.Append('<'); + + foreach (var t in type.GetGenericArguments()) + { + if (!first) + { + stringBuilder.Append(','); + } + stringBuilder.Append(TypeName(t)); + first = false; + } + stringBuilder.Append('>'); + + // Return result + return RemoveNamespace(stringBuilder.ToString()); + } + + private static string RemoveNamespace(string value) + { + return value.Split('.')[value.Split('.').Length - 1]; + } + } +} diff --git a/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/TypeNameHelper.cs b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/TypeNameHelper.cs new file mode 100644 index 00000000..c0e05c3f --- /dev/null +++ b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/TypeNameHelper.cs @@ -0,0 +1,216 @@ +using System.Text; + +namespace Cropper.Blazor.Shared.Extensions +{ + // Adapted from https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs + public static class TypeNameHelper + { + public static string GetTypeDisplayName(this Type type) => GetTypeDisplayName(type, false); + + public static readonly Dictionary BuiltInTypeNames = new Dictionary + { + { typeof(void), "void" }, + { typeof(bool), "bool" }, + { typeof(byte), "byte" }, + { typeof(char), "char" }, + { typeof(decimal), "decimal" }, + { typeof(double), "double" }, + { typeof(float), "float" }, + { typeof(int), "int" }, + { typeof(long), "long" }, + { typeof(object), "object" }, + { typeof(sbyte), "sbyte" }, + { typeof(short), "short" }, + { typeof(string), "string" }, + { typeof(uint), "uint" }, + { typeof(ulong), "ulong" }, + { typeof(ushort), "ushort" } + }; + + public static readonly Dictionary FSharpTypeNames = new Dictionary + { + { "Unit", "void" }, + { "FSharpOption", "Option" }, + { "FSharpAsync", "Async" }, + { "FSharpOption`1", "Option" }, + { "FSharpAsync`1", "Async" } + }; + + /// + /// Pretty print a type name. + /// + /// The . + /// true to print a fully qualified name. + /// true to include generic parameter names. + /// The pretty printed type name. + public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false) + { + var builder = new StringBuilder(); + ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames)); + return builder.ToString(); + } + + public static StringBuilder AppendTypeDisplayName(this StringBuilder builder, Type type, bool fullName = true, bool includeGenericParameterNames = false) + { + ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames)); + return builder; + } + + /// + /// Returns a name of given generic type without '`'. + /// + public static string GetTypeNameForGenericType(Type type) + { + if (!type.IsGenericType) + { + throw new ArgumentException("The given type should be generic", nameof(type)); + } + + var genericPartIndex = type.Name.IndexOf('`'); + + return genericPartIndex >= 0 ? type.Name.Substring(0, genericPartIndex) : type.Name; + } + + private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options) + { + if (type.IsGenericType) + { + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType != null) + { + ProcessType(builder, underlyingType, options); + builder.Append('?'); + } + else + { + var genericArguments = type.GetGenericArguments(); + ProcessGenericType(builder, type, genericArguments, genericArguments.Length, options); + } + } + else if (type.IsArray) + { + ProcessArrayType(builder, type, options); + } + else if (BuiltInTypeNames.TryGetValue(type, out var builtInName)) + { + builder.Append(builtInName); + } + else if (type.Namespace == nameof(System)) + { + builder.Append(type.Name); + } + else if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll" + && FSharpTypeNames.TryGetValue(type.Name, out builtInName)) + { + builder.Append(builtInName); + } + else if (type.IsGenericParameter) + { + if (options.IncludeGenericParameterNames) + { + builder.Append(type.Name); + } + } + else + { + builder.Append(options.FullName ? type.FullName ?? type.Name : type.Name); + } + } + + private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options) + { + var innerType = type; + while (innerType.IsArray) + { + if (innerType.GetElementType() is { } inner) + { + innerType = inner; + } + } + + ProcessType(builder, innerType, options); + + while (type.IsArray) + { + builder.Append('['); + builder.Append(',', type.GetArrayRank() - 1); + builder.Append(']'); + if (type.GetElementType() is not { } elementType) + { + break; + } + type = elementType; + } + } + + private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options) + { + var offset = 0; + if (type.IsNested && type.DeclaringType is not null) + { + offset = type.DeclaringType.GetGenericArguments().Length; + } + + if (options.FullName) + { + if (type.IsNested && type.DeclaringType is not null) + { + ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options); + builder.Append('+'); + } + else if (!string.IsNullOrEmpty(type.Namespace)) + { + builder.Append(type.Namespace); + builder.Append('.'); + } + } + + var genericPartIndex = type.Name.IndexOf('`'); + if (genericPartIndex <= 0) + { + builder.Append(type.Name); + return; + } + + if (type.Assembly.ManifestModule.Name == "FSharp.Core.dll" + && FSharpTypeNames.TryGetValue(type.Name, out var builtInName)) + { + builder.Append(builtInName); + } + else + { + builder.Append(type.Name, 0, genericPartIndex); + } + + builder.Append('<'); + for (var i = offset; i < length; i++) + { + ProcessType(builder, genericArguments[i], options); + if (i + 1 == length) + { + continue; + } + + builder.Append(','); + if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter) + { + builder.Append(' '); + } + } + builder.Append('>'); + } + + private struct DisplayNameOptions + { + public DisplayNameOptions(bool fullName, bool includeGenericParameterNames) + { + FullName = fullName; + IncludeGenericParameterNames = includeGenericParameterNames; + } + + public bool FullName { get; } + + public bool IncludeGenericParameterNames { get; } + } + } +} diff --git a/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/XmlDocumentationExtension.cs b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/XmlDocumentationExtension.cs new file mode 100644 index 00000000..0fb34cdc --- /dev/null +++ b/src/Cropper.Blazor/Cropper.Blazor.Shared/Extensions/XmlDocumentationExtension.cs @@ -0,0 +1,436 @@ +using System.Reflection; +using System.Text.RegularExpressions; +using System.Xml; + +namespace Cropper.Blazor.Shared.Extensions +{ + public static class XmlDocumentationExtension + { + public static IEnumerable GetPropertyInfosWithAttribute(this Type type) + where AttributeType : Attribute + { + foreach (var propertyInfo in type.GetProperties( + BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.Public | + BindingFlags.NonPublic)) + { + if (propertyInfo.GetCustomAttributes(typeof(AttributeType), true).Length > 0) + { + yield return propertyInfo; + } + } + } + + /// Gets the file path of an assembly. + /// The assembly to get the file path of. + /// The file path of the assembly. + public static string GetDirectoryPath(this Assembly assembly) + { + var codeBase = "file://" + assembly.Location; + var uri = new UriBuilder(codeBase); + var path = Uri.UnescapeDataString(uri.Path); + return Path.GetDirectoryName(path); + } + + #region System.Type.ConvertToCSharpSource + + /// Converts a into a as it would appear in C# source code. + /// The to convert to a . + /// If the generic parameters are the generic types, whether they should be shown or not. + /// The as the would appear in C# source code. + public static string ConvertToCSharpSource(this Type type, bool showGenericParameters = false) + { + var genericParameters = new Queue(); + foreach (var x in type.GetGenericArguments()) + genericParameters.Enqueue(x); + return ConvertToCsharpSource(type); + + string ConvertToCsharpSource(Type type) + { + _ = type ?? throw new ArgumentNullException(nameof(type)); + var result = type.IsNested + ? ConvertToCsharpSource(type.DeclaringType) + "." + : ""; //: type.Namespace + "."; + result += Regex.Replace(type.Name, "`.*", string.Empty); + if (type.IsGenericType) + { + result += "<"; + var firstIteration = true; + foreach (var generic in type.GetGenericArguments()) + { + if (genericParameters.Count <= 0) + { + break; + } + var correctGeneric = genericParameters.Dequeue(); + result += (firstIteration ? string.Empty : ",") + + (correctGeneric.IsGenericParameter + ? showGenericParameters ? (firstIteration ? string.Empty : " ") + correctGeneric.Name : string.Empty + : (firstIteration ? string.Empty : " ") + correctGeneric.ConvertToCSharpSource()); + firstIteration = false; + } + result += ">"; + } + return result; + } + } + + #endregion + + #region XML Code Documentation + + public static HashSet LoadedAssemblies = new(); + public static Dictionary LoadedXmlDocumentation = new(); + + public static void LoadXmlDocumentation(Assembly assembly) + { + string xmlFilePath; + + if (LoadedAssemblies.Contains(assembly)) + { + return; + } + + var directoryPath = assembly.GetDirectoryPath(); + if (!string.IsNullOrEmpty(directoryPath)) + { + xmlFilePath = Path.Combine(directoryPath, assembly.GetName().Name + ".xml"); + } + else + { + xmlFilePath = assembly.GetName().Name + ".xml"; + } + + if (File.Exists(xmlFilePath)) + { + using var streamReader = new StreamReader(xmlFilePath); + LoadXmlDocumentation(streamReader); + } + // currently marking assembly as loaded even if the XML file was not found + // may want to adjust in future, but I think this is good for now + LoadedAssemblies.Add(assembly); + } + + /// Loads the XML code documentation into memory so it can be accessed by extension methods on reflection types. + /// The content of the XML code documentation. + public static void LoadXmlDocumentation(string xmlDocumentation) + { + using var stringReader = new StringReader(xmlDocumentation); + LoadXmlDocumentation(stringReader); + } + + /// Loads the XML code documentation into memory so it can be accessed by extension methods on reflection types. + /// The text reader to process in an XmlReader. + public static void LoadXmlDocumentation(TextReader textReader) + { + using var xmlReader = XmlReader.Create(textReader); + while (xmlReader.Read()) + { + if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "member") + { + var raw_name = xmlReader["name"]; + LoadedXmlDocumentation[raw_name] = xmlReader.ReadInnerXml(); + } + } + } + + /// Clears the currently loaded XML documentation. + public static void ClearXmlDocumentation() + { + LoadedAssemblies.Clear(); + LoadedXmlDocumentation.Clear(); + } + + /// Gets the XML documentation on a type. + /// The type to get the XML documentation of. + /// The XML documentation on the type. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this Type type) + { + LoadXmlDocumentation(type.Assembly); + var key = "T:" + XmlDocumentationKeyHelper(type.FullName, null); + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + /// Gets the XML documentation on a method. + /// The method to get the XML documentation of. + /// The XML documentation on the method. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this MethodInfo methodInfo) + { + LoadXmlDocumentation(methodInfo.DeclaringType.Assembly); + + var typeGenericMap = new Dictionary(); + var tempTypeGeneric = 0; + Array.ForEach(methodInfo.DeclaringType.GetGenericArguments(), x => typeGenericMap[x.Name] = tempTypeGeneric++); + + var methodGenericMap = new Dictionary(); + var tempMethodGeneric = 0; + Array.ForEach(methodInfo.GetGenericArguments(), x => methodGenericMap.Add(x.Name, tempMethodGeneric++)); + + var parameterInfos = methodInfo.GetParameters(); + + var memberTypePrefix = "M:"; + var declarationTypeString = GetXmlDocumentationFormattedString(methodInfo.DeclaringType, false, typeGenericMap, methodGenericMap); + var memberNameString = methodInfo.Name; + var methodGenericArgumentsString = + methodGenericMap.Count > 0 ? + "``" + methodGenericMap.Count : + string.Empty; + var parametersString = + parameterInfos.Length > 0 ? + "(" + string.Join(",", methodInfo.GetParameters().Select(x => GetXmlDocumentationFormattedString(x.ParameterType, true, typeGenericMap, methodGenericMap))).Replace("MudBlazor.Docs.Models.T", "`0") + ")" : + string.Empty; + + var key = + memberTypePrefix + + declarationTypeString + + "." + + memberNameString + + methodGenericArgumentsString + + parametersString; + + if (methodInfo.Name is "op_Implicit" or "op_Explicit") + { + key += "~" + GetXmlDocumentationFormattedString(methodInfo.ReturnType, true, typeGenericMap, methodGenericMap); + } + + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + /// Gets the XML documentation on a constructor. + /// The constructor to get the XML documentation of. + /// The XML documentation on the constructor. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this ConstructorInfo constructorInfo) + { + LoadXmlDocumentation(constructorInfo.DeclaringType.Assembly); + + var typeGenericMap = new Dictionary(); + var tempTypeGeneric = 0; + Array.ForEach(constructorInfo.DeclaringType.GetGenericArguments(), x => typeGenericMap[x.Name] = tempTypeGeneric++); + + // constructors don't support generic types so this will always be empty + var methodGenericMap = new Dictionary(); + + var parameterInfos = constructorInfo.GetParameters(); + + var memberTypePrefix = "M:"; + var declarationTypeString = GetXmlDocumentationFormattedString(constructorInfo.DeclaringType, false, typeGenericMap, methodGenericMap); + var memberNameString = "#ctor"; + var parametersString = + parameterInfos.Length > 0 ? + "(" + string.Join(",", constructorInfo.GetParameters().Select(x => GetXmlDocumentationFormattedString(x.ParameterType, true, typeGenericMap, methodGenericMap))) + ")" : + string.Empty; + + var key = + memberTypePrefix + + declarationTypeString + + "." + + memberNameString + + parametersString; + + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + public static string GetXmlDocumentationFormattedString( + Type type, + bool isMethodParameter, + Dictionary typeGenericMap, + Dictionary methodGenericMap) + { + if (type.IsGenericParameter) + { + return methodGenericMap.TryGetValue(type.Name, out var methodIndex) + ? "``" + methodIndex + : "`" + typeGenericMap[type.Name]; + } + else if (type.HasElementType) + { + var elementTypeString = GetXmlDocumentationFormattedString( + type.GetElementType(), + isMethodParameter, + typeGenericMap, + methodGenericMap); + + if (type.IsPointer) + { + return elementTypeString + "*"; + } + else if (type.IsArray) + { + var rank = type.GetArrayRank(); + var arrayDimensionsString = rank > 1 + ? "[" + string.Join(",", Enumerable.Repeat("0:", rank)) + "]" + : "[]"; + return elementTypeString + arrayDimensionsString; + } + else if (type.IsByRef) + { + return elementTypeString + "@"; + } + else + { + // Hopefully this will never hit. At the time of writing + // this code, type.HasElementType is only true if the type + // is a pointer, array, or by reference. + throw new Exception(nameof(GetXmlDocumentationFormattedString) + + " encountered an unhandled element type. " + + "Please submit this issue to the Towel GitHub repository. " + + "https://github.com/ZacharyPatten/Towel/issues/new/choose"); + } + } + else + { + var prefaceString = type.IsNested + ? GetXmlDocumentationFormattedString( + type.DeclaringType, + isMethodParameter, + typeGenericMap, + methodGenericMap) + "." + : type.Namespace + "."; + + string typeNameString = isMethodParameter + ? typeNameString = Regex.Replace(type.Name, @"`\d+", string.Empty) + : typeNameString = type.Name; + + var genericArgumentsString = type.IsGenericType && isMethodParameter + ? "{" + string.Join(",", + type.GetGenericArguments().Select(argument => + GetXmlDocumentationFormattedString( + argument, + isMethodParameter, + typeGenericMap, + methodGenericMap)) + ) + "}" + : string.Empty; + + return prefaceString + typeNameString + genericArgumentsString; + } + } + + /// Gets the XML documentation on a property. + /// The property to get the XML documentation of. + /// The XML documentation on the property. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this PropertyInfo propertyInfo) + { + LoadXmlDocumentation(propertyInfo.DeclaringType.Assembly); + var key = "P:" + XmlDocumentationKeyHelper(propertyInfo.DeclaringType.FullName, propertyInfo.Name); + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + /// Gets the XML documentation on a field. + /// The field to get the XML documentation of. + /// The XML documentation on the field. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this FieldInfo fieldInfo) + { + LoadXmlDocumentation(fieldInfo.DeclaringType.Assembly); + var key = "F:" + XmlDocumentationKeyHelper(fieldInfo.DeclaringType.FullName, fieldInfo.Name); + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + /// Gets the XML documentation on an event. + /// The event to get the XML documentation of. + /// The XML documentation on the event. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this EventInfo eventInfo) + { + LoadXmlDocumentation(eventInfo.DeclaringType.Assembly); + var key = "E:" + XmlDocumentationKeyHelper(eventInfo.DeclaringType.FullName, eventInfo.Name); + LoadedXmlDocumentation.TryGetValue(key, out var documentation); + return documentation; + } + + public static string XmlDocumentationKeyHelper(string typeFullNameString, string memberNameString) + { + var key = Regex.Replace(typeFullNameString, @"\[.*\]", string.Empty).Replace('+', '.'); + if (!(memberNameString is null)) + { + key += "." + memberNameString; + } + return key; + } + + /// Gets the XML documentation on a member. + /// The member to get the XML documentation of. + /// The XML documentation on the member. + /// The XML documentation must be loaded into memory for this function to work. + public static string GetDocumentation(this MemberInfo memberInfo) + { + if (memberInfo is FieldInfo fieldInfo) + { + return fieldInfo.GetDocumentation(); + } + else if (memberInfo is PropertyInfo propertyInfo) + { + return propertyInfo.GetDocumentation(); + } + else if (memberInfo is EventInfo eventInfo) + { + return eventInfo.GetDocumentation(); + } + else if (memberInfo is ConstructorInfo constructorInfo) + { + return constructorInfo.GetDocumentation(); + } + else if (memberInfo is MethodInfo methodInfo) + { + return methodInfo.GetDocumentation(); + } + else if (memberInfo is Type type) // + TypeInfo + { + return type.GetDocumentation(); + } + else if (memberInfo.MemberType.HasFlag(MemberTypes.Custom)) + { + // This represents a custom type that is not part of + // the standard .NET languages as far as I'm aware. + // This will never be supported so return null. + return null; + } + else + { + // Hopefully this will never hit. At the time of writing + // this code, I am only aware of the following Member types: + // FieldInfo, PropertyInfo, EventInfo, ConstructorInfo, + // MethodInfo, and Type. + throw new Exception(nameof(GetDocumentation) + + " encountered an unhandled type [" + memberInfo.GetType().FullName + "]. " + + "Please submit this issue to the Towel GitHub repository. " + + "https://github.com/ZacharyPatten/Towel/issues/new/choose"); + } + } + + /// Gets the XML documentation for a parameter. + /// The parameter to get the XML documentation for. + /// The XML documentation of the parameter. + public static string GetDocumentation(this ParameterInfo parameterInfo) + { + var memberDocumentation = parameterInfo.Member.GetDocumentation(); + if (!(memberDocumentation is null)) + { + var regexPattern = + Regex.Escape(@"") + + ".*?" + + Regex.Escape(@""); + + var match = Regex.Match(memberDocumentation, regexPattern); + if (match.Success) + { + return match.Value; + } + } + return null; + } + + #endregion + } +} diff --git a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Cropper.Blazor.UnitTests.csproj b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Cropper.Blazor.UnitTests.csproj index 1fc65ae7..de4c8218 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Cropper.Blazor.UnitTests.csproj +++ b/src/Cropper.Blazor/Cropper.Blazor.UnitTests/Cropper.Blazor.UnitTests.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Cropper.Blazor/Cropper.Blazor.sln b/src/Cropper.Blazor/Cropper.Blazor.sln index 027871bb..7138c4d7 100644 --- a/src/Cropper.Blazor/Cropper.Blazor.sln +++ b/src/Cropper.Blazor/Cropper.Blazor.sln @@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Library", "Library", "{1CC7 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demo", "Demo", "{AA493A4F-99D7-4C5D-9355-28E5159F9DB6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cropper.Blazor.Shared", "Cropper.Blazor.Shared\Cropper.Blazor.Shared.csproj", "{B5ECE039-10C3-4B89-A7C9-5685464930C9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -72,6 +74,10 @@ Global {AA2CDA13-6D81-4A3B-871D-2C5B26926D9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA2CDA13-6D81-4A3B-871D-2C5B26926D9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA2CDA13-6D81-4A3B-871D-2C5B26926D9B}.Release|Any CPU.Build.0 = Release|Any CPU + {B5ECE039-10C3-4B89-A7C9-5685464930C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5ECE039-10C3-4B89-A7C9-5685464930C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5ECE039-10C3-4B89-A7C9-5685464930C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5ECE039-10C3-4B89-A7C9-5685464930C9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -85,6 +91,7 @@ Global {42196427-7CE7-4B56-BAA3-717B1CB6C245} = {624968C7-925A-4896-8405-4174FD48B9BC} {378A2956-8E21-434D-9480-9D4E0732098B} = {624968C7-925A-4896-8405-4174FD48B9BC} {AA2CDA13-6D81-4A3B-871D-2C5B26926D9B} = {AA493A4F-99D7-4C5D-9355-28E5159F9DB6} + {B5ECE039-10C3-4B89-A7C9-5685464930C9} = {AA493A4F-99D7-4C5D-9355-28E5159F9DB6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C2EE0C77-7D1D-41D9-B923-314095849A25} diff --git a/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj b/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj index 617ed77f..881db38f 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj +++ b/src/Cropper.Blazor/Cropper.Blazor/Cropper.Blazor.csproj @@ -14,13 +14,13 @@ - 1.2.4 + 1.2.5 LICENSE NuGet.png Cropper.Blazor Max56132, ColdForeign Copyright 2022-present Cropper.Blazor - Cropper.Blazor is a component element that wraps around Cropper.js + Cropper.Blazor is a component that wraps around Cropper.js Blazor, Cropper.Blazor, Cropper.js, Blazor Components, Blazor Library, Blazor Cropper, Cropper, Image, Crop, Resize, image-cropper, crop-image, csharp, blazor-cropper https://CropperBlazor.github.io https://github.com/CropperBlazor/Cropper.Blazor diff --git a/src/Cropper.Blazor/Cropper.Blazor/Models/GetCroppedCanvasOptions.cs b/src/Cropper.Blazor/Cropper.Blazor/Models/GetCroppedCanvasOptions.cs index 5dff5ce2..8cdd20e8 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/Models/GetCroppedCanvasOptions.cs +++ b/src/Cropper.Blazor/Cropper.Blazor/Models/GetCroppedCanvasOptions.cs @@ -52,5 +52,10 @@ public class GetCroppedCanvasOptions /// [EnumDataType(typeof(ImageSmoothingQuality))] public string? ImageSmoothingQuality { get; set; } + + /// + /// Set true to use rounded values (the cropped area position and size data), the default value is false. + /// + public bool? Rounded { get; set; } } } \ No newline at end of file diff --git a/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropper.min.js b/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropper.min.js index 2eb34373..6236294a 100644 --- a/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropper.min.js +++ b/src/Cropper.Blazor/Cropper.Blazor/wwwroot/cropper.min.js @@ -1,10 +1,10 @@ /*! - * Cropper.js v1.5.13 + * Cropper.js v1.6.0 * https://fengyuanchen.github.io/cropperjs * * Copyright 2015-present Chen Fengyuan * Released under the MIT license * - * Date: 2022-11-20T05:30:46.114Z + * Date: 2023-08-26T08:14:27.943Z */ -!function (t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).Cropper = e() }(this, function () { "use strict"; function C(e, t) { var i, a = Object.keys(e); return Object.getOwnPropertySymbols && (i = Object.getOwnPropertySymbols(e), t && (i = i.filter(function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable })), a.push.apply(a, i)), a } function S(a) { for (var t = 1; t < arguments.length; t++) { var n = null != arguments[t] ? arguments[t] : {}; t % 2 ? C(Object(n), !0).forEach(function (t) { var e, i; e = a, i = n[t = t], t in e ? Object.defineProperty(e, t, { value: i, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = i }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(a, Object.getOwnPropertyDescriptors(n)) : C(Object(n)).forEach(function (t) { Object.defineProperty(a, t, Object.getOwnPropertyDescriptor(n, t)) }) } return a } function D(t) { return (D = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t })(t) } function A(t, e) { for (var i = 0; i < e.length; i++) { var a = e[i]; a.enumerable = a.enumerable || !1, a.configurable = !0, "value" in a && (a.writable = !0), Object.defineProperty(t, a.key, a) } } function j(t) { return function (t) { if (Array.isArray(t)) return a(t) }(t) || function (t) { if ("undefined" != typeof Symbol && null != t[Symbol.iterator] || null != t["@@iterator"]) return Array.from(t) }(t) || function (t, e) { var i; if (t) return "string" == typeof t ? a(t, e) : "Map" === (i = "Object" === (i = Object.prototype.toString.call(t).slice(8, -1)) && t.constructor ? t.constructor.name : i) || "Set" === i ? Array.from(t) : "Arguments" === i || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i) ? a(t, e) : void 0 }(t) || function () { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.") }() } function a(t, e) { (null == e || e > t.length) && (e = t.length); for (var i = 0, a = new Array(e); i < e; i++)a[i] = t[i]; return a } var t = "undefined" != typeof window && void 0 !== window.document, h = t ? window : {}, e = !(!t || !h.document.documentElement) && "ontouchstart" in h.document.documentElement, i = t && "PointerEvent" in h, c = "cropper", P = "all", I = "crop", U = "move", q = "zoom", B = "e", k = "w", O = "s", T = "n", E = "ne", W = "nw", H = "se", N = "sw", $ = "".concat(c, "-crop"), Q = "".concat(c, "-disabled"), L = "".concat(c, "-hidden"), K = "".concat(c, "-hide"), Z = "".concat(c, "-invisible"), n = "".concat(c, "-modal"), G = "".concat(c, "-move"), d = "".concat(c, "Action"), m = "".concat(c, "Preview"), V = "crop", F = "move", J = "none", _ = "crop", tt = "cropend", et = "cropmove", it = "cropstart", at = "dblclick", nt = i ? "pointerdown" : e ? "touchstart" : "mousedown", ot = i ? "pointermove" : e ? "touchmove" : "mousemove", ht = i ? "pointerup pointercancel" : e ? "touchend touchcancel" : "mouseup", rt = "zoom", st = "image/jpeg", ct = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/, dt = /^data:/, lt = /^data:image\/jpeg;base64,/, pt = /^img|canvas$/i, mt = { viewMode: 0, dragMode: V, initialAspectRatio: NaN, aspectRatio: NaN, data: null, preview: "", responsive: !0, restore: !0, checkCrossOrigin: !0, checkOrientation: !0, modal: !0, guides: !0, center: !0, highlight: !0, background: !0, autoCrop: !0, autoCropArea: .8, movable: !0, rotatable: !0, scalable: !0, zoomable: !0, zoomOnTouch: !0, zoomOnWheel: !0, wheelZoomRatio: .1, cropBoxMovable: !0, cropBoxResizable: !0, toggleDragModeOnDblclick: !0, minCanvasWidth: 0, minCanvasHeight: 0, minCropBoxWidth: 0, minCropBoxHeight: 0, minContainerWidth: 200, minContainerHeight: 100, ready: null, cropstart: null, cropmove: null, cropend: null, crop: null, zoom: null }, ut = Number.isNaN || h.isNaN; function p(t) { return "number" == typeof t && !ut(t) } function gt(t) { return 0 < t && t < 1 / 0 } function ft(t) { return void 0 === t } function o(t) { return "object" === D(t) && null !== t } var vt = Object.prototype.hasOwnProperty; function u(t) { if (!o(t)) return !1; try { var e = t.constructor, i = e.prototype; return e && i && vt.call(i, "isPrototypeOf") } catch (t) { return !1 } } function l(t) { return "function" == typeof t } var wt = Array.prototype.slice; function bt(t) { return Array.from ? Array.from(t) : wt.call(t) } function z(i, a) { return i && l(a) && (Array.isArray(i) || p(i.length) ? bt(i).forEach(function (t, e) { a.call(i, t, e, i) }) : o(i) && Object.keys(i).forEach(function (t) { a.call(i, i[t], t, i) })), i } var g = Object.assign || function (i) { for (var t = arguments.length, e = new Array(1 < t ? t - 1 : 0), a = 1; a < t; a++)e[a - 1] = arguments[a]; return o(i) && 0 < e.length && e.forEach(function (e) { o(e) && Object.keys(e).forEach(function (t) { i[t] = e[t] }) }), i }, yt = /\.\d*(?:0|9){12}\d*$/; function Y(t, e) { e = 1 < arguments.length && void 0 !== e ? e : 1e11; return yt.test(t) ? Math.round(t * e) / e : t } var xt = /^width|height|left|top|marginLeft|marginTop$/; function f(t, e) { var i = t.style; z(e, function (t, e) { xt.test(e) && p(t) && (t = "".concat(t, "px")), i[e] = t }) } function v(t, e) { var i; e && (p(t.length) ? z(t, function (t) { v(t, e) }) : t.classList ? t.classList.add(e) : (i = t.className.trim()) ? i.indexOf(e) < 0 && (t.className = "".concat(i, " ").concat(e)) : t.className = e) } function X(t, e) { e && (p(t.length) ? z(t, function (t) { X(t, e) }) : t.classList ? t.classList.remove(e) : 0 <= t.className.indexOf(e) && (t.className = t.className.replace(e, ""))) } function r(t, e, i) { e && (p(t.length) ? z(t, function (t) { r(t, e, i) }) : (i ? v : X)(t, e)) } var Mt = /([a-z\d])([A-Z])/g; function Ct(t) { return t.replace(Mt, "$1-$2").toLowerCase() } function Dt(t, e) { return o(t[e]) ? t[e] : t.dataset ? t.dataset[e] : t.getAttribute("data-".concat(Ct(e))) } function w(t, e, i) { o(i) ? t[e] = i : t.dataset ? t.dataset[e] = i : t.setAttribute("data-".concat(Ct(e)), i) } var Bt, kt, Ot = /\s\s*/, Tt = (kt = !1, t && (Bt = !1, i = function () { }, e = Object.defineProperty({}, "once", { get: function () { return kt = !0, Bt }, set: function (t) { Bt = t } }), h.addEventListener("test", i, e), h.removeEventListener("test", i, e)), kt); function s(i, t, a, e) { var n = 3 < arguments.length && void 0 !== e ? e : {}, o = a; t.trim().split(Ot).forEach(function (t) { var e; Tt || (e = i.listeners) && e[t] && e[t][a] && (o = e[t][a], delete e[t][a], 0 === Object.keys(e[t]).length && delete e[t], 0 === Object.keys(e).length) && delete i.listeners, i.removeEventListener(t, o, n) }) } function b(o, t, h, e) { var r = 3 < arguments.length && void 0 !== e ? e : {}, s = h; t.trim().split(Ot).forEach(function (a) { var t, n; r.once && !Tt && (t = o.listeners, s = function () { delete n[a][h], o.removeEventListener(a, s, r); for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++)e[i] = arguments[i]; h.apply(o, e) }, (n = void 0 === t ? {} : t)[a] || (n[a] = {}), n[a][h] && o.removeEventListener(a, n[a][h], r), n[a][h] = s, o.listeners = n), o.addEventListener(a, s, r) }) } function y(t, e, i) { var a; return l(Event) && l(CustomEvent) ? a = new CustomEvent(e, { detail: i, bubbles: !0, cancelable: !0 }) : (a = document.createEvent("CustomEvent")).initCustomEvent(e, !0, !0, i), t.dispatchEvent(a) } function Et(t) { t = t.getBoundingClientRect(); return { left: t.left + (window.pageXOffset - document.documentElement.clientLeft), top: t.top + (window.pageYOffset - document.documentElement.clientTop) } } var Wt = h.location, Ht = /^(\w+:)\/\/([^:/?#]*):?(\d*)/i; function Nt(t) { t = t.match(Ht); return null !== t && (t[1] !== Wt.protocol || t[2] !== Wt.hostname || t[3] !== Wt.port) } function Lt(t) { var e = "timestamp=".concat((new Date).getTime()); return t + (-1 === t.indexOf("?") ? "?" : "&") + e } function x(t) { var e = t.rotate, i = t.scaleX, a = t.scaleY, n = t.translateX, t = t.translateY, o = [], n = (p(n) && 0 !== n && o.push("translateX(".concat(n, "px)")), p(t) && 0 !== t && o.push("translateY(".concat(t, "px)")), p(e) && 0 !== e && o.push("rotate(".concat(e, "deg)")), p(i) && 1 !== i && o.push("scaleX(".concat(i, ")")), p(a) && 1 !== a && o.push("scaleY(".concat(a, ")")), o.length ? o.join(" ") : "none"); return { WebkitTransform: n, msTransform: n, transform: n } } function M(t, e) { var i = t.pageX, t = t.pageY, a = { endX: i, endY: t }; return e ? a : S({ startX: i, startY: t }, a) } function R(t, e) { var i, a = t.aspectRatio, n = t.height, t = t.width, e = 1 < arguments.length && void 0 !== e ? e : "contain", o = gt(t), h = gt(n); return o && h ? (i = n * a, "contain" === e && t < i || "cover" === e && i < t ? n = t / a : t = n * a) : o ? n = t / a : h && (t = n * a), { width: t, height: n } } var zt = String.fromCharCode; var Yt = /^data:.*,/; function Xt(t) { var e, i, a, n, o, h, r, s = new DataView(t); try { if (255 === s.getUint8(0) && 216 === s.getUint8(1)) for (var c = s.byteLength, d = 2; d + 1 < c;) { if (255 === s.getUint8(d) && 225 === s.getUint8(d + 1)) { i = d; break } d += 1 } if (a = i && (n = i + 10, "Exif" === function (t, e, i) { var a = ""; i += e; for (var n = e; n < i; n += 1)a += zt(t.getUint8(n)); return a }(s, i + 4, 4)) && ((r = 18761 === (o = s.getUint16(n))) || 19789 === o) && 42 === s.getUint16(n + 2, r) && 8 <= (h = s.getUint32(n + 4, r)) ? n + h : a) for (var l, p = s.getUint16(a, r), m = 0; m < p; m += 1)if (l = a + 12 * m + 2, 274 === s.getUint16(l, r)) { l += 8, e = s.getUint16(l, r), s.setUint16(l, 1, r); break } } catch (t) { e = 1 } return e } var t = { render: function () { this.initContainer(), this.initCanvas(), this.initCropBox(), this.renderCanvas(), this.cropped && this.renderCropBox() }, initContainer: function () { var t = this.element, e = this.options, i = this.container, a = this.cropper, n = Number(e.minContainerWidth), e = Number(e.minContainerHeight), n = (v(a, L), X(t, L), { width: Math.max(i.offsetWidth, 0 <= n ? n : 200), height: Math.max(i.offsetHeight, 0 <= e ? e : 100) }); f(a, { width: (this.containerData = n).width, height: n.height }), v(t, L), X(a, L) }, initCanvas: function () { var t = this.containerData, e = this.imageData, i = this.options.viewMode, a = Math.abs(e.rotate) % 180 == 90, n = a ? e.naturalHeight : e.naturalWidth, a = a ? e.naturalWidth : e.naturalHeight, e = n / a, o = t.width, h = t.height, e = (t.height * e > t.width ? 3 === i ? o = t.height * e : h = t.width / e : 3 === i ? h = t.width / e : o = t.height * e, { aspectRatio: e, naturalWidth: n, naturalHeight: a, width: o, height: h }); this.canvasData = e, this.limited = 1 === i || 2 === i, this.limitCanvas(!0, !0), e.width = Math.min(Math.max(e.width, e.minWidth), e.maxWidth), e.height = Math.min(Math.max(e.height, e.minHeight), e.maxHeight), e.left = (t.width - e.width) / 2, e.top = (t.height - e.height) / 2, e.oldLeft = e.left, e.oldTop = e.top, this.initialCanvasData = g({}, e) }, limitCanvas: function (t, e) { var i = this.options, a = this.containerData, n = this.canvasData, o = this.cropBoxData, h = i.viewMode, r = n.aspectRatio, s = this.cropped && o; t && (t = Number(i.minCanvasWidth) || 0, i = Number(i.minCanvasHeight) || 0, 1 < h ? (t = Math.max(t, a.width), i = Math.max(i, a.height), 3 === h && (t < i * r ? t = i * r : i = t / r)) : 0 < h && (t ? t = Math.max(t, s ? o.width : 0) : i ? i = Math.max(i, s ? o.height : 0) : s && ((t = o.width) < (i = o.height) * r ? t = i * r : i = t / r)), t = (r = R({ aspectRatio: r, width: t, height: i })).width, i = r.height, n.minWidth = t, n.minHeight = i, n.maxWidth = 1 / 0, n.maxHeight = 1 / 0), e && ((s ? 0 : 1) < h ? (r = a.width - n.width, t = a.height - n.height, n.minLeft = Math.min(0, r), n.minTop = Math.min(0, t), n.maxLeft = Math.max(0, r), n.maxTop = Math.max(0, t), s && this.limited && (n.minLeft = Math.min(o.left, o.left + (o.width - n.width)), n.minTop = Math.min(o.top, o.top + (o.height - n.height)), n.maxLeft = o.left, n.maxTop = o.top, 2 === h) && (n.width >= a.width && (n.minLeft = Math.min(0, r), n.maxLeft = Math.max(0, r)), n.height >= a.height) && (n.minTop = Math.min(0, t), n.maxTop = Math.max(0, t))) : (n.minLeft = -n.width, n.minTop = -n.height, n.maxLeft = a.width, n.maxTop = a.height)) }, renderCanvas: function (t, e) { var i, a, n, o, h = this.canvasData, r = this.imageData; e && (e = { width: r.naturalWidth * Math.abs(r.scaleX || 1), height: r.naturalHeight * Math.abs(r.scaleY || 1), degree: r.rotate || 0 }, r = e.width, o = e.height, e = e.degree, i = 90 == (e = Math.abs(e) % 180) ? { width: o, height: r } : (a = e % 90 * Math.PI / 180, i = Math.sin(a), n = r * (a = Math.cos(a)) + o * i, r = r * i + o * a, 90 < e ? { width: r, height: n } : { width: n, height: r }), a = h.width * ((o = i.width) / h.naturalWidth), n = h.height * ((e = i.height) / h.naturalHeight), h.left -= (a - h.width) / 2, h.top -= (n - h.height) / 2, h.width = a, h.height = n, h.aspectRatio = o / e, h.naturalWidth = o, h.naturalHeight = e, this.limitCanvas(!0, !1)), (h.width > h.maxWidth || h.width < h.minWidth) && (h.left = h.oldLeft), (h.height > h.maxHeight || h.height < h.minHeight) && (h.top = h.oldTop), h.width = Math.min(Math.max(h.width, h.minWidth), h.maxWidth), h.height = Math.min(Math.max(h.height, h.minHeight), h.maxHeight), this.limitCanvas(!1, !0), h.left = Math.min(Math.max(h.left, h.minLeft), h.maxLeft), h.top = Math.min(Math.max(h.top, h.minTop), h.maxTop), h.oldLeft = h.left, h.oldTop = h.top, f(this.canvas, g({ width: h.width, height: h.height }, x({ translateX: h.left, translateY: h.top }))), this.renderImage(t), this.cropped && this.limited && this.limitCropBox(!0, !0) }, renderImage: function (t) { var e = this.canvasData, i = this.imageData, a = i.naturalWidth * (e.width / e.naturalWidth), n = i.naturalHeight * (e.height / e.naturalHeight); g(i, { width: a, height: n, left: (e.width - a) / 2, top: (e.height - n) / 2 }), f(this.image, g({ width: i.width, height: i.height }, x(g({ translateX: i.left, translateY: i.top }, i)))), t && this.output() }, initCropBox: function () { var t = this.options, e = this.canvasData, i = t.aspectRatio || t.initialAspectRatio, t = Number(t.autoCropArea) || .8, a = { width: e.width, height: e.height }; i && (e.height * i > e.width ? a.height = a.width / i : a.width = a.height * i), this.cropBoxData = a, this.limitCropBox(!0, !0), a.width = Math.min(Math.max(a.width, a.minWidth), a.maxWidth), a.height = Math.min(Math.max(a.height, a.minHeight), a.maxHeight), a.width = Math.max(a.minWidth, a.width * t), a.height = Math.max(a.minHeight, a.height * t), a.left = e.left + (e.width - a.width) / 2, a.top = e.top + (e.height - a.height) / 2, a.oldLeft = a.left, a.oldTop = a.top, this.initialCropBoxData = g({}, a) }, limitCropBox: function (t, e) { var i, a, n = this.options, o = this.containerData, h = this.canvasData, r = this.cropBoxData, s = this.limited, c = n.aspectRatio; t && (t = Number(n.minCropBoxWidth) || 0, n = Number(n.minCropBoxHeight) || 0, i = s ? Math.min(o.width, h.width, h.width + h.left, o.width - h.left) : o.width, a = s ? Math.min(o.height, h.height, h.height + h.top, o.height - h.top) : o.height, t = Math.min(t, o.width), n = Math.min(n, o.height), c && (t && n ? t < n * c ? n = t / c : t = n * c : t ? n = t / c : n && (t = n * c), i < a * c ? a = i / c : i = a * c), r.minWidth = Math.min(t, i), r.minHeight = Math.min(n, a), r.maxWidth = i, r.maxHeight = a), e && (s ? (r.minLeft = Math.max(0, h.left), r.minTop = Math.max(0, h.top), r.maxLeft = Math.min(o.width, h.left + h.width) - r.width, r.maxTop = Math.min(o.height, h.top + h.height) - r.height) : (r.minLeft = 0, r.minTop = 0, r.maxLeft = o.width - r.width, r.maxTop = o.height - r.height)) }, renderCropBox: function () { var t = this.options, e = this.containerData, i = this.cropBoxData; (i.width > i.maxWidth || i.width < i.minWidth) && (i.left = i.oldLeft), (i.height > i.maxHeight || i.height < i.minHeight) && (i.top = i.oldTop), i.width = Math.min(Math.max(i.width, i.minWidth), i.maxWidth), i.height = Math.min(Math.max(i.height, i.minHeight), i.maxHeight), this.limitCropBox(!1, !0), i.left = Math.min(Math.max(i.left, i.minLeft), i.maxLeft), i.top = Math.min(Math.max(i.top, i.minTop), i.maxTop), i.oldLeft = i.left, i.oldTop = i.top, t.movable && t.cropBoxMovable && w(this.face, d, i.width >= e.width && i.height >= e.height ? U : P), f(this.cropBox, g({ width: i.width, height: i.height }, x({ translateX: i.left, translateY: i.top }))), this.cropped && this.limited && this.limitCanvas(!0, !0), this.disabled || this.output() }, output: function () { this.preview(), y(this.element, _, this.getData()) } }, i = { initPreview: function () { var t = this.element, i = this.crossOrigin, e = this.options.preview, a = i ? this.crossOriginUrl : this.url, n = t.alt || "The image to preview", o = document.createElement("img"); i && (o.crossOrigin = i), o.src = a, o.alt = n, this.viewBox.appendChild(o), this.viewBoxImage = o, e && ("string" == typeof (o = e) ? o = t.ownerDocument.querySelectorAll(e) : e.querySelector && (o = [e]), z(this.previews = o, function (t) { var e = document.createElement("img"); w(t, m, { width: t.offsetWidth, height: t.offsetHeight, html: t.innerHTML }), i && (e.crossOrigin = i), e.src = a, e.alt = n, e.style.cssText = 'display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;"', t.innerHTML = "", t.appendChild(e) })) }, resetPreview: function () { z(this.previews, function (e) { var i = Dt(e, m), i = (f(e, { width: i.width, height: i.height }), e.innerHTML = i.html, e), e = m; if (o(i[e])) try { delete i[e] } catch (t) { i[e] = void 0 } else if (i.dataset) try { delete i.dataset[e] } catch (t) { i.dataset[e] = void 0 } else i.removeAttribute("data-".concat(Ct(e))) }) }, preview: function () { var h = this.imageData, t = this.canvasData, e = this.cropBoxData, r = e.width, s = e.height, c = h.width, d = h.height, l = e.left - t.left - h.left, p = e.top - t.top - h.top; this.cropped && !this.disabled && (f(this.viewBoxImage, g({ width: c, height: d }, x(g({ translateX: -l, translateY: -p }, h)))), z(this.previews, function (t) { var e = Dt(t, m), i = e.width, e = e.height, a = i, n = e, o = 1; r && (n = s * (o = i / r)), s && e < n && (a = r * (o = e / s), n = e), f(t, { width: a, height: n }), f(t.getElementsByTagName("img")[0], g({ width: c * o, height: d * o }, x(g({ translateX: -l * o, translateY: -p * o }, h)))) })) } }, e = { bind: function () { var t = this.element, e = this.options, i = this.cropper; l(e.cropstart) && b(t, it, e.cropstart), l(e.cropmove) && b(t, et, e.cropmove), l(e.cropend) && b(t, tt, e.cropend), l(e.crop) && b(t, _, e.crop), l(e.zoom) && b(t, rt, e.zoom), b(i, nt, this.onCropStart = this.cropStart.bind(this)), e.zoomable && e.zoomOnWheel && b(i, "wheel", this.onWheel = this.wheel.bind(this), { passive: !1, capture: !0 }), e.toggleDragModeOnDblclick && b(i, at, this.onDblclick = this.dblclick.bind(this)), b(t.ownerDocument, ot, this.onCropMove = this.cropMove.bind(this)), b(t.ownerDocument, ht, this.onCropEnd = this.cropEnd.bind(this)), e.responsive && b(window, "resize", this.onResize = this.resize.bind(this)) }, unbind: function () { var t = this.element, e = this.options, i = this.cropper; l(e.cropstart) && s(t, it, e.cropstart), l(e.cropmove) && s(t, et, e.cropmove), l(e.cropend) && s(t, tt, e.cropend), l(e.crop) && s(t, _, e.crop), l(e.zoom) && s(t, rt, e.zoom), s(i, nt, this.onCropStart), e.zoomable && e.zoomOnWheel && s(i, "wheel", this.onWheel, { passive: !1, capture: !0 }), e.toggleDragModeOnDblclick && s(i, at, this.onDblclick), s(t.ownerDocument, ot, this.onCropMove), s(t.ownerDocument, ht, this.onCropEnd), e.responsive && s(window, "resize", this.onResize) } }, Rt = { resize: function () { var t, e, i, a, n, o, h; this.disabled || (t = this.options, a = this.container, e = this.containerData, i = a.offsetWidth / e.width, a = a.offsetHeight / e.height, 1 != (n = Math.abs(i - 1) > Math.abs(a - 1) ? i : a) && (t.restore && (o = this.getCanvasData(), h = this.getCropBoxData()), this.render(), t.restore) && (this.setCanvasData(z(o, function (t, e) { o[e] = t * n })), this.setCropBoxData(z(h, function (t, e) { h[e] = t * n })))) }, dblclick: function () { var t, e; this.disabled || this.options.dragMode === J || this.setDragMode((t = this.dragBox, e = $, (t.classList ? t.classList.contains(e) : -1 < t.className.indexOf(e)) ? F : V)) }, wheel: function (t) { var e = this, i = Number(this.options.wheelZoomRatio) || .1, a = 1; this.disabled || (t.preventDefault(), this.wheeling) || (this.wheeling = !0, setTimeout(function () { e.wheeling = !1 }, 50), t.deltaY ? a = 0 < t.deltaY ? 1 : -1 : t.wheelDelta ? a = -t.wheelDelta / 120 : t.detail && (a = 0 < t.detail ? 1 : -1), this.zoom(-a * i, t)) }, cropStart: function (t) { var e, i = t.buttons, a = t.button; this.disabled || ("mousedown" === t.type || "pointerdown" === t.type && "mouse" === t.pointerType) && (p(i) && 1 !== i || p(a) && 0 !== a || t.ctrlKey) || (i = this.options, e = this.pointers, t.changedTouches ? z(t.changedTouches, function (t) { e[t.identifier] = M(t) }) : e[t.pointerId || 0] = M(t), a = 1 < Object.keys(e).length && i.zoomable && i.zoomOnTouch ? q : Dt(t.target, d), ct.test(a) && !1 !== y(this.element, it, { originalEvent: t, action: a }) && (t.preventDefault(), this.action = a, this.cropping = !1, a === I) && (this.cropping = !0, v(this.dragBox, n))) }, cropMove: function (t) { var e, i = this.action; !this.disabled && i && (e = this.pointers, t.preventDefault(), !1 !== y(this.element, et, { originalEvent: t, action: i })) && (t.changedTouches ? z(t.changedTouches, function (t) { g(e[t.identifier] || {}, M(t, !0)) }) : g(e[t.pointerId || 0] || {}, M(t, !0)), this.change(t)) }, cropEnd: function (t) { var e, i; this.disabled || (e = this.action, i = this.pointers, t.changedTouches ? z(t.changedTouches, function (t) { delete i[t.identifier] }) : delete i[t.pointerId || 0], e && (t.preventDefault(), Object.keys(i).length || (this.action = ""), this.cropping && (this.cropping = !1, r(this.dragBox, n, this.cropped && this.options.modal)), y(this.element, tt, { originalEvent: t, action: e }))) } }, St = { change: function (t) { function e(t) { switch (t) { case B: f + D.x > y && (D.x = y - f); break; case k: p + D.x < w && (D.x = w - p); break; case T: m + D.y < b && (D.y = b - m); break; case O: v + D.y > x && (D.y = x - v) } } var i, a, o, n = this.options, h = this.canvasData, r = this.containerData, s = this.cropBoxData, c = this.pointers, d = this.action, l = n.aspectRatio, p = s.left, m = s.top, u = s.width, g = s.height, f = p + u, v = m + g, w = 0, b = 0, y = r.width, x = r.height, M = !0, C = (!l && t.shiftKey && (l = u && g ? u / g : 1), this.limited && (w = s.minLeft, b = s.minTop, y = w + Math.min(r.width, h.width, h.left + h.width), x = b + Math.min(r.height, h.height, h.top + h.height)), c[Object.keys(c)[0]]), D = { x: C.endX - C.startX, y: C.endY - C.startY }; switch (d) { case P: p += D.x, m += D.y; break; case B: 0 <= D.x && (y <= f || l && (m <= b || x <= v)) ? M = !1 : (e(B), (u += D.x) < 0 && (d = k, p -= u = -u), l && (m += (s.height - (g = u / l)) / 2)); break; case T: D.y <= 0 && (m <= b || l && (p <= w || y <= f)) ? M = !1 : (e(T), g -= D.y, m += D.y, g < 0 && (d = O, m -= g = -g), l && (p += (s.width - (u = g * l)) / 2)); break; case k: D.x <= 0 && (p <= w || l && (m <= b || x <= v)) ? M = !1 : (e(k), u -= D.x, p += D.x, u < 0 && (d = B, p -= u = -u), l && (m += (s.height - (g = u / l)) / 2)); break; case O: 0 <= D.y && (x <= v || l && (p <= w || y <= f)) ? M = !1 : (e(O), (g += D.y) < 0 && (d = T, m -= g = -g), l && (p += (s.width - (u = g * l)) / 2)); break; case E: if (l) { if (D.y <= 0 && (m <= b || y <= f)) { M = !1; break } e(T), g -= D.y, m += D.y, u = g * l } else e(T), e(B), !(0 <= D.x) || f < y ? u += D.x : D.y <= 0 && m <= b && (M = !1), (!(D.y <= 0) || b < m) && (g -= D.y, m += D.y); u < 0 && g < 0 ? (d = N, m -= g = -g, p -= u = -u) : u < 0 ? (d = W, p -= u = -u) : g < 0 && (d = H, m -= g = -g); break; case W: if (l) { if (D.y <= 0 && (m <= b || p <= w)) { M = !1; break } e(T), g -= D.y, m += D.y, p += s.width - (u = g * l) } else e(T), e(k), !(D.x <= 0) || w < p ? (u -= D.x, p += D.x) : D.y <= 0 && m <= b && (M = !1), (!(D.y <= 0) || b < m) && (g -= D.y, m += D.y); u < 0 && g < 0 ? (d = H, m -= g = -g, p -= u = -u) : u < 0 ? (d = E, p -= u = -u) : g < 0 && (d = N, m -= g = -g); break; case N: if (l) { if (D.x <= 0 && (p <= w || x <= v)) { M = !1; break } e(k), u -= D.x, p += D.x, g = u / l } else e(O), e(k), !(D.x <= 0) || w < p ? (u -= D.x, p += D.x) : 0 <= D.y && x <= v && (M = !1), (!(0 <= D.y) || v < x) && (g += D.y); u < 0 && g < 0 ? (d = E, m -= g = -g, p -= u = -u) : u < 0 ? (d = H, p -= u = -u) : g < 0 && (d = W, m -= g = -g); break; case H: if (l) { if (0 <= D.x && (y <= f || x <= v)) { M = !1; break } e(B), g = (u += D.x) / l } else e(O), e(B), !(0 <= D.x) || f < y ? u += D.x : 0 <= D.y && x <= v && (M = !1), (!(0 <= D.y) || v < x) && (g += D.y); u < 0 && g < 0 ? (d = W, m -= g = -g, p -= u = -u) : u < 0 ? (d = N, p -= u = -u) : g < 0 && (d = E, m -= g = -g); break; case U: this.move(D.x, D.y), M = !1; break; case q: this.zoom((a = S({}, i = c), o = 0, z(i, function (n, t) { delete a[t], z(a, function (t) { var e = Math.abs(n.startX - t.startX), i = Math.abs(n.startY - t.startY), a = Math.abs(n.endX - t.endX), t = Math.abs(n.endY - t.endY), e = Math.sqrt(e * e + i * i), i = (Math.sqrt(a * a + t * t) - e) / e; Math.abs(i) > Math.abs(o) && (o = i) }) }), o), t), M = !1; break; case I: D.x && D.y ? (i = Et(this.cropper), p = C.startX - i.left, m = C.startY - i.top, u = s.minWidth, g = s.minHeight, 0 < D.x ? d = 0 < D.y ? H : E : D.x < 0 && (p -= u, d = 0 < D.y ? N : W), D.y < 0 && (m -= g), this.cropped || (X(this.cropBox, L), this.cropped = !0, this.limited && this.limitCropBox(!0, !0))) : M = !1 }M && (s.width = u, s.height = g, s.left = p, s.top = m, this.action = d, this.renderCropBox()), z(c, function (t) { t.startX = t.endX, t.startY = t.endY }) } }, At = { crop: function () { return !this.ready || this.cropped || this.disabled || (this.cropped = !0, this.limitCropBox(!0, !0), this.options.modal && v(this.dragBox, n), X(this.cropBox, L), this.setCropBoxData(this.initialCropBoxData)), this }, reset: function () { return this.ready && !this.disabled && (this.imageData = g({}, this.initialImageData), this.canvasData = g({}, this.initialCanvasData), this.cropBoxData = g({}, this.initialCropBoxData), this.renderCanvas(), this.cropped) && this.renderCropBox(), this }, clear: function () { return this.cropped && !this.disabled && (g(this.cropBoxData, { left: 0, top: 0, width: 0, height: 0 }), this.cropped = !1, this.renderCropBox(), this.limitCanvas(!0, !0), this.renderCanvas(), X(this.dragBox, n), v(this.cropBox, L)), this }, replace: function (e) { var t = 1 < arguments.length && void 0 !== arguments[1] && arguments[1]; return !this.disabled && e && (this.isImg && (this.element.src = e), t ? (this.url = e, this.image.src = e, this.ready && (this.viewBoxImage.src = e, z(this.previews, function (t) { t.getElementsByTagName("img")[0].src = e }))) : (this.isImg && (this.replaced = !0), this.options.data = null, this.uncreate(), this.load(e))), this }, enable: function () { return this.ready && this.disabled && (this.disabled = !1, X(this.cropper, Q)), this }, disable: function () { return this.ready && !this.disabled && (this.disabled = !0, v(this.cropper, Q)), this }, destroy: function () { var t = this.element; return t[c] && (t[c] = void 0, this.isImg && this.replaced && (t.src = this.originalUrl), this.uncreate()), this }, move: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.canvasData, a = i.left, i = i.top; return this.moveTo(ft(t) ? t : a + Number(t), ft(e) ? e : i + Number(e)) }, moveTo: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.canvasData, a = !1; return t = Number(t), e = Number(e), this.ready && !this.disabled && this.options.movable && (p(t) && (i.left = t, a = !0), p(e) && (i.top = e, a = !0), a) && this.renderCanvas(!0), this }, zoom: function (t, e) { var i = this.canvasData; return t = Number(t), this.zoomTo(i.width * (t = t < 0 ? 1 / (1 - t) : 1 + t) / i.naturalWidth, null, e) }, zoomTo: function (t, e, i) { var a, n, o, h = this.options, r = this.canvasData, s = r.width, c = r.height, d = r.naturalWidth, l = r.naturalHeight; if (0 <= (t = Number(t)) && this.ready && !this.disabled && h.zoomable) { h = d * t, l = l * t; if (!1 === y(this.element, rt, { ratio: t, oldRatio: s / d, originalEvent: i })) return this; i ? (t = this.pointers, d = Et(this.cropper), t = t && Object.keys(t).length ? (o = n = a = 0, z(t, function (t) { var e = t.startX, t = t.startY; a += e, n += t, o += 1 }), { pageX: a /= o, pageY: n /= o }) : { pageX: i.pageX, pageY: i.pageY }, r.left -= (h - s) * ((t.pageX - d.left - r.left) / s), r.top -= (l - c) * ((t.pageY - d.top - r.top) / c)) : u(e) && p(e.x) && p(e.y) ? (r.left -= (h - s) * ((e.x - r.left) / s), r.top -= (l - c) * ((e.y - r.top) / c)) : (r.left -= (h - s) / 2, r.top -= (l - c) / 2), r.width = h, r.height = l, this.renderCanvas(!0) } return this }, rotate: function (t) { return this.rotateTo((this.imageData.rotate || 0) + Number(t)) }, rotateTo: function (t) { return p(t = Number(t)) && this.ready && !this.disabled && this.options.rotatable && (this.imageData.rotate = t % 360, this.renderCanvas(!0, !0)), this }, scaleX: function (t) { var e = this.imageData.scaleY; return this.scale(t, p(e) ? e : 1) }, scaleY: function (t) { var e = this.imageData.scaleX; return this.scale(p(e) ? e : 1, t) }, scale: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.imageData, a = !1; return t = Number(t), e = Number(e), this.ready && !this.disabled && this.options.scalable && (p(t) && (i.scaleX = t, a = !0), p(e) && (i.scaleY = e, a = !0), a) && this.renderCanvas(!0, !0), this }, getData: function () { var i, a, t = 0 < arguments.length && void 0 !== arguments[0] && arguments[0], e = this.options, n = this.imageData, o = this.canvasData, h = this.cropBoxData; return this.ready && this.cropped ? (i = { x: h.left - o.left, y: h.top - o.top, width: h.width, height: h.height }, a = n.width / n.naturalWidth, z(i, function (t, e) { i[e] = t / a }), t && (o = Math.round(i.y + i.height), h = Math.round(i.x + i.width), i.x = Math.round(i.x), i.y = Math.round(i.y), i.width = h - i.x, i.height = o - i.y)) : i = { x: 0, y: 0, width: 0, height: 0 }, e.rotatable && (i.rotate = n.rotate || 0), e.scalable && (i.scaleX = n.scaleX || 1, i.scaleY = n.scaleY || 1), i }, setData: function (t) { var e, i = this.options, a = this.imageData, n = this.canvasData, o = {}; return this.ready && !this.disabled && u(t) && (e = !1, i.rotatable && p(t.rotate) && t.rotate !== a.rotate && (a.rotate = t.rotate, e = !0), i.scalable && (p(t.scaleX) && t.scaleX !== a.scaleX && (a.scaleX = t.scaleX, e = !0), p(t.scaleY)) && t.scaleY !== a.scaleY && (a.scaleY = t.scaleY, e = !0), e && this.renderCanvas(!0, !0), i = a.width / a.naturalWidth, p(t.x) && (o.left = t.x * i + n.left), p(t.y) && (o.top = t.y * i + n.top), p(t.width) && (o.width = t.width * i), p(t.height) && (o.height = t.height * i), this.setCropBoxData(o)), this }, getContainerData: function () { return this.ready ? g({}, this.containerData) : {} }, getImageData: function () { return this.sized ? g({}, this.imageData) : {} }, getCanvasData: function () { var e = this.canvasData, i = {}; return this.ready && z(["left", "top", "width", "height", "naturalWidth", "naturalHeight"], function (t) { i[t] = e[t] }), i }, setCanvasData: function (t) { var e = this.canvasData, i = e.aspectRatio; return this.ready && !this.disabled && u(t) && (p(t.left) && (e.left = t.left), p(t.top) && (e.top = t.top), p(t.width) ? (e.width = t.width, e.height = t.width / i) : p(t.height) && (e.height = t.height, e.width = t.height * i), this.renderCanvas(!0)), this }, getCropBoxData: function () { var t, e = this.cropBoxData; return (t = this.ready && this.cropped ? { left: e.left, top: e.top, width: e.width, height: e.height } : t) || {} }, setCropBoxData: function (t) { var e, i, a = this.cropBoxData, n = this.options.aspectRatio; return this.ready && this.cropped && !this.disabled && u(t) && (p(t.left) && (a.left = t.left), p(t.top) && (a.top = t.top), p(t.width) && t.width !== a.width && (e = !0, a.width = t.width), p(t.height) && t.height !== a.height && (i = !0, a.height = t.height), n && (e ? a.height = a.width / n : i && (a.width = a.height * n)), this.renderCropBox()), this }, getCroppedCanvas: function () { var t, e, i, a, n, o, h, r, s, c, d, l, p, m, u, g, f, v, w, b, y, x, M, C, D, B, k, O = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; return this.ready && window.HTMLCanvasElement ? (B = this.canvasData, u = this.image, l = this.imageData, a = B, v = O, g = l.aspectRatio, e = l.naturalWidth, n = l.naturalHeight, c = void 0 === (c = l.rotate) ? 0 : c, d = void 0 === (d = l.scaleX) ? 1 : d, l = void 0 === (l = l.scaleY) ? 1 : l, i = a.aspectRatio, r = a.naturalWidth, a = a.naturalHeight, h = void 0 === (h = v.fillColor) ? "transparent" : h, p = void 0 === (p = v.imageSmoothingEnabled) || p, m = void 0 === (m = v.imageSmoothingQuality) ? "low" : m, o = void 0 === (o = v.maxWidth) ? 1 / 0 : o, k = void 0 === (k = v.maxHeight) ? 1 / 0 : k, t = void 0 === (t = v.minWidth) ? 0 : t, v = void 0 === (v = v.minHeight) ? 0 : v, w = document.createElement("canvas"), f = w.getContext("2d"), s = R({ aspectRatio: i, width: o, height: k }), i = R({ aspectRatio: i, width: t, height: v }, "cover"), r = Math.min(s.width, Math.max(i.width, r)), s = Math.min(s.height, Math.max(i.height, a)), i = R({ aspectRatio: g, width: o, height: k }), a = R({ aspectRatio: g, width: t, height: v }, "cover"), o = Math.min(i.width, Math.max(a.width, e)), k = Math.min(i.height, Math.max(a.height, n)), g = [-o / 2, -k / 2, o, k], w.width = Y(r), w.height = Y(s), f.fillStyle = h, f.fillRect(0, 0, r, s), f.save(), f.translate(r / 2, s / 2), f.rotate(c * Math.PI / 180), f.scale(d, l), f.imageSmoothingEnabled = p, f.imageSmoothingQuality = m, f.drawImage.apply(f, [u].concat(j(g.map(function (t) { return Math.floor(Y(t)) })))), f.restore(), t = w, this.cropped ? (e = (v = this.getData()).x, i = v.y, a = v.width, n = v.height, 1 != (o = t.width / Math.floor(B.naturalWidth)) && (e *= o, i *= o, a *= o, n *= o), h = R({ aspectRatio: k = a / n, width: O.maxWidth || 1 / 0, height: O.maxHeight || 1 / 0 }), r = R({ aspectRatio: k, width: O.minWidth || 0, height: O.minHeight || 0 }, "cover"), c = (s = R({ aspectRatio: k, width: O.width || (1 != o ? t.width : a), height: O.height || (1 != o ? t.height : n) })).width, d = s.height, c = Math.min(h.width, Math.max(r.width, c)), d = Math.min(h.height, Math.max(r.height, d)), p = (l = document.createElement("canvas")).getContext("2d"), l.width = Y(c), l.height = Y(d), p.fillStyle = O.fillColor || "transparent", p.fillRect(0, 0, c, d), m = O.imageSmoothingEnabled, u = O.imageSmoothingQuality, p.imageSmoothingEnabled = void 0 === m || m, u && (p.imageSmoothingQuality = u), g = t.width, f = t.height, w = i, (v = e) <= -a || g < v ? C = x = b = v = 0 : v <= 0 ? (x = -v, v = 0, C = b = Math.min(g, a + v)) : v <= g && (x = 0, C = b = Math.min(a, g - v)), b <= 0 || w <= -n || f < w ? D = M = y = w = 0 : w <= 0 ? (M = -w, w = 0, D = y = Math.min(f, n + w)) : w <= f && (M = 0, D = y = Math.min(n, f - w)), B = [v, w, b, y], 0 < C && 0 < D && B.push(x * (k = c / a), M * k, C * k, D * k), p.drawImage.apply(p, [t].concat(j(B.map(function (t) { return Math.floor(Y(t)) })))), l) : t) : null }, setAspectRatio: function (t) { var e = this.options; return this.disabled || ft(t) || (e.aspectRatio = Math.max(0, t) || NaN, this.ready && (this.initCropBox(), this.cropped) && this.renderCropBox()), this }, setDragMode: function (t) { var e, i, a = this.options, n = this.dragBox, o = this.face; return this.ready && !this.disabled && (i = a.movable && t === F, a.dragMode = t = (e = t === V) || i ? t : J, w(n, d, t), r(n, $, e), r(n, G, i), a.cropBoxMovable || (w(o, d, t), r(o, $, e), r(o, G, i))), this } }, jt = h.Cropper, Pt = function () { function n(t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, i = this, a = n; if (!(i instanceof a)) throw new TypeError("Cannot call a class as a function"); if (!t || !pt.test(t.tagName)) throw new Error("The first argument is required and must be an or element."); this.element = t, this.options = g({}, mt, u(e) && e), this.cropped = !1, this.disabled = !1, this.pointers = {}, this.ready = !1, this.reloading = !1, this.replaced = !1, this.sized = !1, this.sizing = !1, this.init() } var t, e, i; return t = n, i = [{ key: "noConflict", value: function () { return window.Cropper = jt, n } }, { key: "setDefaults", value: function (t) { g(mt, u(t) && t) } }], (e = [{ key: "init", value: function () { var t, e = this.element, i = e.tagName.toLowerCase(); if (!e[c]) { if (e[c] = this, "img" === i) { if (this.isImg = !0, t = e.getAttribute("src") || "", !(this.originalUrl = t)) return; t = e.src } else "canvas" === i && window.HTMLCanvasElement && (t = e.toDataURL()); this.load(t) } } }, { key: "load", value: function (t) { var e, i, a, n, o, h, r = this; t && (this.url = t, this.imageData = {}, e = this.element, (i = this.options).rotatable || i.scalable || (i.checkOrientation = !1), i.checkOrientation && window.ArrayBuffer ? dt.test(t) ? lt.test(t) ? this.read((h = (h = t).replace(Yt, ""), a = atob(h), h = new ArrayBuffer(a.length), z(n = new Uint8Array(h), function (t, e) { n[e] = a.charCodeAt(e) }), h)) : this.clone() : (o = new XMLHttpRequest, h = this.clone.bind(this), this.reloading = !0, (this.xhr = o).onabort = h, o.onerror = h, o.ontimeout = h, o.onprogress = function () { o.getResponseHeader("content-type") !== st && o.abort() }, o.onload = function () { r.read(o.response) }, o.onloadend = function () { r.reloading = !1, r.xhr = null }, i.checkCrossOrigin && Nt(t) && e.crossOrigin && (t = Lt(t)), o.open("GET", t, !0), o.responseType = "arraybuffer", o.withCredentials = "use-credentials" === e.crossOrigin, o.send()) : this.clone()) } }, { key: "read", value: function (t) { var e = this.options, i = this.imageData, a = Xt(t), n = 0, o = 1, h = 1; 1 < a && (this.url = function (t, e) { for (var i = [], a = new Uint8Array(t); 0 < a.length;)i.push(zt.apply(null, bt(a.subarray(0, 8192)))), a = a.subarray(8192); return "data:".concat(e, ";base64,").concat(btoa(i.join(""))) }(t, st), n = (t = function (t) { var e = 0, i = 1, a = 1; switch (t) { case 2: i = -1; break; case 3: e = -180; break; case 4: a = -1; break; case 5: e = 90, a = -1; break; case 6: e = 90; break; case 7: e = 90, i = -1; break; case 8: e = -90 }return { rotate: e, scaleX: i, scaleY: a } }(a)).rotate, o = t.scaleX, h = t.scaleY), e.rotatable && (i.rotate = n), e.scalable && (i.scaleX = o, i.scaleY = h), this.clone() } }, { key: "clone", value: function () { var t = this.element, e = this.url, i = t.crossOrigin, a = e, n = (this.options.checkCrossOrigin && Nt(e) && (i = i || "anonymous", a = Lt(e)), this.crossOrigin = i, this.crossOriginUrl = a, document.createElement("img")); i && (n.crossOrigin = i), n.src = a || e, n.alt = t.alt || "The image to crop", (this.image = n).onload = this.start.bind(this), n.onerror = this.stop.bind(this), v(n, K), t.parentNode.insertBefore(n, t.nextSibling) } }, { key: "start", value: function () { function t(t, e) { g(a.imageData, { naturalWidth: t, naturalHeight: e, aspectRatio: t / e }), a.initialImageData = g({}, a.imageData), a.sizing = !1, a.sized = !0, a.build() } var e, i, a = this, n = this.image, o = (n.onload = null, n.onerror = null, this.sizing = !0, h.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(h.navigator.userAgent)); n.naturalWidth && !o ? t(n.naturalWidth, n.naturalHeight) : (e = document.createElement("img"), i = document.body || document.documentElement, (this.sizingImage = e).onload = function () { t(e.width, e.height), o || i.removeChild(e) }, e.src = n.src, o || (e.style.cssText = "left:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;opacity:0;position:absolute;top:0;z-index:-1;", i.appendChild(e))) } }, { key: "stop", value: function () { var t = this.image; t.onload = null, t.onerror = null, t.parentNode.removeChild(t), this.image = null } }, { key: "build", value: function () { var t, e, i, a, n, o, h, r, s; this.sized && !this.ready && (t = this.element, e = this.options, i = this.image, a = t.parentNode, (n = document.createElement("div")).innerHTML = '
', o = (n = n.querySelector(".".concat(c, "-container"))).querySelector(".".concat(c, "-canvas")), h = n.querySelector(".".concat(c, "-drag-box")), s = (r = n.querySelector(".".concat(c, "-crop-box"))).querySelector(".".concat(c, "-face")), this.container = a, this.cropper = n, this.canvas = o, this.dragBox = h, this.cropBox = r, this.viewBox = n.querySelector(".".concat(c, "-view-box")), this.face = s, o.appendChild(i), v(t, L), a.insertBefore(n, t.nextSibling), X(i, K), this.initPreview(), this.bind(), e.initialAspectRatio = Math.max(0, e.initialAspectRatio) || NaN, e.aspectRatio = Math.max(0, e.aspectRatio) || NaN, e.viewMode = Math.max(0, Math.min(3, Math.round(e.viewMode))) || 0, v(r, L), e.guides || v(r.getElementsByClassName("".concat(c, "-dashed")), L), e.center || v(r.getElementsByClassName("".concat(c, "-center")), L), e.background && v(n, "".concat(c, "-bg")), e.highlight || v(s, Z), e.cropBoxMovable && (v(s, G), w(s, d, P)), e.cropBoxResizable || (v(r.getElementsByClassName("".concat(c, "-line")), L), v(r.getElementsByClassName("".concat(c, "-point")), L)), this.render(), this.ready = !0, this.setDragMode(e.dragMode), e.autoCrop && this.crop(), this.setData(e.data), l(e.ready) && b(t, "ready", e.ready, { once: !0 }), y(t, "ready")) } }, { key: "unbuild", value: function () { var t; this.ready && (this.ready = !1, this.unbind(), this.resetPreview(), (t = this.cropper.parentNode) && t.removeChild(this.cropper), X(this.element, L)) } }, { key: "uncreate", value: function () { this.ready ? (this.unbuild(), this.ready = !1, this.cropped = !1) : this.sizing ? (this.sizingImage.onload = null, this.sizing = !1, this.sized = !1) : this.reloading ? (this.xhr.onabort = null, this.xhr.abort()) : this.image && this.stop() } }]) && A(t.prototype, e), i && A(t, i), Object.defineProperty(t, "prototype", { writable: !1 }), n }(); return g(Pt.prototype, t, i, e, Rt, St, At), Pt }); \ No newline at end of file +!function (t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).Cropper = e() }(this, function () { "use strict"; function C(e, t) { var i, a = Object.keys(e); return Object.getOwnPropertySymbols && (i = Object.getOwnPropertySymbols(e), t && (i = i.filter(function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable })), a.push.apply(a, i)), a } function S(a) { for (var t = 1; t < arguments.length; t++) { var n = null != arguments[t] ? arguments[t] : {}; t % 2 ? C(Object(n), !0).forEach(function (t) { var e, i; e = a, i = n[t = t], (t = P(t)) in e ? Object.defineProperty(e, t, { value: i, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = i }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(a, Object.getOwnPropertyDescriptors(n)) : C(Object(n)).forEach(function (t) { Object.defineProperty(a, t, Object.getOwnPropertyDescriptor(n, t)) }) } return a } function D(t) { return (D = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t })(t) } function j(t, e) { for (var i = 0; i < e.length; i++) { var a = e[i]; a.enumerable = a.enumerable || !1, a.configurable = !0, "value" in a && (a.writable = !0), Object.defineProperty(t, P(a.key), a) } } function A(t) { return function (t) { if (Array.isArray(t)) return a(t) }(t) || function (t) { if ("undefined" != typeof Symbol && null != t[Symbol.iterator] || null != t["@@iterator"]) return Array.from(t) }(t) || function (t, e) { var i; if (t) return "string" == typeof t ? a(t, e) : "Map" === (i = "Object" === (i = Object.prototype.toString.call(t).slice(8, -1)) && t.constructor ? t.constructor.name : i) || "Set" === i ? Array.from(t) : "Arguments" === i || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i) ? a(t, e) : void 0 }(t) || function () { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.") }() } function a(t, e) { (null == e || e > t.length) && (e = t.length); for (var i = 0, a = new Array(e); i < e; i++)a[i] = t[i]; return a } function P(t) { t = function (t, e) { if ("object" != typeof t || null === t) return t; var i = t[Symbol.toPrimitive]; if (void 0 === i) return ("string" === e ? String : Number)(t); if ("object" != typeof (i = i.call(t, e || "default"))) return i; throw new TypeError("@@toPrimitive must return a primitive value.") }(t, "string"); return "symbol" == typeof t ? t : String(t) } var t = "undefined" != typeof window && void 0 !== window.document, h = t ? window : {}, e = !(!t || !h.document.documentElement) && "ontouchstart" in h.document.documentElement, i = t && "PointerEvent" in h, c = "cropper", I = "all", U = "crop", q = "move", $ = "zoom", B = "e", k = "w", O = "s", T = "n", E = "ne", W = "nw", H = "se", N = "sw", Q = "".concat(c, "-crop"), K = "".concat(c, "-disabled"), L = "".concat(c, "-hidden"), Z = "".concat(c, "-hide"), G = "".concat(c, "-invisible"), n = "".concat(c, "-modal"), V = "".concat(c, "-move"), d = "".concat(c, "Action"), m = "".concat(c, "Preview"), F = "crop", J = "move", _ = "none", tt = "crop", et = "cropend", it = "cropmove", at = "cropstart", nt = "dblclick", ot = i ? "pointerdown" : e ? "touchstart" : "mousedown", ht = i ? "pointermove" : e ? "touchmove" : "mousemove", rt = i ? "pointerup pointercancel" : e ? "touchend touchcancel" : "mouseup", st = "zoom", ct = "image/jpeg", dt = /^e|w|s|n|se|sw|ne|nw|all|crop|move|zoom$/, lt = /^data:/, pt = /^data:image\/jpeg;base64,/, mt = /^img|canvas$/i, ut = { viewMode: 0, dragMode: F, initialAspectRatio: NaN, aspectRatio: NaN, data: null, preview: "", responsive: !0, restore: !0, checkCrossOrigin: !0, checkOrientation: !0, modal: !0, guides: !0, center: !0, highlight: !0, background: !0, autoCrop: !0, autoCropArea: .8, movable: !0, rotatable: !0, scalable: !0, zoomable: !0, zoomOnTouch: !0, zoomOnWheel: !0, wheelZoomRatio: .1, cropBoxMovable: !0, cropBoxResizable: !0, toggleDragModeOnDblclick: !0, minCanvasWidth: 0, minCanvasHeight: 0, minCropBoxWidth: 0, minCropBoxHeight: 0, minContainerWidth: 200, minContainerHeight: 100, ready: null, cropstart: null, cropmove: null, cropend: null, crop: null, zoom: null }, gt = Number.isNaN || h.isNaN; function p(t) { return "number" == typeof t && !gt(t) } function ft(t) { return 0 < t && t < 1 / 0 } function vt(t) { return void 0 === t } function o(t) { return "object" === D(t) && null !== t } var wt = Object.prototype.hasOwnProperty; function u(t) { if (!o(t)) return !1; try { var e = t.constructor, i = e.prototype; return e && i && wt.call(i, "isPrototypeOf") } catch (t) { return !1 } } function l(t) { return "function" == typeof t } var bt = Array.prototype.slice; function yt(t) { return Array.from ? Array.from(t) : bt.call(t) } function z(i, a) { return i && l(a) && (Array.isArray(i) || p(i.length) ? yt(i).forEach(function (t, e) { a.call(i, t, e, i) }) : o(i) && Object.keys(i).forEach(function (t) { a.call(i, i[t], t, i) })), i } var g = Object.assign || function (i) { for (var t = arguments.length, e = new Array(1 < t ? t - 1 : 0), a = 1; a < t; a++)e[a - 1] = arguments[a]; return o(i) && 0 < e.length && e.forEach(function (e) { o(e) && Object.keys(e).forEach(function (t) { i[t] = e[t] }) }), i }, xt = /\.\d*(?:0|9){12}\d*$/; function Y(t, e) { e = 1 < arguments.length && void 0 !== e ? e : 1e11; return xt.test(t) ? Math.round(t * e) / e : t } var Mt = /^width|height|left|top|marginLeft|marginTop$/; function f(t, e) { var i = t.style; z(e, function (t, e) { Mt.test(e) && p(t) && (t = "".concat(t, "px")), i[e] = t }) } function v(t, e) { var i; e && (p(t.length) ? z(t, function (t) { v(t, e) }) : t.classList ? t.classList.add(e) : (i = t.className.trim()) ? i.indexOf(e) < 0 && (t.className = "".concat(i, " ").concat(e)) : t.className = e) } function X(t, e) { e && (p(t.length) ? z(t, function (t) { X(t, e) }) : t.classList ? t.classList.remove(e) : 0 <= t.className.indexOf(e) && (t.className = t.className.replace(e, ""))) } function r(t, e, i) { e && (p(t.length) ? z(t, function (t) { r(t, e, i) }) : (i ? v : X)(t, e)) } var Ct = /([a-z\d])([A-Z])/g; function Dt(t) { return t.replace(Ct, "$1-$2").toLowerCase() } function Bt(t, e) { return o(t[e]) ? t[e] : t.dataset ? t.dataset[e] : t.getAttribute("data-".concat(Dt(e))) } function w(t, e, i) { o(i) ? t[e] = i : t.dataset ? t.dataset[e] = i : t.setAttribute("data-".concat(Dt(e)), i) } var kt, Ot, Tt = /\s\s*/, Et = (Ot = !1, t && (kt = !1, i = function () { }, e = Object.defineProperty({}, "once", { get: function () { return Ot = !0, kt }, set: function (t) { kt = t } }), h.addEventListener("test", i, e), h.removeEventListener("test", i, e)), Ot); function s(i, t, a, e) { var n = 3 < arguments.length && void 0 !== e ? e : {}, o = a; t.trim().split(Tt).forEach(function (t) { var e; Et || (e = i.listeners) && e[t] && e[t][a] && (o = e[t][a], delete e[t][a], 0 === Object.keys(e[t]).length && delete e[t], 0 === Object.keys(e).length) && delete i.listeners, i.removeEventListener(t, o, n) }) } function b(o, t, h, e) { var r = 3 < arguments.length && void 0 !== e ? e : {}, s = h; t.trim().split(Tt).forEach(function (a) { var t, n; r.once && !Et && (t = o.listeners, s = function () { delete n[a][h], o.removeEventListener(a, s, r); for (var t = arguments.length, e = new Array(t), i = 0; i < t; i++)e[i] = arguments[i]; h.apply(o, e) }, (n = void 0 === t ? {} : t)[a] || (n[a] = {}), n[a][h] && o.removeEventListener(a, n[a][h], r), n[a][h] = s, o.listeners = n), o.addEventListener(a, s, r) }) } function y(t, e, i) { var a; return l(Event) && l(CustomEvent) ? a = new CustomEvent(e, { detail: i, bubbles: !0, cancelable: !0 }) : (a = document.createEvent("CustomEvent")).initCustomEvent(e, !0, !0, i), t.dispatchEvent(a) } function Wt(t) { t = t.getBoundingClientRect(); return { left: t.left + (window.pageXOffset - document.documentElement.clientLeft), top: t.top + (window.pageYOffset - document.documentElement.clientTop) } } var Ht = h.location, Nt = /^(\w+:)\/\/([^:/?#]*):?(\d*)/i; function Lt(t) { t = t.match(Nt); return null !== t && (t[1] !== Ht.protocol || t[2] !== Ht.hostname || t[3] !== Ht.port) } function zt(t) { var e = "timestamp=".concat((new Date).getTime()); return t + (-1 === t.indexOf("?") ? "?" : "&") + e } function x(t) { var e = t.rotate, i = t.scaleX, a = t.scaleY, n = t.translateX, t = t.translateY, o = [], n = (p(n) && 0 !== n && o.push("translateX(".concat(n, "px)")), p(t) && 0 !== t && o.push("translateY(".concat(t, "px)")), p(e) && 0 !== e && o.push("rotate(".concat(e, "deg)")), p(i) && 1 !== i && o.push("scaleX(".concat(i, ")")), p(a) && 1 !== a && o.push("scaleY(".concat(a, ")")), o.length ? o.join(" ") : "none"); return { WebkitTransform: n, msTransform: n, transform: n } } function M(t, e) { var i = t.pageX, t = t.pageY, a = { endX: i, endY: t }; return e ? a : S({ startX: i, startY: t }, a) } function R(t, e) { var i, a = t.aspectRatio, n = t.height, t = t.width, e = 1 < arguments.length && void 0 !== e ? e : "contain", o = ft(t), h = ft(n); return o && h ? (i = n * a, "contain" === e && t < i || "cover" === e && i < t ? n = t / a : t = n * a) : o ? n = t / a : h && (t = n * a), { width: t, height: n } } var Yt = String.fromCharCode; var Xt = /^data:.*,/; function Rt(t) { var e, i, a, n, o, h, r, s = new DataView(t); try { if (255 === s.getUint8(0) && 216 === s.getUint8(1)) for (var c = s.byteLength, d = 2; d + 1 < c;) { if (255 === s.getUint8(d) && 225 === s.getUint8(d + 1)) { i = d; break } d += 1 } if (a = i && (n = i + 10, "Exif" === function (t, e, i) { var a = ""; i += e; for (var n = e; n < i; n += 1)a += Yt(t.getUint8(n)); return a }(s, i + 4, 4)) && ((r = 18761 === (o = s.getUint16(n))) || 19789 === o) && 42 === s.getUint16(n + 2, r) && 8 <= (h = s.getUint32(n + 4, r)) ? n + h : a) for (var l, p = s.getUint16(a, r), m = 0; m < p; m += 1)if (l = a + 12 * m + 2, 274 === s.getUint16(l, r)) { l += 8, e = s.getUint16(l, r), s.setUint16(l, 1, r); break } } catch (t) { e = 1 } return e } var t = { render: function () { this.initContainer(), this.initCanvas(), this.initCropBox(), this.renderCanvas(), this.cropped && this.renderCropBox() }, initContainer: function () { var t = this.element, e = this.options, i = this.container, a = this.cropper, n = Number(e.minContainerWidth), e = Number(e.minContainerHeight), n = (v(a, L), X(t, L), { width: Math.max(i.offsetWidth, 0 <= n ? n : 200), height: Math.max(i.offsetHeight, 0 <= e ? e : 100) }); f(a, { width: (this.containerData = n).width, height: n.height }), v(t, L), X(a, L) }, initCanvas: function () { var t = this.containerData, e = this.imageData, i = this.options.viewMode, a = Math.abs(e.rotate) % 180 == 90, n = a ? e.naturalHeight : e.naturalWidth, a = a ? e.naturalWidth : e.naturalHeight, e = n / a, o = t.width, h = t.height, e = (t.height * e > t.width ? 3 === i ? o = t.height * e : h = t.width / e : 3 === i ? h = t.width / e : o = t.height * e, { aspectRatio: e, naturalWidth: n, naturalHeight: a, width: o, height: h }); this.canvasData = e, this.limited = 1 === i || 2 === i, this.limitCanvas(!0, !0), e.width = Math.min(Math.max(e.width, e.minWidth), e.maxWidth), e.height = Math.min(Math.max(e.height, e.minHeight), e.maxHeight), e.left = (t.width - e.width) / 2, e.top = (t.height - e.height) / 2, e.oldLeft = e.left, e.oldTop = e.top, this.initialCanvasData = g({}, e) }, limitCanvas: function (t, e) { var i = this.options, a = this.containerData, n = this.canvasData, o = this.cropBoxData, h = i.viewMode, r = n.aspectRatio, s = this.cropped && o; t && (t = Number(i.minCanvasWidth) || 0, i = Number(i.minCanvasHeight) || 0, 1 < h ? (t = Math.max(t, a.width), i = Math.max(i, a.height), 3 === h && (t < i * r ? t = i * r : i = t / r)) : 0 < h && (t ? t = Math.max(t, s ? o.width : 0) : i ? i = Math.max(i, s ? o.height : 0) : s && ((t = o.width) < (i = o.height) * r ? t = i * r : i = t / r)), t = (r = R({ aspectRatio: r, width: t, height: i })).width, i = r.height, n.minWidth = t, n.minHeight = i, n.maxWidth = 1 / 0, n.maxHeight = 1 / 0), e && ((s ? 0 : 1) < h ? (r = a.width - n.width, t = a.height - n.height, n.minLeft = Math.min(0, r), n.minTop = Math.min(0, t), n.maxLeft = Math.max(0, r), n.maxTop = Math.max(0, t), s && this.limited && (n.minLeft = Math.min(o.left, o.left + (o.width - n.width)), n.minTop = Math.min(o.top, o.top + (o.height - n.height)), n.maxLeft = o.left, n.maxTop = o.top, 2 === h) && (n.width >= a.width && (n.minLeft = Math.min(0, r), n.maxLeft = Math.max(0, r)), n.height >= a.height) && (n.minTop = Math.min(0, t), n.maxTop = Math.max(0, t))) : (n.minLeft = -n.width, n.minTop = -n.height, n.maxLeft = a.width, n.maxTop = a.height)) }, renderCanvas: function (t, e) { var i, a, n, o, h = this.canvasData, r = this.imageData; e && (e = { width: r.naturalWidth * Math.abs(r.scaleX || 1), height: r.naturalHeight * Math.abs(r.scaleY || 1), degree: r.rotate || 0 }, r = e.width, o = e.height, e = e.degree, i = 90 == (e = Math.abs(e) % 180) ? { width: o, height: r } : (a = e % 90 * Math.PI / 180, i = Math.sin(a), n = r * (a = Math.cos(a)) + o * i, r = r * i + o * a, 90 < e ? { width: r, height: n } : { width: n, height: r }), a = h.width * ((o = i.width) / h.naturalWidth), n = h.height * ((e = i.height) / h.naturalHeight), h.left -= (a - h.width) / 2, h.top -= (n - h.height) / 2, h.width = a, h.height = n, h.aspectRatio = o / e, h.naturalWidth = o, h.naturalHeight = e, this.limitCanvas(!0, !1)), (h.width > h.maxWidth || h.width < h.minWidth) && (h.left = h.oldLeft), (h.height > h.maxHeight || h.height < h.minHeight) && (h.top = h.oldTop), h.width = Math.min(Math.max(h.width, h.minWidth), h.maxWidth), h.height = Math.min(Math.max(h.height, h.minHeight), h.maxHeight), this.limitCanvas(!1, !0), h.left = Math.min(Math.max(h.left, h.minLeft), h.maxLeft), h.top = Math.min(Math.max(h.top, h.minTop), h.maxTop), h.oldLeft = h.left, h.oldTop = h.top, f(this.canvas, g({ width: h.width, height: h.height }, x({ translateX: h.left, translateY: h.top }))), this.renderImage(t), this.cropped && this.limited && this.limitCropBox(!0, !0) }, renderImage: function (t) { var e = this.canvasData, i = this.imageData, a = i.naturalWidth * (e.width / e.naturalWidth), n = i.naturalHeight * (e.height / e.naturalHeight); g(i, { width: a, height: n, left: (e.width - a) / 2, top: (e.height - n) / 2 }), f(this.image, g({ width: i.width, height: i.height }, x(g({ translateX: i.left, translateY: i.top }, i)))), t && this.output() }, initCropBox: function () { var t = this.options, e = this.canvasData, i = t.aspectRatio || t.initialAspectRatio, t = Number(t.autoCropArea) || .8, a = { width: e.width, height: e.height }; i && (e.height * i > e.width ? a.height = a.width / i : a.width = a.height * i), this.cropBoxData = a, this.limitCropBox(!0, !0), a.width = Math.min(Math.max(a.width, a.minWidth), a.maxWidth), a.height = Math.min(Math.max(a.height, a.minHeight), a.maxHeight), a.width = Math.max(a.minWidth, a.width * t), a.height = Math.max(a.minHeight, a.height * t), a.left = e.left + (e.width - a.width) / 2, a.top = e.top + (e.height - a.height) / 2, a.oldLeft = a.left, a.oldTop = a.top, this.initialCropBoxData = g({}, a) }, limitCropBox: function (t, e) { var i, a, n = this.options, o = this.containerData, h = this.canvasData, r = this.cropBoxData, s = this.limited, c = n.aspectRatio; t && (t = Number(n.minCropBoxWidth) || 0, n = Number(n.minCropBoxHeight) || 0, i = s ? Math.min(o.width, h.width, h.width + h.left, o.width - h.left) : o.width, a = s ? Math.min(o.height, h.height, h.height + h.top, o.height - h.top) : o.height, t = Math.min(t, o.width), n = Math.min(n, o.height), c && (t && n ? t < n * c ? n = t / c : t = n * c : t ? n = t / c : n && (t = n * c), i < a * c ? a = i / c : i = a * c), r.minWidth = Math.min(t, i), r.minHeight = Math.min(n, a), r.maxWidth = i, r.maxHeight = a), e && (s ? (r.minLeft = Math.max(0, h.left), r.minTop = Math.max(0, h.top), r.maxLeft = Math.min(o.width, h.left + h.width) - r.width, r.maxTop = Math.min(o.height, h.top + h.height) - r.height) : (r.minLeft = 0, r.minTop = 0, r.maxLeft = o.width - r.width, r.maxTop = o.height - r.height)) }, renderCropBox: function () { var t = this.options, e = this.containerData, i = this.cropBoxData; (i.width > i.maxWidth || i.width < i.minWidth) && (i.left = i.oldLeft), (i.height > i.maxHeight || i.height < i.minHeight) && (i.top = i.oldTop), i.width = Math.min(Math.max(i.width, i.minWidth), i.maxWidth), i.height = Math.min(Math.max(i.height, i.minHeight), i.maxHeight), this.limitCropBox(!1, !0), i.left = Math.min(Math.max(i.left, i.minLeft), i.maxLeft), i.top = Math.min(Math.max(i.top, i.minTop), i.maxTop), i.oldLeft = i.left, i.oldTop = i.top, t.movable && t.cropBoxMovable && w(this.face, d, i.width >= e.width && i.height >= e.height ? q : I), f(this.cropBox, g({ width: i.width, height: i.height }, x({ translateX: i.left, translateY: i.top }))), this.cropped && this.limited && this.limitCanvas(!0, !0), this.disabled || this.output() }, output: function () { this.preview(), y(this.element, tt, this.getData()) } }, i = { initPreview: function () { var t = this.element, i = this.crossOrigin, e = this.options.preview, a = i ? this.crossOriginUrl : this.url, n = t.alt || "The image to preview", o = document.createElement("img"); i && (o.crossOrigin = i), o.src = a, o.alt = n, this.viewBox.appendChild(o), this.viewBoxImage = o, e && ("string" == typeof (o = e) ? o = t.ownerDocument.querySelectorAll(e) : e.querySelector && (o = [e]), z(this.previews = o, function (t) { var e = document.createElement("img"); w(t, m, { width: t.offsetWidth, height: t.offsetHeight, html: t.innerHTML }), i && (e.crossOrigin = i), e.src = a, e.alt = n, e.style.cssText = 'display:block;width:100%;height:auto;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation:0deg!important;"', t.innerHTML = "", t.appendChild(e) })) }, resetPreview: function () { z(this.previews, function (e) { var i = Bt(e, m), i = (f(e, { width: i.width, height: i.height }), e.innerHTML = i.html, e), e = m; if (o(i[e])) try { delete i[e] } catch (t) { i[e] = void 0 } else if (i.dataset) try { delete i.dataset[e] } catch (t) { i.dataset[e] = void 0 } else i.removeAttribute("data-".concat(Dt(e))) }) }, preview: function () { var h = this.imageData, t = this.canvasData, e = this.cropBoxData, r = e.width, s = e.height, c = h.width, d = h.height, l = e.left - t.left - h.left, p = e.top - t.top - h.top; this.cropped && !this.disabled && (f(this.viewBoxImage, g({ width: c, height: d }, x(g({ translateX: -l, translateY: -p }, h)))), z(this.previews, function (t) { var e = Bt(t, m), i = e.width, e = e.height, a = i, n = e, o = 1; r && (n = s * (o = i / r)), s && e < n && (a = r * (o = e / s), n = e), f(t, { width: a, height: n }), f(t.getElementsByTagName("img")[0], g({ width: c * o, height: d * o }, x(g({ translateX: -l * o, translateY: -p * o }, h)))) })) } }, e = { bind: function () { var t = this.element, e = this.options, i = this.cropper; l(e.cropstart) && b(t, at, e.cropstart), l(e.cropmove) && b(t, it, e.cropmove), l(e.cropend) && b(t, et, e.cropend), l(e.crop) && b(t, tt, e.crop), l(e.zoom) && b(t, st, e.zoom), b(i, ot, this.onCropStart = this.cropStart.bind(this)), e.zoomable && e.zoomOnWheel && b(i, "wheel", this.onWheel = this.wheel.bind(this), { passive: !1, capture: !0 }), e.toggleDragModeOnDblclick && b(i, nt, this.onDblclick = this.dblclick.bind(this)), b(t.ownerDocument, ht, this.onCropMove = this.cropMove.bind(this)), b(t.ownerDocument, rt, this.onCropEnd = this.cropEnd.bind(this)), e.responsive && b(window, "resize", this.onResize = this.resize.bind(this)) }, unbind: function () { var t = this.element, e = this.options, i = this.cropper; l(e.cropstart) && s(t, at, e.cropstart), l(e.cropmove) && s(t, it, e.cropmove), l(e.cropend) && s(t, et, e.cropend), l(e.crop) && s(t, tt, e.crop), l(e.zoom) && s(t, st, e.zoom), s(i, ot, this.onCropStart), e.zoomable && e.zoomOnWheel && s(i, "wheel", this.onWheel, { passive: !1, capture: !0 }), e.toggleDragModeOnDblclick && s(i, nt, this.onDblclick), s(t.ownerDocument, ht, this.onCropMove), s(t.ownerDocument, rt, this.onCropEnd), e.responsive && s(window, "resize", this.onResize) } }, St = { resize: function () { var t, e, i, a, n, o, h; this.disabled || (t = this.options, a = this.container, e = this.containerData, i = a.offsetWidth / e.width, a = a.offsetHeight / e.height, 1 != (n = Math.abs(i - 1) > Math.abs(a - 1) ? i : a) && (t.restore && (o = this.getCanvasData(), h = this.getCropBoxData()), this.render(), t.restore) && (this.setCanvasData(z(o, function (t, e) { o[e] = t * n })), this.setCropBoxData(z(h, function (t, e) { h[e] = t * n })))) }, dblclick: function () { var t, e; this.disabled || this.options.dragMode === _ || this.setDragMode((t = this.dragBox, e = Q, (t.classList ? t.classList.contains(e) : -1 < t.className.indexOf(e)) ? J : F)) }, wheel: function (t) { var e = this, i = Number(this.options.wheelZoomRatio) || .1, a = 1; this.disabled || (t.preventDefault(), this.wheeling) || (this.wheeling = !0, setTimeout(function () { e.wheeling = !1 }, 50), t.deltaY ? a = 0 < t.deltaY ? 1 : -1 : t.wheelDelta ? a = -t.wheelDelta / 120 : t.detail && (a = 0 < t.detail ? 1 : -1), this.zoom(-a * i, t)) }, cropStart: function (t) { var e, i = t.buttons, a = t.button; this.disabled || ("mousedown" === t.type || "pointerdown" === t.type && "mouse" === t.pointerType) && (p(i) && 1 !== i || p(a) && 0 !== a || t.ctrlKey) || (i = this.options, e = this.pointers, t.changedTouches ? z(t.changedTouches, function (t) { e[t.identifier] = M(t) }) : e[t.pointerId || 0] = M(t), a = 1 < Object.keys(e).length && i.zoomable && i.zoomOnTouch ? $ : Bt(t.target, d), dt.test(a) && !1 !== y(this.element, at, { originalEvent: t, action: a }) && (t.preventDefault(), this.action = a, this.cropping = !1, a === U) && (this.cropping = !0, v(this.dragBox, n))) }, cropMove: function (t) { var e, i = this.action; !this.disabled && i && (e = this.pointers, t.preventDefault(), !1 !== y(this.element, it, { originalEvent: t, action: i })) && (t.changedTouches ? z(t.changedTouches, function (t) { g(e[t.identifier] || {}, M(t, !0)) }) : g(e[t.pointerId || 0] || {}, M(t, !0)), this.change(t)) }, cropEnd: function (t) { var e, i; this.disabled || (e = this.action, i = this.pointers, t.changedTouches ? z(t.changedTouches, function (t) { delete i[t.identifier] }) : delete i[t.pointerId || 0], e && (t.preventDefault(), Object.keys(i).length || (this.action = ""), this.cropping && (this.cropping = !1, r(this.dragBox, n, this.cropped && this.options.modal)), y(this.element, et, { originalEvent: t, action: e }))) } }, jt = { change: function (t) { function e(t) { switch (t) { case B: f + D.x > y && (D.x = y - f); break; case k: p + D.x < w && (D.x = w - p); break; case T: m + D.y < b && (D.y = b - m); break; case O: v + D.y > x && (D.y = x - v) } } var i, a, o, n = this.options, h = this.canvasData, r = this.containerData, s = this.cropBoxData, c = this.pointers, d = this.action, l = n.aspectRatio, p = s.left, m = s.top, u = s.width, g = s.height, f = p + u, v = m + g, w = 0, b = 0, y = r.width, x = r.height, M = !0, C = (!l && t.shiftKey && (l = u && g ? u / g : 1), this.limited && (w = s.minLeft, b = s.minTop, y = w + Math.min(r.width, h.width, h.left + h.width), x = b + Math.min(r.height, h.height, h.top + h.height)), c[Object.keys(c)[0]]), D = { x: C.endX - C.startX, y: C.endY - C.startY }; switch (d) { case I: p += D.x, m += D.y; break; case B: 0 <= D.x && (y <= f || l && (m <= b || x <= v)) ? M = !1 : (e(B), (u += D.x) < 0 && (d = k, p -= u = -u), l && (m += (s.height - (g = u / l)) / 2)); break; case T: D.y <= 0 && (m <= b || l && (p <= w || y <= f)) ? M = !1 : (e(T), g -= D.y, m += D.y, g < 0 && (d = O, m -= g = -g), l && (p += (s.width - (u = g * l)) / 2)); break; case k: D.x <= 0 && (p <= w || l && (m <= b || x <= v)) ? M = !1 : (e(k), u -= D.x, p += D.x, u < 0 && (d = B, p -= u = -u), l && (m += (s.height - (g = u / l)) / 2)); break; case O: 0 <= D.y && (x <= v || l && (p <= w || y <= f)) ? M = !1 : (e(O), (g += D.y) < 0 && (d = T, m -= g = -g), l && (p += (s.width - (u = g * l)) / 2)); break; case E: if (l) { if (D.y <= 0 && (m <= b || y <= f)) { M = !1; break } e(T), g -= D.y, m += D.y, u = g * l } else e(T), e(B), !(0 <= D.x) || f < y ? u += D.x : D.y <= 0 && m <= b && (M = !1), (!(D.y <= 0) || b < m) && (g -= D.y, m += D.y); u < 0 && g < 0 ? (d = N, m -= g = -g, p -= u = -u) : u < 0 ? (d = W, p -= u = -u) : g < 0 && (d = H, m -= g = -g); break; case W: if (l) { if (D.y <= 0 && (m <= b || p <= w)) { M = !1; break } e(T), g -= D.y, m += D.y, p += s.width - (u = g * l) } else e(T), e(k), !(D.x <= 0) || w < p ? (u -= D.x, p += D.x) : D.y <= 0 && m <= b && (M = !1), (!(D.y <= 0) || b < m) && (g -= D.y, m += D.y); u < 0 && g < 0 ? (d = H, m -= g = -g, p -= u = -u) : u < 0 ? (d = E, p -= u = -u) : g < 0 && (d = N, m -= g = -g); break; case N: if (l) { if (D.x <= 0 && (p <= w || x <= v)) { M = !1; break } e(k), u -= D.x, p += D.x, g = u / l } else e(O), e(k), !(D.x <= 0) || w < p ? (u -= D.x, p += D.x) : 0 <= D.y && x <= v && (M = !1), (!(0 <= D.y) || v < x) && (g += D.y); u < 0 && g < 0 ? (d = E, m -= g = -g, p -= u = -u) : u < 0 ? (d = H, p -= u = -u) : g < 0 && (d = W, m -= g = -g); break; case H: if (l) { if (0 <= D.x && (y <= f || x <= v)) { M = !1; break } e(B), g = (u += D.x) / l } else e(O), e(B), !(0 <= D.x) || f < y ? u += D.x : 0 <= D.y && x <= v && (M = !1), (!(0 <= D.y) || v < x) && (g += D.y); u < 0 && g < 0 ? (d = W, m -= g = -g, p -= u = -u) : u < 0 ? (d = N, p -= u = -u) : g < 0 && (d = E, m -= g = -g); break; case q: this.move(D.x, D.y), M = !1; break; case $: this.zoom((a = S({}, i = c), o = 0, z(i, function (n, t) { delete a[t], z(a, function (t) { var e = Math.abs(n.startX - t.startX), i = Math.abs(n.startY - t.startY), a = Math.abs(n.endX - t.endX), t = Math.abs(n.endY - t.endY), e = Math.sqrt(e * e + i * i), i = (Math.sqrt(a * a + t * t) - e) / e; Math.abs(i) > Math.abs(o) && (o = i) }) }), o), t), M = !1; break; case U: D.x && D.y ? (i = Wt(this.cropper), p = C.startX - i.left, m = C.startY - i.top, u = s.minWidth, g = s.minHeight, 0 < D.x ? d = 0 < D.y ? H : E : D.x < 0 && (p -= u, d = 0 < D.y ? N : W), D.y < 0 && (m -= g), this.cropped || (X(this.cropBox, L), this.cropped = !0, this.limited && this.limitCropBox(!0, !0))) : M = !1 }M && (s.width = u, s.height = g, s.left = p, s.top = m, this.action = d, this.renderCropBox()), z(c, function (t) { t.startX = t.endX, t.startY = t.endY }) } }, At = { crop: function () { return !this.ready || this.cropped || this.disabled || (this.cropped = !0, this.limitCropBox(!0, !0), this.options.modal && v(this.dragBox, n), X(this.cropBox, L), this.setCropBoxData(this.initialCropBoxData)), this }, reset: function () { return this.ready && !this.disabled && (this.imageData = g({}, this.initialImageData), this.canvasData = g({}, this.initialCanvasData), this.cropBoxData = g({}, this.initialCropBoxData), this.renderCanvas(), this.cropped) && this.renderCropBox(), this }, clear: function () { return this.cropped && !this.disabled && (g(this.cropBoxData, { left: 0, top: 0, width: 0, height: 0 }), this.cropped = !1, this.renderCropBox(), this.limitCanvas(!0, !0), this.renderCanvas(), X(this.dragBox, n), v(this.cropBox, L)), this }, replace: function (e) { var t = 1 < arguments.length && void 0 !== arguments[1] && arguments[1]; return !this.disabled && e && (this.isImg && (this.element.src = e), t ? (this.url = e, this.image.src = e, this.ready && (this.viewBoxImage.src = e, z(this.previews, function (t) { t.getElementsByTagName("img")[0].src = e }))) : (this.isImg && (this.replaced = !0), this.options.data = null, this.uncreate(), this.load(e))), this }, enable: function () { return this.ready && this.disabled && (this.disabled = !1, X(this.cropper, K)), this }, disable: function () { return this.ready && !this.disabled && (this.disabled = !0, v(this.cropper, K)), this }, destroy: function () { var t = this.element; return t[c] && (t[c] = void 0, this.isImg && this.replaced && (t.src = this.originalUrl), this.uncreate()), this }, move: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.canvasData, a = i.left, i = i.top; return this.moveTo(vt(t) ? t : a + Number(t), vt(e) ? e : i + Number(e)) }, moveTo: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.canvasData, a = !1; return t = Number(t), e = Number(e), this.ready && !this.disabled && this.options.movable && (p(t) && (i.left = t, a = !0), p(e) && (i.top = e, a = !0), a) && this.renderCanvas(!0), this }, zoom: function (t, e) { var i = this.canvasData; return t = Number(t), this.zoomTo(i.width * (t = t < 0 ? 1 / (1 - t) : 1 + t) / i.naturalWidth, null, e) }, zoomTo: function (t, e, i) { var a, n, o, h = this.options, r = this.canvasData, s = r.width, c = r.height, d = r.naturalWidth, l = r.naturalHeight; if (0 <= (t = Number(t)) && this.ready && !this.disabled && h.zoomable) { h = d * t, l = l * t; if (!1 === y(this.element, st, { ratio: t, oldRatio: s / d, originalEvent: i })) return this; i ? (t = this.pointers, d = Wt(this.cropper), t = t && Object.keys(t).length ? (o = n = a = 0, z(t, function (t) { var e = t.startX, t = t.startY; a += e, n += t, o += 1 }), { pageX: a /= o, pageY: n /= o }) : { pageX: i.pageX, pageY: i.pageY }, r.left -= (h - s) * ((t.pageX - d.left - r.left) / s), r.top -= (l - c) * ((t.pageY - d.top - r.top) / c)) : u(e) && p(e.x) && p(e.y) ? (r.left -= (h - s) * ((e.x - r.left) / s), r.top -= (l - c) * ((e.y - r.top) / c)) : (r.left -= (h - s) / 2, r.top -= (l - c) / 2), r.width = h, r.height = l, this.renderCanvas(!0) } return this }, rotate: function (t) { return this.rotateTo((this.imageData.rotate || 0) + Number(t)) }, rotateTo: function (t) { return p(t = Number(t)) && this.ready && !this.disabled && this.options.rotatable && (this.imageData.rotate = t % 360, this.renderCanvas(!0, !0)), this }, scaleX: function (t) { var e = this.imageData.scaleY; return this.scale(t, p(e) ? e : 1) }, scaleY: function (t) { var e = this.imageData.scaleX; return this.scale(p(e) ? e : 1, t) }, scale: function (t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : t, i = this.imageData, a = !1; return t = Number(t), e = Number(e), this.ready && !this.disabled && this.options.scalable && (p(t) && (i.scaleX = t, a = !0), p(e) && (i.scaleY = e, a = !0), a) && this.renderCanvas(!0, !0), this }, getData: function () { var i, a, t = 0 < arguments.length && void 0 !== arguments[0] && arguments[0], e = this.options, n = this.imageData, o = this.canvasData, h = this.cropBoxData; return this.ready && this.cropped ? (i = { x: h.left - o.left, y: h.top - o.top, width: h.width, height: h.height }, a = n.width / n.naturalWidth, z(i, function (t, e) { i[e] = t / a }), t && (o = Math.round(i.y + i.height), h = Math.round(i.x + i.width), i.x = Math.round(i.x), i.y = Math.round(i.y), i.width = h - i.x, i.height = o - i.y)) : i = { x: 0, y: 0, width: 0, height: 0 }, e.rotatable && (i.rotate = n.rotate || 0), e.scalable && (i.scaleX = n.scaleX || 1, i.scaleY = n.scaleY || 1), i }, setData: function (t) { var e, i = this.options, a = this.imageData, n = this.canvasData, o = {}; return this.ready && !this.disabled && u(t) && (e = !1, i.rotatable && p(t.rotate) && t.rotate !== a.rotate && (a.rotate = t.rotate, e = !0), i.scalable && (p(t.scaleX) && t.scaleX !== a.scaleX && (a.scaleX = t.scaleX, e = !0), p(t.scaleY)) && t.scaleY !== a.scaleY && (a.scaleY = t.scaleY, e = !0), e && this.renderCanvas(!0, !0), i = a.width / a.naturalWidth, p(t.x) && (o.left = t.x * i + n.left), p(t.y) && (o.top = t.y * i + n.top), p(t.width) && (o.width = t.width * i), p(t.height) && (o.height = t.height * i), this.setCropBoxData(o)), this }, getContainerData: function () { return this.ready ? g({}, this.containerData) : {} }, getImageData: function () { return this.sized ? g({}, this.imageData) : {} }, getCanvasData: function () { var e = this.canvasData, i = {}; return this.ready && z(["left", "top", "width", "height", "naturalWidth", "naturalHeight"], function (t) { i[t] = e[t] }), i }, setCanvasData: function (t) { var e = this.canvasData, i = e.aspectRatio; return this.ready && !this.disabled && u(t) && (p(t.left) && (e.left = t.left), p(t.top) && (e.top = t.top), p(t.width) ? (e.width = t.width, e.height = t.width / i) : p(t.height) && (e.height = t.height, e.width = t.height * i), this.renderCanvas(!0)), this }, getCropBoxData: function () { var t, e = this.cropBoxData; return (t = this.ready && this.cropped ? { left: e.left, top: e.top, width: e.width, height: e.height } : t) || {} }, setCropBoxData: function (t) { var e, i, a = this.cropBoxData, n = this.options.aspectRatio; return this.ready && this.cropped && !this.disabled && u(t) && (p(t.left) && (a.left = t.left), p(t.top) && (a.top = t.top), p(t.width) && t.width !== a.width && (e = !0, a.width = t.width), p(t.height) && t.height !== a.height && (i = !0, a.height = t.height), n && (e ? a.height = a.width / n : i && (a.width = a.height * n)), this.renderCropBox()), this }, getCroppedCanvas: function () { var t, e, i, a, n, o, h, r, s, c, d, l, p, m, u, g, f, v, w, b, y, x, M, C, D, B, k, O = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : {}; return this.ready && window.HTMLCanvasElement ? (B = this.canvasData, u = this.image, l = this.imageData, a = B, v = O, g = l.aspectRatio, e = l.naturalWidth, n = l.naturalHeight, c = void 0 === (c = l.rotate) ? 0 : c, d = void 0 === (d = l.scaleX) ? 1 : d, l = void 0 === (l = l.scaleY) ? 1 : l, i = a.aspectRatio, r = a.naturalWidth, a = a.naturalHeight, h = void 0 === (h = v.fillColor) ? "transparent" : h, p = void 0 === (p = v.imageSmoothingEnabled) || p, m = void 0 === (m = v.imageSmoothingQuality) ? "low" : m, o = void 0 === (o = v.maxWidth) ? 1 / 0 : o, k = void 0 === (k = v.maxHeight) ? 1 / 0 : k, t = void 0 === (t = v.minWidth) ? 0 : t, v = void 0 === (v = v.minHeight) ? 0 : v, w = document.createElement("canvas"), f = w.getContext("2d"), s = R({ aspectRatio: i, width: o, height: k }), i = R({ aspectRatio: i, width: t, height: v }, "cover"), r = Math.min(s.width, Math.max(i.width, r)), s = Math.min(s.height, Math.max(i.height, a)), i = R({ aspectRatio: g, width: o, height: k }), a = R({ aspectRatio: g, width: t, height: v }, "cover"), o = Math.min(i.width, Math.max(a.width, e)), k = Math.min(i.height, Math.max(a.height, n)), g = [-o / 2, -k / 2, o, k], w.width = Y(r), w.height = Y(s), f.fillStyle = h, f.fillRect(0, 0, r, s), f.save(), f.translate(r / 2, s / 2), f.rotate(c * Math.PI / 180), f.scale(d, l), f.imageSmoothingEnabled = p, f.imageSmoothingQuality = m, f.drawImage.apply(f, [u].concat(A(g.map(function (t) { return Math.floor(Y(t)) })))), f.restore(), t = w, this.cropped ? (e = (v = this.getData(O.rounded)).x, i = v.y, a = v.width, n = v.height, 1 != (o = t.width / Math.floor(B.naturalWidth)) && (e *= o, i *= o, a *= o, n *= o), h = R({ aspectRatio: k = a / n, width: O.maxWidth || 1 / 0, height: O.maxHeight || 1 / 0 }), r = R({ aspectRatio: k, width: O.minWidth || 0, height: O.minHeight || 0 }, "cover"), c = (s = R({ aspectRatio: k, width: O.width || (1 != o ? t.width : a), height: O.height || (1 != o ? t.height : n) })).width, d = s.height, c = Math.min(h.width, Math.max(r.width, c)), d = Math.min(h.height, Math.max(r.height, d)), p = (l = document.createElement("canvas")).getContext("2d"), l.width = Y(c), l.height = Y(d), p.fillStyle = O.fillColor || "transparent", p.fillRect(0, 0, c, d), m = O.imageSmoothingEnabled, u = O.imageSmoothingQuality, p.imageSmoothingEnabled = void 0 === m || m, u && (p.imageSmoothingQuality = u), g = t.width, f = t.height, w = i, (v = e) <= -a || g < v ? C = x = b = v = 0 : v <= 0 ? (x = -v, v = 0, C = b = Math.min(g, a + v)) : v <= g && (x = 0, C = b = Math.min(a, g - v)), b <= 0 || w <= -n || f < w ? D = M = y = w = 0 : w <= 0 ? (M = -w, w = 0, D = y = Math.min(f, n + w)) : w <= f && (M = 0, D = y = Math.min(n, f - w)), B = [v, w, b, y], 0 < C && 0 < D && B.push(x * (k = c / a), M * k, C * k, D * k), p.drawImage.apply(p, [t].concat(A(B.map(function (t) { return Math.floor(Y(t)) })))), l) : t) : null }, setAspectRatio: function (t) { var e = this.options; return this.disabled || vt(t) || (e.aspectRatio = Math.max(0, t) || NaN, this.ready && (this.initCropBox(), this.cropped) && this.renderCropBox()), this }, setDragMode: function (t) { var e, i, a = this.options, n = this.dragBox, o = this.face; return this.ready && !this.disabled && (i = a.movable && t === J, a.dragMode = t = (e = t === F) || i ? t : _, w(n, d, t), r(n, Q, e), r(n, V, i), a.cropBoxMovable || (w(o, d, t), r(o, Q, e), r(o, V, i))), this } }, Pt = h.Cropper, It = function () { function n(t) { var e = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, i = this, a = n; if (!(i instanceof a)) throw new TypeError("Cannot call a class as a function"); if (!t || !mt.test(t.tagName)) throw new Error("The first argument is required and must be an or element."); this.element = t, this.options = g({}, ut, u(e) && e), this.cropped = !1, this.disabled = !1, this.pointers = {}, this.ready = !1, this.reloading = !1, this.replaced = !1, this.sized = !1, this.sizing = !1, this.init() } var t, e, i; return t = n, i = [{ key: "noConflict", value: function () { return window.Cropper = Pt, n } }, { key: "setDefaults", value: function (t) { g(ut, u(t) && t) } }], (e = [{ key: "init", value: function () { var t, e = this.element, i = e.tagName.toLowerCase(); if (!e[c]) { if (e[c] = this, "img" === i) { if (this.isImg = !0, t = e.getAttribute("src") || "", !(this.originalUrl = t)) return; t = e.src } else "canvas" === i && window.HTMLCanvasElement && (t = e.toDataURL()); this.load(t) } } }, { key: "load", value: function (t) { var e, i, a, n, o, h, r = this; t && (this.url = t, this.imageData = {}, e = this.element, (i = this.options).rotatable || i.scalable || (i.checkOrientation = !1), i.checkOrientation && window.ArrayBuffer ? lt.test(t) ? pt.test(t) ? this.read((h = (h = t).replace(Xt, ""), a = atob(h), h = new ArrayBuffer(a.length), z(n = new Uint8Array(h), function (t, e) { n[e] = a.charCodeAt(e) }), h)) : this.clone() : (o = new XMLHttpRequest, h = this.clone.bind(this), this.reloading = !0, (this.xhr = o).onabort = h, o.onerror = h, o.ontimeout = h, o.onprogress = function () { o.getResponseHeader("content-type") !== ct && o.abort() }, o.onload = function () { r.read(o.response) }, o.onloadend = function () { r.reloading = !1, r.xhr = null }, i.checkCrossOrigin && Lt(t) && e.crossOrigin && (t = zt(t)), o.open("GET", t, !0), o.responseType = "arraybuffer", o.withCredentials = "use-credentials" === e.crossOrigin, o.send()) : this.clone()) } }, { key: "read", value: function (t) { var e = this.options, i = this.imageData, a = Rt(t), n = 0, o = 1, h = 1; 1 < a && (this.url = function (t, e) { for (var i = [], a = new Uint8Array(t); 0 < a.length;)i.push(Yt.apply(null, yt(a.subarray(0, 8192)))), a = a.subarray(8192); return "data:".concat(e, ";base64,").concat(btoa(i.join(""))) }(t, ct), n = (t = function (t) { var e = 0, i = 1, a = 1; switch (t) { case 2: i = -1; break; case 3: e = -180; break; case 4: a = -1; break; case 5: e = 90, a = -1; break; case 6: e = 90; break; case 7: e = 90, i = -1; break; case 8: e = -90 }return { rotate: e, scaleX: i, scaleY: a } }(a)).rotate, o = t.scaleX, h = t.scaleY), e.rotatable && (i.rotate = n), e.scalable && (i.scaleX = o, i.scaleY = h), this.clone() } }, { key: "clone", value: function () { var t = this.element, e = this.url, i = t.crossOrigin, a = e, n = (this.options.checkCrossOrigin && Lt(e) && (i = i || "anonymous", a = zt(e)), this.crossOrigin = i, this.crossOriginUrl = a, document.createElement("img")); i && (n.crossOrigin = i), n.src = a || e, n.alt = t.alt || "The image to crop", (this.image = n).onload = this.start.bind(this), n.onerror = this.stop.bind(this), v(n, Z), t.parentNode.insertBefore(n, t.nextSibling) } }, { key: "start", value: function () { function t(t, e) { g(a.imageData, { naturalWidth: t, naturalHeight: e, aspectRatio: t / e }), a.initialImageData = g({}, a.imageData), a.sizing = !1, a.sized = !0, a.build() } var e, i, a = this, n = this.image, o = (n.onload = null, n.onerror = null, this.sizing = !0, h.navigator && /(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(h.navigator.userAgent)); n.naturalWidth && !o ? t(n.naturalWidth, n.naturalHeight) : (e = document.createElement("img"), i = document.body || document.documentElement, (this.sizingImage = e).onload = function () { t(e.width, e.height), o || i.removeChild(e) }, e.src = n.src, o || (e.style.cssText = "left:0;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;opacity:0;position:absolute;top:0;z-index:-1;", i.appendChild(e))) } }, { key: "stop", value: function () { var t = this.image; t.onload = null, t.onerror = null, t.parentNode.removeChild(t), this.image = null } }, { key: "build", value: function () { var t, e, i, a, n, o, h, r, s; this.sized && !this.ready && (t = this.element, e = this.options, i = this.image, a = t.parentNode, (n = document.createElement("div")).innerHTML = '
', o = (n = n.querySelector(".".concat(c, "-container"))).querySelector(".".concat(c, "-canvas")), h = n.querySelector(".".concat(c, "-drag-box")), s = (r = n.querySelector(".".concat(c, "-crop-box"))).querySelector(".".concat(c, "-face")), this.container = a, this.cropper = n, this.canvas = o, this.dragBox = h, this.cropBox = r, this.viewBox = n.querySelector(".".concat(c, "-view-box")), this.face = s, o.appendChild(i), v(t, L), a.insertBefore(n, t.nextSibling), X(i, Z), this.initPreview(), this.bind(), e.initialAspectRatio = Math.max(0, e.initialAspectRatio) || NaN, e.aspectRatio = Math.max(0, e.aspectRatio) || NaN, e.viewMode = Math.max(0, Math.min(3, Math.round(e.viewMode))) || 0, v(r, L), e.guides || v(r.getElementsByClassName("".concat(c, "-dashed")), L), e.center || v(r.getElementsByClassName("".concat(c, "-center")), L), e.background && v(n, "".concat(c, "-bg")), e.highlight || v(s, G), e.cropBoxMovable && (v(s, V), w(s, d, I)), e.cropBoxResizable || (v(r.getElementsByClassName("".concat(c, "-line")), L), v(r.getElementsByClassName("".concat(c, "-point")), L)), this.render(), this.ready = !0, this.setDragMode(e.dragMode), e.autoCrop && this.crop(), this.setData(e.data), l(e.ready) && b(t, "ready", e.ready, { once: !0 }), y(t, "ready")) } }, { key: "unbuild", value: function () { var t; this.ready && (this.ready = !1, this.unbind(), this.resetPreview(), (t = this.cropper.parentNode) && t.removeChild(this.cropper), X(this.element, L)) } }, { key: "uncreate", value: function () { this.ready ? (this.unbuild(), this.ready = !1, this.cropped = !1) : this.sizing ? (this.sizingImage.onload = null, this.sizing = !1, this.sized = !1) : this.reloading ? (this.xhr.onabort = null, this.xhr.abort()) : this.image && this.stop() } }]) && j(t.prototype, e), i && j(t, i), Object.defineProperty(t, "prototype", { writable: !1 }), n }(); return g(It.prototype, t, i, e, St, jt, At), It }); \ No newline at end of file diff --git a/src/Cropper.Blazor/Server/Cropper.Blazor.Server.csproj b/src/Cropper.Blazor/Server/Cropper.Blazor.Server.csproj index bc31deaa..92084a00 100644 --- a/src/Cropper.Blazor/Server/Cropper.Blazor.Server.csproj +++ b/src/Cropper.Blazor/Server/Cropper.Blazor.Server.csproj @@ -1,18 +1,18 @@  - - net7.0 - enable - enable - + + net7.0 + enable + enable + - - - + + + - - - + + +